← Back to Blogs

Backend

Generate Laravel 11 Tailwind CRUD Operation

InnovationM Admin 23 May 2024 9 min read
Generate Laravel 11 Tailwind CRUD Operation

An updated Laravel CRUD Generator version of  ibex/crud-generator now generates CRUD in Tailwind CSS in the blade with the blank Laravel installation.

1- Install Laravel

composer create-project laravel/laravel laravel-11-crud

2- Create Migration

php artisan make:model Posts –migration

For example posts table has the following columns in migration below.

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->string('description');
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('posts');
    }
};

3- Install ibex/crud-generator

php artisan make:crud posts tailwind

A single command will generate all CRUD operations in a second. This will include a resource PostControllerPost (Model), PostRequest, and all Blade views in Tailwind CSS.

4- Add route in web.php

Route::resource(‘posts’, PostController::class);

Below is the code generated for PostController

<?php

namespace App\Http\Controllers;

use App\Models\Post;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use App\Http\Requests\PostRequest;
use Illuminate\Support\Facades\Redirect;
use Illuminate\View\View;

class PostController extends Controller
{
    public function index(Request $request): View
    {
        $posts = Post::paginate();
        return view('post.index', compact('posts'))
            ->with('i', ($request->input('page', 1) - 1) * $posts->perPage());
    }

public function create(): View
    {
        $post = new Post();
        return view('post.create', compact('post'));
    }

    public function store(PostRequest $request): RedirectResponse
    {
        Post::create($request->validated());
        return Redirect::route('posts.index')
            ->with('success', 'Post created successfully.');
    }
    public function show($id): View
    {
        $post = Post::find($id);
        return view('post.show', compact('post'));
    }
    public function edit($id): View
    {
        $post = Post::find($id);
        return view('post.edit', compact('post'));
    }
    public function update(PostRequest $request, Post $post): RedirectResponse
    {
        $post->update($request->validated());
        return Redirect::route('posts.index')
            ->with('success', 'Post updated successfully');
    }
    public function destroy($id): RedirectResponse
    {
        Post::find($id)->delete();
        return Redirect::route('posts.index')
            ->with('success', 'Post deleted successfully');
    }
}

5- Create view files.

6-Create view file- create.blade.php

<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            {{ __('Create') }} {{modelTitle}}
        </h2>
    </x-slot>
    <div class="py-12">
        <div class="max-w-full mx-auto sm:px-6 lg:px-8 space-y-6">
            <div class="p-4 sm:p-8 bg-white shadow sm:rounded-lg">
                <div class="w-full">
                    <div class="sm:flex sm:items-center">
                        <div class="sm:flex-auto">
                            <h1 class="text-base font-semibold leading-6 text-gray-900">{{ __('Create') }} {{modelTitle}}</h1>
                            <p class="mt-2 text-sm text-gray-700">Add a new {{ __('{{modelTitle}}') }}.</p>
                        </div>
                        <div class="mt-4 sm:ml-16 sm:mt-0 sm:flex-none">
                            <a type="button" href="{{ route('{{modelRoute}}.index') }}" class="block rounded-md bg-indigo-600 px-3 py-2 text-center text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">Back</a>
                        </div>
                    </div>
                    <div class="flow-root">
                        <div class="mt-8 overflow-x-auto">
                            <div class="max-w-xl py-2 align-middle">
                                <form method="POST" action="{{ route('{{modelRoute}}.store') }}"  role="form" enctype="multipart/form-data">
                                    @csrf
                                    @include('{{modelView}}.form')
                                </form>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</x-app-layout>

7- Create view file- edit.blade.php

<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            {{ __('Update') }} {{modelTitle}}
        </h2>
    </x-slot>
    <div class="py-12">
     <div class="max-w-full mx-auto sm:px-6 lg:px-8 space-y-6">
            <div class="p-4 sm:p-8 bg-white shadow sm:rounded-lg">
                <div class="w-full">
                    <div class="sm:flex sm:items-center">
                        <div class="sm:flex-auto">
                            <h1 class="text-base font-semibold leading-6 text-gray-900">{{ __('Update') }} {{modelTitle}}</h1>
                            <p class="mt-2 text-sm text-gray-700">Update existing {{ __('{{modelTitle}}') }}.</p>
                        </div>
                       <div class="mt-4 sm:ml-16 sm:mt-0 sm:flex-none">
                            <a type="button" href="{{ route('{{modelRoute}}.index') }}" class="block rounded-md bg-indigo-600 px-3 py-2 text-center text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">Back</a>
                        </div>
                    </div>
                    <div class="flow-root">
                        <div class="mt-8 overflow-x-auto">
                            <div class="max-w-xl py-2 align-middle">
                                <form method="POST" action="{{ route('{{modelRoute}}.update', ${{modelNameLowerCase}}->id) }}"  role="form" enctype="multipart/form-data">
                                    {{ method_field('PATCH') }}
                                    @csrf
                                    @include('{{modelView}}.form')
                                </form>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</x-app-layout>

 

8-Create view file-  form-field.blade.php

    <div>
        <x-input-label for="{{column_snake}}" :value="__('{{title}}')"/>
        <x-text-input id="{{column_snake}}" name="{{column}}" type="text" class="mt-1 block w-full" :value="old('{{column}}', ${{modelNameLowerCase}}?->{{column}})" autocomplete="{{column}}" placeholder="{{title}}"/>
        <x-input-error class="mt-2" :messages="$errors->get('{{column}}')"/>
    </div>

9-Create view file- form.blade.php

<div class="space-y-6">
    {{form}}
    <div class="flex items-center gap-4">
        <x-primary-button>Submit</x-primary-button>
    </div>
</div>

10-Create view file- index.blade.php

<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            {{ __('{{modelTitlePlural}}') }}
      </h2>
    </x-slot>
    <div class="py-12">
        <div class="max-w-full mx-auto sm:px-6 lg:px-8 space-y-6">
            <div class="p-4 sm:p-8 bg-white shadow sm:rounded-lg">
                <div class="w-full">
                    <div class="sm:flex sm:items-center">
                        <div class="sm:flex-auto">
                            <h1 class="text-base font-semibold leading-6 text-gray-900">{{ __('{{modelTitlePlural}}') }}</h1>
                            <p class="mt-2 text-sm text-gray-700">A list of all the {{ __('{{modelTitlePlural}}') }}.</p>
                        </div>
                        <div class="mt-4 sm:ml-16 sm:mt-0 sm:flex-none">
                           <a type="button" href="{{ route('{{modelRoute}}.create') }}" class="block rounded-md bg-indigo-600 px-3 py-2 text-center text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">Add new</a>
                        </div>
                    </div>
                  <div class="flow-root">
                        <div class="mt-8 overflow-x-auto">
                            <div class="inline-block min-w-full py-2 align-middle">
                                <table class="w-full divide-y divide-gray-300">
                                    <thead>
                                    <tr>
                                        <th scope="col" class="py-3 pl-4 pr-3 text-left text-xs font-semibold uppercase tracking-wide text-gray-500">No</th>
                                       {{tableHeader}}
                                        <th scope="col" class="px-3 py-3 text-left text-xs font-semibold uppercase tracking-wide text-gray-500"></th>
                                    </tr>

                                    </thead>

                                    <tbody class="divide-y divide-gray-200 bg-white">

                                    @foreach (${{modelNamePluralLowerCase}} as ${{modelNameLowerCase}})

                                        <tr class="even:bg-gray-50">

                                            <td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-semibold text-gray-900">{{ ++$i }}</td>

                                            {{tableBody}}

                                            <td class="whitespace-nowrap py-4 pl-4 pr-3 text-sm font-medium text-gray-900">

                                                <form action="{{ route('{{modelRoute}}.destroy', ${{modelNameLowerCase}}->id) }}" method="POST">

                                                    <a href="{{ route('{{modelRoute}}.show', ${{modelNameLowerCase}}->id) }}" class="text-gray-600 font-bold hover:text-gray-900 mr-2">{{ __('Show') }}</a>

                                                    <a href="{{ route('{{modelRoute}}.edit', ${{modelNameLowerCase}}->id) }}" class="text-indigo-600 font-bold hover:text-indigo-900  mr-2">{{ __('Edit') }}</a>

                                                    @csrf

                                                    @method('DELETE')

                                                    <a href="{{ route('{{modelRoute}}.destroy', ${{modelNameLowerCase}}->id) }}" class="text-red-600 font-bold hover:text-red-900" onclick="event.preventDefault(); confirm('Are you sure to delete?') ? this.closest('form').submit() : false;">{{ __('Delete') }}</a>

                                                </form>

                                            </td>

                                        </tr>

                                    @endforeach

                                    </tbody>

                                </table>

                                <div class="mt-4 px-4">

                                    {!! ${{modelNamePluralLowerCase}}->withQueryString()->links() !!}
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</x-app-layout>

11- Create view file-  show.blade.php

<x-app-layout>
    <x-slot name="header">
       <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            {{ ${{modelNameLowerCase}}->name ?? __('Show') . " " . __('{{modelTitle}}') }}
        </h2>
    </x-slot>
    <div class="py-12">
        <div class="max-w-full mx-auto sm:px-6 lg:px-8 space-y-6">
            <div class="p-4 sm:p-8 bg-white shadow sm:rounded-lg">
                <div class="w-full">
                    <div class="sm:flex sm:items-center">
                        <div class="sm:flex-auto">
                            <h1 class="text-base font-semibold leading-6 text-gray-900">{{ __('Show') }} {{modelTitle}}</h1>
                            <p class="mt-2 text-sm text-gray-700">Details of {{ __('{{modelTitle}}') }}.</p>
                        </div>
                        <div class="mt-4 sm:ml-16 sm:mt-0 sm:flex-none">                            <a type="button" href="{{ route('{{modelRoute}}.index') }}" class="block rounded-md bg-indigo-600 px-3 py-2 text-center text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600">Back</a>
                        </div>
                    </div>
     <div class="flow-root">
                        <div class="mt-8 overflow-x-auto">
                            <div class="inline-block min-w-full py-2 align-middle">
                                <div class="mt-6 border-t border-gray-100">
                                    <dl class="divide-y divide-gray-100">
                                        {{viewRows}}
                                    </dl>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</x-app-layout>

12-Create view file-  view-field.blade.php

<div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">

<dt class="text-sm font-medium leading-6 text-gray-900">{{title}}</dt>

<dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">{{ ${{modelNameLowerCase}}->{{column}} }}</dd>

</div>
About the Author
InnovationM Admin

Contributor at InnovationM.

LinkedIn

We don't
predict
the future.
We build what
comes next.

Share your goals, challenges, or ideas, we'll help you turn them into scalable digital solutions.