Zaawansowana praca z danymi (CRUD w Livewire)

Livewire to jedno z najpotężniejszych narzędzi w ekosystemie Laravel, umożliwiające tworzenie dynamicznych aplikacji webowych przy minimalnej ilości JavaScriptu. Jednym z najczęściej wykorzystywanych przypadków w Livewire jest obsługa formularzy oraz walidacja danych. Dzięki Livewire możemy łatwo tworzyć formularze, dynamicznie je walidować i od razu informować użytkowników o błędach, co znacząco poprawia doświadczenie użytkownika.

W tym artykule przyjrzymy się, jak wykorzystać Livewire do tworzenia formularzy, walidacji danych oraz wyświetlania komunikatów błędów. Omówimy zarówno walidację w locie, jak i walidację przy wysyłce formularza.

Artykuł jest kontynuacją poprzednich tematów:

Integracja modelu Eloquent w komponencie Livewire

W Livewire możemy łatwo zintegrować modele Eloquent, co pozwala na wygodną interakcję z bazą danych. Wystarczy użyć standardowych funkcji Eloquent, takich jak all(), find(), create(), update() czy delete(), bez konieczności pisania dużej ilości kodu JavaScript.

Poniżej przedstawiamy przykład integracji modelu w komponencie Livewire, który będzie obsługiwał proste operacje CRUD na modelu Task.

Operacje CRUD w Livewire

Załóżmy, że mamy model Task, który zawiera zadania z nazwą oraz statusem. W tym przykładzie pokażemy, jak zaimplementować pełen scenariusz CRUD.

1. Tworzenie komponentu

Na początek, tworzymy komponent Livewire, który będzie odpowiedzialny za operacje CRUD:

				
					php artisan make:livewire TaskList

				
			

2. Wyświetlanie listy zadań

W komponencie TaskList możemy wyświetlić wszystkie zadania z bazy danych:

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

class TaskList extends Component
{
    use WithPagination;

    public $name, $status, $taskId;
    public $isEditing = false;

    protected $rules = [
        'name' => 'required|min:3',
        'status' => 'required',
    ];

    public function render()
    {
        $tasks = Task::orderBy('name') // Możliwość dodania sortowania po nazwie
            ->paginate(10); // Paginacja - 10 zadań na stronę

        return view('livewire.task-list', compact('tasks'));
    }
}

				
			

Tutaj wykorzystujemy WithPagination, które pozwala na łatwą paginację wyników. Dzięki temu, w widoku będziemy mogli dodać linki do kolejnych stron wyników.

3. Tworzenie nowego zadania

Dodajmy metodę, która umożliwi dodawanie nowych zadań do bazy danych. Będzie to metoda, która wywołuje create() modelu Eloquent.

				
					public function create()
{
    $this->validate();

    Task::create([
        'name' => $this->name,
        'status' => $this->status,
    ]);

    $this->reset(); // Resetujemy dane formularza
    session()->flash('message', 'Zadanie zostało dodane!');
}

				
			

Metoda create() tworzy nowe zadanie w bazie, a po zakończeniu procesu wywołujemy reset(), aby wyczyścić dane formularza.

4. Edytowanie zadania

Edytowanie zadania wymaga wyświetlenia istniejącego zadania w formularzu, a następnie zapisania wprowadzonych zmian. Możemy dodać metodę edit(), która będzie wywoływana po kliknięciu przycisku edycji.

				
					public function edit($taskId)
{
    $this->isEditing = true;
    $task = Task::find($taskId);

    $this->taskId = $task->id;
    $this->name = $task->name;
    $this->status = $task->status;
}

public function update()
{
    $this->validate();

    $task = Task::find($this->taskId);
    $task->update([
        'name' => $this->name,
        'status' => $this->status,
    ]);

    $this->reset();
    $this->isEditing = false;
    session()->flash('message', 'Zadanie zostało zaktualizowane!');
}

				
			

Metoda edit() ustawia dane wybranego zadania w formularzu, a metoda update() aktualizuje je w bazie danych.

5. Usuwanie zadania

Aby usunąć zadanie, wystarczy dodać metodę delete(), która usunie zadanie z bazy danych.

				
					public function delete($taskId)
{
    Task::find($taskId)->delete();
    session()->flash('message', 'Zadanie zostało usunięte!');
}

				
			

Paginacja i sortowanie

Livewire posiada wbudowane mechanizmy do paginacji, które znacznie upraszczają implementację tego elementu w aplikacji. Aby dodać paginację, wystarczy użyć WithPagination, jak pokazano wcześniej, oraz dodać w widoku odpowiednie elementy do nawigacji między stronami.

Paginacja w widoku

				
					<div>
    @foreach($tasks as $task)
        <div>{{ $task->name }} - {{ $task->status }}</div>
        <button wire:click="edit({{ $task->id }})">Edytuj</button>
        <button wire:click="delete({{ $task->id }})">Usuń</button>
    @endforeach

    <div>
        {{ $tasks->links() }} 
    </div>
</div>

				
			

$tasks->links() generuje linki do paginacji, umożliwiając przechodzenie między stronami.

Sortowanie w locie

Do dodania sortowania wystarczy wywołać metodę orderBy() na zapytaniu do bazy danych. Można to łatwo rozszerzyć, pozwalając użytkownikowi sortować listę po różnych kolumnach.

				
					$tasks = Task::orderBy($this->sortBy, $this->sortDirection)
    ->paginate(10);

				
			

Wartości sortBy i sortDirection możemy przechowywać w publicznych właściwościach komponentu i umożliwić użytkownikowi kliknięcie na nagłówki tabeli, by zmieniać kryteria sortowania.

Walidacja danych

W Livewire walidacja danych jest niezwykle łatwa do zaimplementowania. Jak wspomniano wcześniej, reguły walidacji definiujemy w zmiennej $rules. Możemy także dodać niestandardowe komunikaty walidacyjne za pomocą $messages.

				
					protected $rules = [
    'name' => 'required|min:3',
    'status' => 'required',
];

protected $messages = [
    'name.required' => 'Proszę podać nazwę zadania.',
    'status.required' => 'Proszę wybrać status zadania.',
];

				
			

Jeśli dane nie spełniają reguł walidacji, Livewire automatycznie przekroczy formularz i wyświetli błędy. Możemy je wyświetlić w widoku przy pomocy dyrektywy @error.

				
					<div>
    @error('name') <span class="error">{{ $message }}</span> @enderror
</div>

				
			

Ćwiczenia

Ćwiczenie 1: Stworzenie komponentu TaskList

Zaczynamy od stworzenia komponentu TaskList, który będzie odpowiedzialny za zarządzanie zadaniami. Aby to zrobić, należy utworzyć komponent Livewire:

				
					php artisan make:livewire TaskList

				
			

Po tym kroku, w folderze app/Http/Livewire znajdziesz plik TaskList.php. Zawiera on kod komponentu, który będziemy rozbudowywać. Dodajmy teraz podstawową funkcjonalność wyświetlania zadań:

				
					// app/Http/Livewire/TaskList.php

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

class TaskList extends Component
{
    use WithPagination;

    public $name, $status, $taskId;
    public $isEditing = false;

    protected $rules = [
        'name' => 'required|min:3',
        'status' => 'required',
    ];

    public function render()
    {
        // Pobieranie zadań z bazy danych z paginacją
        $tasks = Task::orderBy('name')->paginate(10);

        return view('livewire.task-list', compact('tasks'));
    }
}

				
			

Tutaj korzystamy z WithPagination dla łatwego zarządzania paginacją. Komponent wyciąga zadania z bazy danych z sortowaniem po nazwie oraz paginacją – 10 rekordów na stronę.

Ćwiczenie 2: Dodanie paginacji

W Livewire paginacja jest bardzo łatwa do zaimplementowania, wystarczy użyć metody links(), która automatycznie generuje linki do stron.

Oto jak wygląda widok task-list.blade.php, który będzie wyświetlał zadania oraz umożliwiał paginację:

				
					

<div>
    <h1>Lista Zadań</h1>

    
    <ul>
        @foreach($tasks as $task)
            <li>
                {{ $task->name }} - {{ $task->status }}
                <button wire:click="edit({{ $task->id }})">Edytuj</button>
                <button wire:click="delete({{ $task->id }})">Usuń</button>
            </li>
        @endforeach
    </ul>

    
    <div>
        {{ $tasks->links() }}
    </div>
</div>

				
			

Ćwiczenie 3: Sortowanie w locie

Aby dodać sortowanie w locie, wystarczy w komponencie przekazać zmienne do kontrolowania sortowania, np. sortBy i sortDirection. Poniżej dodajemy możliwość sortowania po nazwie zadania.

W TaskList.php dodajemy:

				
					public $sortBy = 'name';
public $sortDirection = 'asc';

public function render()
{
    $tasks = Task::orderBy($this->sortBy, $this->sortDirection)
                ->paginate(10);

    return view('livewire.task-list', compact('tasks'));
}

				
			

Następnie w widoku dodajemy linki do zmiany sortowania:

				
					
<table>
    <thead>
        <tr>
            <th wire:click="sortByColumn('name')">
                Nazwa
                @if($sortBy == 'name')
                    @if($sortDirection == 'asc') &#8593; @else &#8595; @endif
                @endif
            </th>
            <th wire:click="sortByColumn('status')">
                Status
                @if($sortBy == 'status')
                    @if($sortDirection == 'asc') &#8593; @else &#8595; @endif
                @endif
            </th>
        </tr>
    </thead>
    <tbody>
        @foreach($tasks as $task)
            <tr>
                <td>{{ $task->name }}</td>
                <td>{{ $task->status }}</td>
                <td>
                    <button wire:click="edit({{ $task->id }})">Edytuj</button>
                    <button wire:click="delete({{ $task->id }})">Usuń</button>
                </td>
            </tr>
        @endforeach
    </tbody>
</table>

<div>
    {{ $tasks->links() }}
</div>

				
			

Dodajmy metodę sortowania do komponentu:

				
					public function sortByColumn($column)
{
    if ($this->sortBy == $column) {
        $this->sortDirection = $this->sortDirection == 'asc' ? 'desc' : 'asc';
    } else {
        $this->sortBy = $column;
        $this->sortDirection = 'asc';
    }
}

				
			

Ćwiczenie 4: Dodanie walidacji przy tworzeniu i edytowaniu zadania

Livewire automatycznie integruje się z systemem walidacji Laravel. Możemy zdefiniować reguły walidacji dla pól formularza, które będą używane przy tworzeniu oraz edytowaniu zadań.

W naszym komponencie, zaktualizujmy metodę create() i update(), aby używały walidacji:

				
					public function create()
{
    $this->validate();

    Task::create([
        'name' => $this->name,
        'status' => $this->status,
    ]);

    $this->reset();
    session()->flash('message', 'Zadanie zostało dodane!');
}

public function update()
{
    $this->validate();

    $task = Task::find($this->taskId);
    $task->update([
        'name' => $this->name,
        'status' => $this->status,
    ]);

    $this->reset();
    $this->isEditing = false;
    session()->flash('message', 'Zadanie zostało zaktualizowane!');
}

				
			

W widoku formularza możemy teraz dodać dynamiczne wyświetlanie błędów walidacyjnych:

				
					
<div>
    @if($isEditing)
        <h3>Edytuj zadanie</h3>
    @else
        <h3>Dodaj nowe zadanie</h3>
    @endif

    <form wire:submit.prevent="{{ $isEditing ? 'update' : 'create' }}">
        <div>
            <label for="name">Nazwa zadania</label>
            <input type="text" id="name" wire:model="name">
            @error('name') <span class="error">{{ $message }}</span> @enderror
        </div>

        <div>
            <label for="status">Status</label>
            <input type="text" id="status" wire:model="status">
            @error('status') <span class="error">{{ $message }}</span> @enderror
        </div>

        <button type="submit">{{ $isEditing ? 'Zaktualizuj' : 'Dodaj' }}</button>
    </form>
</div>

				
			

Ćwiczenie 5: Usuwanie zadania

Dodajmy funkcję usuwania zadania w komponencie:

				
					public function delete($taskId)
{
    Task::find($taskId)->delete();
    session()->flash('message', 'Zadanie zostało usunięte!');
}

				
			

A w widoku możemy dodać przyciski do usuwania zadań:

				
					<button wire:click="delete({{ $task->id }})">Usuń</button>

				
			

Podsumowanie

Livewire ułatwia pracę z danymi w aplikacjach Laravel, szczególnie w przypadku operacji CRUD. Dzięki wbudowanej obsłudze paginacji, sortowania i walidacji, możemy łatwo zbudować interaktywne komponenty do zarządzania danymi w bazie. Integracja z Eloquent i mechanizmami Livewire pozwala na efektywne zarządzanie danymi i zapewnia dynamiczne doświadczenia użytkownika.