Using Form Objects and Modal Wire Elements in Livewire 3 for CRUD Operations (2024)

With Livewire v3’s introduction, Form Objects now offer a streamlined way to separate field logic from Components.

Using Form Objects and Modal Wire Elements in Livewire 3 for CRUD Operations (2)

Setting Up the Laravel Project

Our ‘Product’ model will encompass two database fields: ‘name’ and ‘description’. Here’s a glimpse of the migration for creating the products table:

// database/migrations/xxx_create_products_table.php:

public function up(): void{
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->text('description');
$table->timestamps();
});
}

And, here’s the essence of our Product model:

app/Models/Product.php:
class Product extends Model
{
protected $fillable = [
'name',
'description',
];
}

Setting up Livewire and Tailoring Breeze

Before diving in, the first step on our to-do list is installing Livewire, which you can accomplish with the following Composer command:

composer require livewire/livewire

Open your resources/js/app.js and remove or comment out the Alpine.js lines:

import './bootstrap';
import Alpine from 'alpinejs';
window.Alpine = Alpine;
Alpine.start();

Once these changes are made, recompile your assets:

npm run prod

By default, Livewire searches for a layout at
resources/views/components/layouts/app.blade.php. We'll need to point Livewire to the right location through its configuration settings.

First, publish the Livewire configuration file:

php artisan livewire:publish --config

Then, within config/livewire.php, modify the layout paths:

return [
// ...
'layout' => 'components.layouts.app',
'layout' => 'layouts.app',
// ...
];

There you have it! Livewire is now integrated into our project. Next, let’s craft a Livewire component to display our list of products.

Crafting the Livewire Component for Product Listing

Start by crafting the component:

php artisan make:livewire ProductList
Navigate to app/Livewire/ProductList.php and set it up as follows:
use App\Models\Product;
use Illuminate\Contracts\View\View;
class ProductList extends Component
{
public function render(): View
{
return view('livewire.product-list', [
'products' => Product::all(),
]);
}
}

To make our products accessible from the main navigation:

Open resources/views/layouts/navigation.blade.php:

// ... 
<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
<x-nav-link :href="route('users.index')" :active="request()->routeIs('users.index')">
{{ __('Users') }}
</x-nav-link>
<x-nav-link :href="route('products')" :active="request()->routeIs('products')">
{{ __('Products') }}
</x-nav-link>
</div>
// ...

Finally, design the product listing:

resources/views/livewire/product-list.blade.php:
<div>
<x-slot name="header">
<h2 class="text-xl font-semibold leading-tight text-gray-800">
{{ __('Products') }}
</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 overflow-hidden overflow-x-auto bg-white border-b border-gray-200">
<div class="min-w-full align-middle">
<table class="min-w-full border divide-y divide-gray-200">
<!-- Table Header -->
<thead>
<tr>
<th class="px-6 py-3 text-left bg-gray-50">
<span class="text-xs font-medium leading-4 tracking-wider text-gray-500 uppercase">Name</span>
</th>
<th class="px-6 py-3 text-left bg-gray-50">
<span class="text-xs font-medium leading-4 tracking-wider text-gray-500 uppercase">Description</span>
</th>
<th class="px-6 py-3 text-left bg-gray-50"></th>
</tr>
</thead>
<!-- Table Body -->
<tbody class="bg-white divide-y divide-gray-200 divide-solid">
@forelse($products as $product)
<tr>
<td class="px-6 py-4 text-sm leading-5 text-gray-900">
{{ $product->name }}
</td>
<td class="px-6 py-4 text-sm leading-5 text-gray-900">
{{ $product->description }}
</td>
<td class="px-6 py-4 text-sm leading-5 text-gray-900">
{{-- Placeholder for Edit action --}}
</td>
</tr>
@empty
<tr>
<td colspan="3" class="px-6 py-4 text-sm leading-5 text-gray-900">
No products available.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>

Now, your Livewire component will showcase a sleek product listing.

Crafting a Modal using Form Object

First, generate a Livewire component and a Form Object that will handle our modal and data validations.

php artisan make:livewire ProductModal php artisan livewire:form ProductForm

To smoothly implement modals, let’s integrate the wire-elements/modal package:

composer require wire-elements/modal:^2.0

For the Livewire component to function as a modal, instead of the typical Livewire\Component we have to extend it to LivewireUI\Modal\ModalComponentModal

Update your app/Livewire/ProductModal.php:

use Livewire\Component;
use Illuminate\Contracts\View\View;
use LivewireUI\Modal\ModalComponent;
class ProductModal extends ModalComponent
{
public function render(): View
{
return view('livewire.product-form');
}
}

Craft a corresponding form within the Blade file,

resources/views/livewire/product-form.blade.php:
<div class="p-6">
<form wire:submit="save">
<div>
<x-input-label for="name" :value="__('Name')" />
<x-text-input id="name" class="mt-1 block w-full" type="text" />
</div>
<div class="mt-4">
<x-input-label for="description" :value="__('Description')" />
<textarea id="description" class="mt-1 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500"></textarea>
</div>
<div class="mt-4">
<x-primary-button>
Save
</x-primary-button>
</div>
</form>
</div>

Lastly, place buttons to invoke the modal in resources/views/livewire/product-list.blade.php:

// ...
<x-primary-button wire:click="$dispatch('openModal', { component: 'product-modal' })" class="mb-4">
New Product
</x-primary-button>
// ...
@forelse($products as $product)
<tr class="bg-white">
<td class="px-6 py-4 text-sm">
{{ $product->name }}
</td>
<td class="px-6 py-4 text-sm">
{{ $product->description }}
</td>
<td class="px-6 py-4 text-sm">
<x-secondary-button wire:click="$dispatch('openModal', { component: 'product-modal', arguments: { product: {{ $product }} }})">
Edit
</x-secondary-button>
</td>
</tr>
@empty
// ...

After clicking the New Product button, the form-containing modal appears.

Product Creation: A Modal Approach

Begin by defining the required properties within our Form Object, and subsequently binding them to input fields.

app/Livewire/Forms/ProductForm.php:

class ProductForm extends Form
{
public string $name = '';
public string $description = '';
}
In app/Livewire/ProductModal.php, inject the form:
class ProductModal extends ModalComponent
{
public Forms\ProductForm $form;
// ...
}
Update your resources/views/livewire/product-modal.blade.php:
<div class="p-6">
<form wire:submit="save">
<!-- Name input -->
<div>
<x-input-label for="name" :value="__('Name')" />
<x-text-input wire:model="form.name" id="name" class="mt-1 block w-full" type="text" />
<x-input-error :messages="$errors->get('form.name')" class="mt-2" />
</div>
<!-- Description input -->
<div class="mt-4">
<x-input-label for="description" :value="__('Description')" />
<textarea wire:model="form.description" id="description" class="mt-1 w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500"></textarea>
<x-input-error :messages="$errors->get('form.description')" class="mt-2" />
</div>
<!-- Save button -->
<div class="mt-4">
<x-primary-button>
Save
</x-primary-button>
</div>
</form>
</div>

For the saving part, the wire:submit directive links to the save method, which we'll define in the Form Object. It'll house all the logic for creating and updating the product.

In app/Livewire/ProductForm.php:

class ProductForm extends Form
{
public function save(): void
{
$this->validate();
Product::create($this->only(['name', 'description']));
$this->reset();
}
public function rules(): array
{
return [
'name' => ['required'],
'description' => ['required']
];
}
}
Upon saving a product, the modal should close and the table should refresh to display the new entries.
In app/Livewire/ProductModal.php:
class ProductModal extends ModalComponent
{
public Forms\ProductForm $form;
public function save(): void
{
$this->form->save();
$this->closeModal();
$this->dispatch('refresh-list');
}
// ...
}

To ensure the table gets automatically updated after a product is saved, add a refresh method in app/Livewire/ProductList.php:

use Livewire\Attributes\On;
class ProductList extends Component
{
#[On('refresh-list')]
public function refresh() {}
// ...
}

After successfully saving a product, the modal will close and the table will update to reflect the new information.

Product Editing: Setting Up the Form and Data Persistence

To refine our product editing capability, begin by setting up properties when a product is provided.

In app/Livewire/ProductModal.php, incorporate the product model:

use App\Models\Product;
class ProductModal extends ModalComponent
{
public ?Product $product = null;
public Forms\ProductForm $form;
public function mount(Product $product = null): void
{
if ($product && $product->exists) {
$this->form->setProduct($product);
}
}
// ...
}

Next, navigate to app/Livewire/Forms/ProductForm.php and include these adjustments:

use App\Models\Product;
class ProductForm extends Form
{
public ?Product $product = null;
public string $name = '';
public string $description = '';
public function setProduct(?Product $product = null): void
{
$this->product = $product;
$this->name = $product->name;
$this->description = $product->description;
}
// ...
}

To ensure our database is updated properly, we’ll include a check within our saving logic. Depending on the result, we’ll either add a new product or update an existing one.

Update app/Livewire/Forms/ProductForm.php:

class ProductForm extends Form
{
// Existing properties and methods...
public function save(): void
{
$this->validate();
if (!$this->product) {
Product::create($this->only(['name', 'description']));
} else {
$this->product->update($this->only(['name', 'description']));
}
$this->reset();
}
// Other methods...
}

Now, when you edit a product, your system will check if the product exists. If it does, the product’s details will be updated, otherwise, a new product will be created.

Ensuring the Uniqueness of Product Names

To maintain the distinctness of each product, we’ll institute a unique rule for product names.

Within app/Livewire/Forms/ProductForm.php:

use Illuminate\Validation\Rule;
class ProductForm extends Form
{
// Existing properties...
public function rules(): array
{
return [
'name' => [
'required',
Rule::unique('products', 'name')->ignore($this->component->product),
],
'description' => ['required'],
];
}
}

An essential aspect of this implementation is to ensure we exclude the current product (if editing) from the uniqueness check. With Livewire, properties from the main component can be fetched using $this->component.

This addition ensures that while creating or updating, each product name remains distinct in the database.

Additional Guidance on Validation: Handling the “form.” Prefix

Upon implementing Form Objects, you might notice that the attribute names referenced in validation messages come prefixed with “form.” This could potentially lead to unclear or inaccurate validation feedback.

To bring clarity to this, we can simply add a validationAttributes method to our ProductForm.

In app/Livewire/Forms/ProductForm.php:

class ProductForm extends Form
{
// Previous properties and methods...
public function validationAttributes(): array
{
return [
'name' => 'name',
'description' => 'description',
];
}
}

With this modest yet impactful change, the attribute names are presented as expected, enhancing the user experience.

Unlock the secrets to mastering Laravel and supercharge your development skills! This guide, crafted by seasoned experts, is your golden ticket to bypass common roadblocks and accelerate your career growth. With this treasure trove of Laravel wisdom, you’re not just buying an eBook; you’re investing in a leap forward. Two years of professional advancement await at your fingertips. Make the smart move — transform your Laravel expertise with just one click. Get Your Copy Now!

Using Form Objects and Modal Wire Elements in Livewire 3 for CRUD Operations (2024)

FAQs

How do I create a crud in Livewire? ›

  1. Step 1: Create Laravel Application. ...
  2. Step 2: Configure Database Details. ...
  3. Step 3: Install Livewire Package: ...
  4. Step 4: Create Model and Migration: ...
  5. Step 5: Create Livewire Component and View. ...
  6. Step 6: Define Routes. ...
  7. Step 7: Run Project.
Jan 22, 2022

How to create a modal in Livewire? ›

Build a Modal in Laravel Livewire
  1. Prerequisites.
  2. Step 1: Create a Livewire Component.
  3. Step 2: Design the Modal Markup.
  4. Step 3: Add Logic to Your Component.
  5. Step 4: Using the Modal Component.
  6. Step 5: Listening for Events.
  7. Step 6: Enhancing the Experience with Tailwind CSS and Alpine.js.
  8. Security Considerations.
Apr 19, 2024

What is a livewire component? ›

A Livewire component is simply a PHP class that extends Livewire\Component . You can create component files by hand or use the following Artisan command: php artisan make:livewire CreatePost. If you prefer kebab-cased names, you can use them as well: php artisan make:livewire create-post.

What is liveware in Laravel? ›

Livewire is a full-stack framework for Laravel that makes building dynamic interfaces simple, without leaving the comfort of Laravel. Consider my interest piqued. It's not like anything you've seen before. The best way to understand it is to just look at the code.

How do you create a CRUD operation? ›

In CRUD operations, 'C' is an acronym for create, which means to add or insert data into the SQL table. So, firstly we will create a table using CREATE command and then we will use the INSERT INTO command to insert rows in the created table. Table_Name is the name that we want to assign to the table.

What is the CRUD create function? ›

Create, Read, Update, Delete. When we are building APIs, we want our models to provide four basic types of functionality. The model must be able to Create, Read, Update, and Delete resources. Computer scientists often refer to these functions by the acronym CRUD.

What is wire model Livewire? ›

Livewire makes it easy to bind a component property's value with form inputs using wire:model . Here is a simple example of using wire:model to bind the $title and $content properties with form inputs in a "Create Post" component: use Livewire\Component; use App\Models\Post; class CreatePost extends Component.

How do I set up modal? ›

Getting started
  1. Create an account at modal.com.
  2. Run pip install modal to install the modal Python package.
  3. Run modal setup to authenticate (if this doesn't work, try python -m modal setup )

How do you make a modal component? ›

By adding required css and open-close funtions we will create React Modal Component. Example: Define Modal Component and use with given Open state and onClose attribute. Define useState variable and open close funtion to show and hide the modal.

Is Livewire frontend or backend? ›

Livewire is one of the most important projects in the Laravel ecosystem specifically targeted to frontend development. The peculiarity of Livewire is that it allows the development of a “modern” web application without the need to use dedicated JavaScript frameworks.

How do I pass data to a component in Livewire? ›

You can pass data into a component by passing additional parameters into the @livewire directive. For example, let's say we have a ShowContact Livewire component that needs to know which contact to show. Here's how you would pass in a contact model. If you are on Laravel 7 or greater, you can use the tag syntax.

What is wire key in Livewire? ›

Template Directives
DirectiveDescription
wire:key="foo"Acts as a reference point for Livewire's DOM diffing system. Useful for adding/removing elements, and keeping track of lists.
wire:click="foo"Listens for a "click" event, and fires the "foo" method in the component.
20 more rows

Is Livewire asynchronous? ›

After the initial rendering of the page containing the Livewire component, Livewire binds some javascript event listeners to its components and watches for every action. Each action is sent to the server as an asynchronous API request, which includes the component's state, payload, or snapshot.

What is the architecture of Laravel Livewire? ›

Component-Based Architecture

Understanding Laravel Livewire, the cornerstone is its robust component-based architecture, offering a structured approach to web development. This architectural paradigm enables developers to create encapsulated, reusable components, fostering a modular and organized codebase.

Is Livewire better than React? ›

Laravel Livewire stands out from other Laravel full-stack frameworks due to its simplicity and ease of use. Unlike traditional JavaScript-heavy frameworks like Vue. js or React, Livewire enables developers to leverage their existing PHP skills to create dynamic interfaces.

How to make a CRUD generator? ›

Introduction
  1. Generate a module ( nest g mo ) to keep code organized and establish clear boundaries (grouping related components)
  2. Generate a controller ( nest g co ) to define CRUD routes (or queries/mutations for GraphQL applications)
  3. Generate a service ( nest g s ) to implement & isolate business logic.

How do you generate a CRUD in SQL? ›

To create a CRUD procedure, in Object Explorer, right-click on the database table and select SQL Complete > Script Table as CRUD. This will generate a code in a new SQL document. For example, SQL Complete will generate the following CRUD procedures for the Production. ProductSubcategory table.

How do I create a CRUD REST API? ›

How to Build a CRUD REST API?
  1. Step 1: Initialize a Node. js project. ...
  2. Step 2: Install the dependencies. ...
  3. Step 3: Create an Express. ...
  4. Step 4: Setup your CRUD operations. ...
  5. Step 5: Run your app. ...
  6. API Documentation: ...
  7. Testing and Debugging: ...
  8. API Exploration:
Jun 14, 2024

Top Articles
Latest Posts
Article information

Author: The Hon. Margery Christiansen

Last Updated:

Views: 6325

Rating: 5 / 5 (70 voted)

Reviews: 85% of readers found this page helpful

Author information

Name: The Hon. Margery Christiansen

Birthday: 2000-07-07

Address: 5050 Breitenberg Knoll, New Robert, MI 45409

Phone: +2556892639372

Job: Investor Mining Engineer

Hobby: Sketching, Cosplaying, Glassblowing, Genealogy, Crocheting, Archery, Skateboarding

Introduction: My name is The Hon. Margery Christiansen, I am a bright, adorable, precious, inexpensive, gorgeous, comfortable, happy person who loves writing and wants to share my knowledge and understanding with you.