Laravel과 Livewire를 사용한 CRUD 예제

안녕하세요 독자 여러분,

이전 두 블로그에서 laravel 및 livewire 및 간단한 점수 앱을 설치하는 방법을 살펴보았습니다.

나는 당신이 livewire가 어떻게 작동하는지에 대한 기본 지식을 가지고 있다고 가정합니다. 잘 모르겠다면 이전 블로그를 참조한 다음 이 게시물을 참조하면 더 잘 이해하는 데 도움이 될 것입니다.



  • 오늘 블로그에서는 laravel n Livewire를 사용하여 CRUD 작업을 볼 것입니다.

    CRUD 예제부터 시작하겠습니다.

    1단계 - 가장 먼저 해야 할 일 - 제품 테이블에 대한 마이그레이션 생성php artisan make:migration “create products table”
    새로 만든 마이그레이션 파일을 열고 다음 필드를 추가하고 php artisan migrate 명령을 실행합니다.

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


    2단계 - web.php 파일을 열고 경로를 추가합니다.

        // 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');
    


    3단계 - 이제 아래 명령을 사용하여 livewire 구성 요소를 생성합니다. 이 명령은 이미 알고 있듯이 하나는 클래스이고 두 번째는 보기 파일입니다.
    php artisan make:livewire Product
    뷰 파일을 열고 아래 코드를 추가하십시오.

    <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>
    


    4단계 제품을 추가하는 데 사용되는 다른 파일을 만들고 이름을 create.blade.php로 지정합니다(보기 폴더 내에서 livewire 폴더를 마우스 오른쪽 버튼으로 클릭하여 파일을 만들고 아래 코드를 추가할 수 있습니다.

    <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>
    


    5단계 - 마지막 단계에서 클래스/컨트롤러 파일Product.php을 열고 아래 코드를 추가합니다.

    <?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');
        }
    
    }
    


    만세! Laravel과 Livewire를 사용하여 첫 번째 CRUD 앱을 만들었습니다.

    행복한 코딩..
    읽어주셔서 감사합니다 🦄 ❤️ 🦄 ❤️

    좋은 웹페이지 즐겨찾기