Basic Laravel 9 Course is available now! 🎉 Get your seat now

Create Laravel CRUD Application

#laravel #tailwind css #tutorials

A completed guide for building CRUD application using Laravel, Tailwind CSS

Livewire CRUD for Beginner

This new article would like to share how I built a CRUD application. After you practice with this article, you will be able to create an application which can perform operation CRUD (Create, Read, Update, and Delete) using Laravel.

What we cover in this articles

We use Laravel, Livewire, Tailwind CSS, Laravel Breeze(Starter Kits for Authentication) and below is our content which we will cover is this article.

  • Setup Laravel project
  • Configure database
  • Install Laravel Breeze
  • Install Livewire
  • Implement CRUD operations
  • Running the application

Setup Laravel project

To install a fresh Laravel 9 application

composer create-project laravel/laravel laravel-crud

where laravel-crud is your project name.

Configure database connection

Now you have to configure the database variable in .env to connect your laravel application with the database server.

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306    
DB_DATABASE=database_name // your database name
DB_USERNAME=database_user_name  // your database user
DB_PASSWORD=database_password   // your datbase password

Install Laravel Breeze && Livewire

To make things more manageable, we use Laravel Breeze, a starter kit for authentication with Tailwind CSS 3. So we don't need to install and configure Tailwind.

  • To Install Laravel Breeze via composer composer require laravel/breeze
  • To Setup breeze ui and auth scaffolding php artisan breeze:install
  • To Install dependencies npm install && npm run dev
  • To migrate php artisan mgirate
  • Install Livewire composer require livewire/livewire

To activate Livewire on a page, we need to add @livewireStyles and @livewireScripts every page you want to use it.

Now let add it to our resources/views/layouts/app.blade.php

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="csrf-token" content="{{ csrf_token() }}">
        <title>{{ config('app.name', 'Livewire CRUD') }}</title>
        <!-- Fonts -->
        <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap">
        <!-- Styles -->
        <link rel="stylesheet" href="{{ asset('css/app.css') }}">

        <!-- Scripts -->
        <script src="{{ asset('js/app.js') }}" defer></script>
          <!-- Add Livewire Style -->
        @livewireStyles   
    </head>
    <body class="font-sans antialiased">
        <div class="min-h-screen bg-gray-100">
            @include('layouts.navigation')

            <!-- Page Heading -->
            <header class="bg-white shadow">
                <div class="px-4 py-6 mx-auto max-w-7xl sm:px-6 lg:px-8">
                    {{ $header }}
                </div>
            </header>

            <!-- Page Content -->
            <main>
                {{ $slot }}
            </main>
        </div>
        <!-- Add Livewire Script -->
        @livewireScripts
    </body>
</html>

Implement CRUD Operations

  • Create Post Model and Migrations
    php artisan make:model Post -m 

After running the command above, you will get a migration. We add field title, description.

<?php

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

class CreatePostsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->string('title');
            $table->text('description');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('posts');
    }
}

You also generate a Model file in App/Models/Post.php as below.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    use HasFactory;

    protected $fillable = [
        'title',
        'description'
    ];
}

Now you can hit a command php artisan migrate to migrate your database schema

  • Now let's create controller php artisan make:controller PostController and update our controller with code below.
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class PostController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        return view('post');
    }
}

Create route for Post by edit routes/web.php and add code below.

Route::get('post', [PostController::class,'index'])->name('post.index');
  • Creating a Livewire Component To create a livewire component, run the following command:
    php artisan make:livewire Post

This will create a new Post.php class file and place it in the App\Views\Components folder. Then, a corresponding view template is created and placed in resources/views/components. To display the component, you can then use this Blade syntax: <x-post/>.

You can further explore Laravel Blade components in the documentation.

Now Let's create template file for post in resources/views/post.blade.php and add content as below.

<x-app-layout>
    <x-slot name="header">
        <h2 class="text-xl font-semibold leading-tight text-gray-800">
            Post
        </h2>
    </x-slot>

    <div class="py-12">
        <div class="mx-auto max-w-7xl sm:px-6 lg:px-8">
            <div class="overflow-hidden bg-white shadow-sm sm:rounded-lg">
                <div class="p-6 bg-white border-b border-gray-200">
                    <livewire:post />
                </div>
            </div>
        </div>
    </div>
</x-app-layout>

Next, You need to update the Post component in app/Http/Livewire/Post.php with code below.

<?php

namespace App\Http\Livewire;

use App\Models\Post;
use Livewire\Component;
use Livewire\WithPagination;

class PostForm extends Component
{
    use WithPagination;

    public $title;
    public $description;
    public $post_id;

    protected $rules = [
        'title' => 'required',
        'description' => 'required',
    ];

    public function storePost()
    {
        $this->validate();
        $post = Post::create([
            'title' => $this->title,
            'description' => $this->description
        ]);
        $this->reset();
    }

    public function edit($id)
    {
        $post = Post::find($id);
        $this->post_id = $post->id;
        $this->title = $post->title;
        $this->description = $post->description;
    }

    public function update()
    {
        $post = Post::updateOrCreate(
            [
                'id'   => $this->post_id,
            ],
            [
                'title' => $this->title,
                'description' => $this->description
            ],

        );

        $this->reset();
    }

    public function destroy($id)
    {
        Post::destroy($id);
    }


    public function render()
    {
        return view('livewire.post', ['posts' => Post::latest()->paginate(10)]);
    }
}

Now you need to update livewire view component in resources/views/livewire/post.blade.php

<div>
  <h4 class="mb-4 text-2xl font-bold">Post </h4>
    <div>
        <div class="container mx-auto">
            <form method="POST" wire:submit.prevent="storePost">
                @csrf
                <div>
                    <label for="title">Title</label>
                    <input type="text" wire:model.lazy="title" class="w-full py-2 rounded">
                    @error('title')
                    <span class="text-red-600">{{ $message }}</span>
                    @enderror
                </div>
                <div class="mt-8">
                    <label class="block mb-2 text-xl">Description </label>
                    <textarea wire:model.lazy="description" rows="3" cols="20" class="w-full rounded">
                </textarea>
                    @error('description')
                    <span class="text-red-600">{{ $message }}</span>
                    @enderror
                </div>
                <button type="submit" class="px-4 py-2 mt-4 text-white bg-blue-600 rounded">
                    Submit
                </button>
                <button type="submit" wire:click="update" class="px-4 py-2 text-white bg-indigo-600 rounded">
                    Update
                </button>
            </form>
        </div>
        <div class="flex flex-col mt-8">
            <div class="py-2">
                <div
                    class="min-w-full border-b border-gray-200 shadow">
                    <table class="min-w-full">
                        <thead>
                            <tr>
                                <th class="px-6 py-3 text-left text-gray-500 border-b border-gray-200 bg-gray-50">
                                    Id
                                </th>
                                <th class="px-6 py-3 text-left text-gray-500 border-b border-gray-200 bg-gray-50">
                                    Title
                                </th>
                                <th class="px-6 py-3 text-left text-gray-500 border-b border-gray-200 bg-gray-50">
                                    Edit
                                </th>
                                <th class="px-6 py-3 text-left text-gray-500 border-b border-gray-200 bg-gray-50">
                                    Delete
                                </th>
                            </tr>
                        </thead>

                        <tbody class="bg-white">
                            @foreach($posts as $post)
                            <tr>
                                <td class="px-6 py-4 border-b border-gray-200">
                                    <div class="flex items-center">
                                        <div class="ml-4">
                                            <div class="text-sm text-gray-900">
                                                {{ $post->id }}
                                            </div>
                                        </div>
                                    </div>
                                </td>

                                <td class="px-6 py-4 border-b border-gray-200">
                                    <div class="text-sm text-gray-900">
                                        {{ $post->title }}
                                    </div>
                                </td>

                                <td class="px-6 py-4 border-b border-gray-200">
                                    <button wire:click="edit({{ $post->id }})" class="px-4 py-2 text-white bg-blue-600">
                                        Edit
                                    </button>
                                </td>

                                <td class="px-6 py-4 text-sm text-gray-500 border-b border-gray-200">
                                    <button wire:click="destroy({{ $post->id }})"
                                        class="px-4 py-2 text-white bg-red-600">
                                        Delete
                                    </button>
                                </td>

                            </tr>
                            @endforeach
                        </tbody>
                    </table>
                </div>
                {{ $posts->links() }}
            </div>
        </div>
    </div>
</div>
  • Running the application Now let's serve the application
    php artisan serve

Then you can visit 127.0.0.1:8000

  • Everything should work as normal, please share this posts If it's helpful to you.
  • If you have issued with this post, please reach me on twitter @iamputhea