DEV Community

Snehal Kadwe
Snehal Kadwe

Posted on • Updated on

CRUD Example using Laravel and Livewire

Hello Readers,

In previous two blogs we have seen how to install laravel and livewire and simple score App.

I assume, you have the basic knowledge of how does livewire works. If you not know please refer my previous blogs and then see this post which will help you to understand it better.

In today’s blog we will see CRUD operation using laravel n Livewire.

So let’s begin with our CRUD example.

Step 1 - First thing first - create a migration for product table
php artisan make:migration “create products table”

Open your newly create migration file and add following fields to it and run php artisan migrate command

 Schema::create('products', function (Blueprint $table) {
            $table->id();
            $table->string('pname');
            $table->bigInteger('price');
            $table->integer('quantity');
            $table->timestamps();
        });
Enter fullscreen mode Exit fullscreen mode

Step 2 - open your web.php file and add routes

    // routes for product
    Route::get('/products', function () {
        return view('product');
    })->name('products');

    Route::get('/products/restore/{id}', [Products::class, 'restore'])->name('products.restore');
    Route::get('/products/permenatly/delete/{id}', [Products::class, 'delete'])->name('products.delete');
Enter fullscreen mode Exit fullscreen mode

Step 3 - Now we will create livewire component using below command which will create two files one is Class and second one is view file, as you already know.

php artisan make:livewire Product

Open your view file and add below code to it.

<div class="max-w-5xl mx-auto my-10">
    {{-- Success is as dangerous as failure. --}}
    <div class="w-full">
        @if (session()->has('message'))
        <div class="w-1/3 p-2 text-center mx-auto my-4 rounded border border-green-400 bg-green-600 text-green-300">
            {{ session('message') }}
        </div>
        @endif
    </div>
    <div class="mb-10">
        @if ($openUpdateMode)
        @include('livewire.products.create-product')
        @else
        @include('livewire.products.create-product')
        @endif
    </div>
    <!-- dropdown -->
    <div class="relative inline-block text-left mb-10">
        <div>
            <button type="button" wire:click="openDropDown"
                class="inline-flex justify-center w-full rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-indigo-500"
                id="menu-button" aria-expanded="true" aria-haspopup="true">
                Options
                <!-- Heroicon name: solid/chevron-down -->
                <svg class="-mr-1 ml-2 h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"
                    fill="currentColor" aria-hidden="true">
                    <path fill-rule="evenodd"
                        d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z"
                        clip-rule="evenodd" />
                </svg>
            </button>
        </div>
        @if ($open)
        <div class="origin-top-right absolute right-0 mt-2 w-56 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none"
            role="menu" aria-orientation="vertical" aria-labelledby="menu-button" tabindex="-1">
            <div class="py-1" role="none">
                <!-- Active: "bg-gray-100 text-gray-900", Not Active: "text-gray-700" -->
                <button wire:click="archivedProduct(true)" class="text-gray-700 block px-4 py-2 text-sm" role="menuitem" tabindex="-1"
                    id="menu-item-0">Archived</button>
            </div>
            <div class="py-1" role="none">
                <!-- Active: "bg-gray-100 text-gray-900", Not Active: "text-gray-700" -->
                <button wire:click="archivedProduct(false)" class="text-gray-700 block px-4 py-2 text-sm" role="menuitem" tabindex="-1"
                    id="menu-item-0">All</button>
            </div>
        </div>
        @endif
    </div>
    <div class="flex flex-col">
        <div class="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
            <div class="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
                <div class="shadow overflow-hidden border-b border-gray-200 sm:rounded-lg">
                    <table class="min-w-full divide-y divide-gray-200">
                        <thead class="bg-gray-50">
                            <tr>
                                <th scope="col"
                                    class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                                    #
                                </th>
                                <th scope="col"
                                    class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                                    Product Name
                                </th>
                                <th scope="col"
                                    class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                                    Price
                                </th>
                                <th scope="col"
                                    class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                                    Quantity
                                </th>
                                <th scope="col" class="relative px-6 py-3">
                                    <span class="sr-only">Edit</span>
                                </th>
                            </tr>
                        </thead>
                        <tbody class="bg-white divide-y divide-gray-200">
                            @foreach ($products as $product)
                            <tr>
                                <td class="px-6 py-4 whitespace-nowrap">
                                    <div class="flex items-center">
                                        <div class="ml-4">
                                            <div class="text-sm font-medium text-gray-900">
                                                {{ $product->id }}
                                            </div>
                                        </div>
                                    </div>
                                </td>
                                <td class="px-6 py-4 whitespace-nowrap">
                                    <div class="text-sm text-gray-900">{{ $product->pname }}</div>
                                </td>
                                <td class="px-6 py-4 whitespace-nowrap">
                                    <span
                                        class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">
                                        {{ $product->price }}
                                    </span>
                                </td>
                                <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
                                    {{ $product->quantity }}
                                </td>
                                <td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
                                    @if ($archivedProduct)
                                    <span
                                        class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">
                                        <a href="{{ route('products.restore', $product->id) }}"
                                        class="text-green-600 hover:text-green-900">Restore</a>
                                    </span>
                                    <a href="{{ route('products.delete', $product->id) }}"
                                        class="text-red-600 hover:text-red-900">Delete</a>
                                    @else
                                        <button wire:click="edit({{ $product->id }})"
                                            class="text-indigo-600 hover:text-indigo-900">
                                            <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
                                                <path d="M17.414 2.586a2 2 0 00-2.828 0L7 10.172V13h2.828l7.586-7.586a2 2 0 000-2.828z" />
                                                <path fill-rule="evenodd" d="M2 6a2 2 0 012-2h4a1 1 0 010 2H4v10h10v-4a1 1 0 112 0v4a2 2 0 01-2 2H4a2 2 0 01-2-2V6z" clip-rule="evenodd" />
                                            </svg>
                                        </button>
                                        <button wire:click="archive({{ $product->id }})"
                                            class="text-red-600 hover:text-red-900">
                                            <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                                                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4" />
                                              </svg>
                                        </button>
                                    @endif
                                </td>
                            </tr>
                            @endforeach
                        </tbody>
                    </table>
                </div>
            </div>
        </div>
    </div>
</div>
Enter fullscreen mode Exit fullscreen mode

Step 4 create another file which is used to add products and name it as create.blade.php (you can create a file by right clicking livewire folder within a view folder, add below code.

<div class="mt-10 sm:mt-0">
    <div class="mt-5 md:mt-0 md:col-span-2">
        <form>
            <div class="shadow overflow-hidden sm:rounded-md">
                <div class="px-4 py-5 bg-white sm:p-6">
                    <div class="grid grid-cols-6 gap-6">
                        <div class="col-span-6 sm:col-span-3">
                            <label for="product_name" class="block text-sm font-medium text-gray-700">Product
                                name</label>
                            <input type="text" wire:model="pname" name="product_name" id="product_name" autocomplete="given-name"
                                class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md">
                        </div>

                        <div class="col-span-6 sm:col-span-3">
                            <label for="price" class="block text-sm font-medium text-gray-700">Price</label>
                            <input type="text" wire:model="price" name="price" id="price" autocomplete="family-name"
                                class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md">
                        </div>

                        <div class="col-span-6 sm:col-span-3">
                            <label for="quantity" class="block text-sm font-medium text-gray-700">Quantity</label>
                            <input type="number" wire:model="quantity" name="quantity" id="quantity" autocomplete="postal-code"
                                class="mt-1 focus:ring-indigo-500 focus:border-indigo-500 block w-full shadow-sm sm:text-sm border-gray-300 rounded-md">
                        </div>
                    </div>
                </div>
                <div class="px-4 py-3 bg-gray-50 text-right sm:px-6">
                    @if ($openUpdateMode)
                        <button type="submit" wire:click.prevent="update({{ $product->id }})"
                            class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
                            Update
                        </button>
                    @else
                        <button type="submit" wire:click.prevent="store()"
                            class="inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
                            Save
                        </button>
                    @endif
                </div>
            </div>
        </form>
    </div>
</div>
Enter fullscreen mode Exit fullscreen mode

Step 5 - Last step open your Class/Controller file Product.php and add below code.

<?php

namespace App\Http\Livewire\Products;

use Livewire\Component;
use App\Models\Product;
use Illuminate\Support\Str;

class Products extends Component
{
    public $pname, $price, $quantity, $products, $product;
    public $openUpdateMode = false, $open = false, $archivedProduct = false;

    public function mount(Product $product)
    {
        $this->pname = $product->pname ? $product->pname : "";
        $this->price = $product->price ? $product->price : "";
        $this->quantity = $product->quantity ?  $product->quantity : "";
        $this->product = $product;
    }

    // create product
    public function store()
    {
        $data = $this->validate([
            'pname' => 'required',
            'price' => 'required',
            'quantity' => 'required',
        ]);

        Product::create($data);
        session()->flash('message', 'Product Created Successfully.');

    }

    // edit product
    public function edit(Product $product)
    {
        $this->openUpdateMode = true;
        $this->mount($product);
    }

    // update product
    public function update(Product $product)
    {
        $data = $this->validate([
            'pname' => 'required',
            'price' => 'required',
            'quantity' => 'required',
        ]);

        $product->update($data);
        session()->flash('message', "Product updated");
    }

    // soft delete product
    public function archive(Product $product)
    {
        $product->delete();
    }

    public function delete($id)
    {
        Product::withTrashed()->find($id)->forceDelete();
        return redirect()->route('products')->with('message', 'Product permanetly deleted');
    }

    // open dropdown
    public function openDropDown()
    {
        $this->open = !$this->open;
    }

    // list of archived products
    public function archivedProduct($val)
    {
        $this->archivedProduct = $val;
    }

    // restore product
    public function restore($id)
    {
        $product = Product::withTrashed()->find($id);
        $product->restore();
        return redirect()->route('products')->with('message', 'Product Restored');
    }

    public function render()
    {
        if ($this->archivedProduct)
        {
            $this->products = Product::onlyTrashed()->get();
        } else {
            $this->products = Product::all();
        }
        return view('livewire.products.products');
    }

}
Enter fullscreen mode Exit fullscreen mode

Horray! We have created our first CRUD app using Laravel and Livewire.

Happy Coding..
Thank you for reading 🦄 ❤️ 🦄 ❤️

Discussion (0)