Création une application de tâches multi-utilisateurs avec Laravel JetStream
Laravel Jetstream est un échafaudage d'applications magnifiquement conçu pour Laravel. Jetstream fournit le point de départ idéal pour votre prochaine application Laravel et comprend la connexion, l'enregistrement, la vérification des e-mails, l'authentification à deux facteurs, la gestion de session, la prise en charge de l'API via Laravel Sanctum et la gestion d'équipe en option.
-
Les Prérequis
-
Installation de Jetstream
Avec une installation Laravel fonctionnelle et opérationnelle, l'étape suivante consiste à installer Jetstream avec Composer
composer require laravel/jetstream
Configurer Jetstream pour utiliser Livewire
php artisan jetstream:install livewire
Ensuite, installez et construisez les dépendances de NPM :
npm install && npm run dev
Ouvrez le fichier app/Provider/AppServiceProvider.php et remplacer par ça
<?php
namespace App\Providers;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Schema::defaultStringLength(191);
}
}
Enfin, migrez la base de données afin que toutes les tables requises soient créées :
php artisan migrate
3. Création de la table todo list
Nous aurons besoin d'une table pour contenir nos tâches à faire. Cela nécessitera un modèle et une migration . Pour ce faire nous allons utiliser la commande artisan suivante :
php artisan make:model TodoItem
php artisan make:migration create-todo-item-table
ou alors
php artisan make:model TodoItem -m
Remplacez le contenu du fichier de migration généré par la classe de migration suivante :
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateTodoItemTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('todo_items', function (Blueprint $table) {
$table->id();
$table->integer('user_id');
$table->string('description');
$table->boolean('done')->default(false);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('todo_items');
}
}
Faite la migration grace a la commande
php artisan migrate
Nous allons maintenant configuré les models pour que chaque utilisateur défini ces tache
Ouvrez le fichier app/Models/User.php et app/Models/TodoItem.php
- User.php et remplacer par ça
<?php
namespace App\Models;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Fortify\TwoFactorAuthenticatable;
use Laravel\Jetstream\HasProfilePhoto;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens;
use HasFactory;
use HasProfilePhoto;
use Notifiable;
use TwoFactorAuthenticatable;
/**
* The attributes that are mass assignable.
*
* @var string[]
*/
protected $fillable = [
'name',
'email',
'password',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array
*/
protected $hidden = [
'password',
'remember_token',
'two_factor_recovery_codes',
'two_factor_secret',
];
/**
* The attributes that should be cast.
*
* @var array
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
/**
* The accessors to append to the model's array form.
*
* @var array
*/
protected $appends = [
'profile_photo_url',
];
public function todoitems()
{
return $this->hasMany(TodoItem::class);
}
}
TodoItem.php et remplacer par ça
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class TodoItem extends Model
{
use HasFactory;
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'user_id', 'description', 'done',
];
public function user()
{
return $this->belongsTo(User::class);
}
}
4.Création une nouvelle section du tableau de bord
C'est maintenant que vient la partie la plus intéressante du travail, et aussi la plus difficile, car l'écart entre la préparation de la configuration initiale et la compréhension de tous les composants installés est assez important - à moins que vous ne soyez habitué à des choses comme Livewire et TailwindCSS, ce qui n'était pas mon cas au début.
Jetstream utilise à la fois les composants Blade et Livewire, ce qui peut entraîner une certaine confusion pour certaines personnes. D'après ma courte expérience, je pense que les composants Blade inclus sont essentiellement structurels et que vous pouvez créer vos nouvelles pages de tableau de bord en utilisant Livewire. Cela vous permettra d'intégrer des actions javascript sans écrire une seule ligne de JS !
Nous allons créer une nouvelle section dans le tableau de bord pour gérer les éléments à faire. Copiez la vue du tableau de bord pour l'utiliser comme base pour votre nouvelle page :
cp resources/views/dashboard.blade.php resources/views/todo.blade.php
Ensuite, créez deux composants Livewire : todo.show et todo.form :
php artisan make:livewire todo.show
php artisan make:livewire todo.form
Si vous vérifiez maintenant les dossiers de votre application, vous trouverez un nouveau répertoire Livewire dans app/Http. Il s'agit des contrôleurs de composants. Les vues générées sont situées dans resources/views/livewire/todo.
Ces composants seront rendus par le nouveau fichier todo.blade.php. Ouvrez ce fichier maintenant et remplacez la section x-jet-welcome par une balise Livewire pour rendre le composant show. Nous allons également inclure le composant de formulaire en haut. Voici à quoi ressemblera la vue mise à jour :
//resources/views/todo.blade.php
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('My To-Do List') }}
</h2>
</x-slot>
<div>
<div class="max-w-7xl mx-auto py-10 sm:px-6 lg:px-8">
@livewire('todo.form')
</div>
</div>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white overflow-hidden shadow-xl sm:rounded-lg">
@livewire('todo.show')
</div>
</div>
</div>
</x-app-layout>
Mettez également à jour le fichier resources/views/navigation-dropdown.blade.php pour y inclure votre nouvelle section. Voici à quoi ressemble mon code de liens de navigation mis à jour :
//resources/views/navigation-dropdown.blade.php
<!-- Navigation Links -->
<div class="hidden space-x-8 sm:-my-px sm:ml-10 sm:flex">
<x-jet-nav-link href="{{ route('dashboard') }}" :active="request()->routeIs('dashboard')">
{{ __('Dashboard') }}
</x-jet-nav-link>
<x-jet-nav-link href="{{ route('dashboard-todo') }}" :active="request()->routeIs('dashboard-todo')">
{{ __('To-Do List') }}
</x-jet-nav-link>
</div>
Maintenant, ajoutez une nouvelle route protégée dans routes/web.php. J'ai juste copié la route du tableau de bord et l'ai mise à jour en conséquence :
Route::middleware(['auth:sanctum', 'verified'])->get('/dashboard/todo', function () {
return view('todo');
})->name('dashboard-todo');
Si vous allez maintenant dans votre tableau de bord, vous devriez voir le nouveau lien dans le menu de navigation. Il affichera une page blanche, puisque nous n'avons pas encore personnalisé nos composants
Ensuite, nous allons personnaliser nos composants.
5. Création du formulaire
Pour inclure de nouveaux éléments dans la liste, nous devons créer un formulaire. La mauvaise nouvelle, c'est qu'il n'y a pas de commande pour le créer pour vous, mais la bonne nouvelle, c'est que vous pouvez utiliser les formulaires de profil inclus dans resources/views/profile comme base pour votre travail.
Ouvrez le fichier resources/views/livewire/todo/form.blade.php et remplacez le contenu par ce qui suit :
//resources/views/livewire/todo/form.blade.php
<x-jet-form-section submit="createItem">
<x-slot name="title">
{{ __('Create New To-Do Item') }}
</x-slot>
<x-slot name="description">
{{ __('Create a new item in your to-do list.') }}
</x-slot>
<x-slot name="form">
<div class="col-span-6 sm:col-span-4">
<x-jet-label for="description" value="{{ __('Item Description') }}" />
<x-jet-input id="description" type="text" class="mt-1 block w-full" wire:model.defer="description" autocomplete="description" />
<x-jet-input-error for="description" class="mt-2" />
</div>
</x-slot>
<x-slot name="actions">
<x-jet-action-message class="mr-3" on="saved">
{{ __('Saved.') }}
</x-jet-action-message>
<x-jet-button>
{{ __('Save') }}
</x-jet-button>
</x-slot>
</x-jet-form-section>
Le formulaire est prêt, mais nous devons encore configurer l'action qui traitera ce formulaire. Ouvrez le fichier app/Http/Livewire/Todo/Form.php et mettez-le à jour pour inclure la méthode createItem.
<?php
//app/Http/Livewire/Todo/Form.php
namespace App\Http\Livewire\Todo;
use App\Models\TodoItem;
use Livewire\Component;
class Form extends Component
{
public $description;
protected $rules = [
'description' => 'required|min:6'
];
public function render()
{
return view('livewire.todo.form');
}
public function createItem()
{
$this->validate();
$item = new TodoItem();
$item->user_id= auth()->user()->id;
$item->description = $this->description;
$item->save();
$this->emit('saved');
}
}
Mettez à jour le contrôleur du composant "show" pour injecter les éléments To-Do dans la vue, après les avoir obtenus avec un appel Eloquent. Nous avons également implémenté ici un écouteur d'événements qui sera déclenché lorsqu'un nouvel élément est enregistré, pour rafraîchir la liste des éléments :
<?php
//app/Http/Livewire/Todo/Show.php
namespace App\Http\Livewire\Todo;
use App\Models\TodoItem;
use Livewire\Component;
class Show extends Component
{
protected $listeners = ['saved'];
public function render()
{
$list = TodoItem::whereUserId(auth()->user()->id)->get()->sortByDesc('created_at');
return view('livewire.todo.show', [ 'list' => $list ]);
}
public function saved()
{
$this->render();
}
}
Mise à jour de la vue du composant "show" pour lister les éléments avec une boucle @foreach :
{{-- //resources/views/livewire/todo/show.blade.php --}}
<div>
<table class="table-auto w-full">
<thead>
<tr>
<th class="px-4 py-2">Item</th>
<th class="px-4 py-2">Status</th>
</tr>
</thead>
<tbody>
@foreach ($list as $item)
<tr @if($loop->even)class="bg-grey"@endif>
<td class="border px-4 py-2">{{ $item->description }}</td>
<td class="border px-4 py-2">@if($item->done)Done @else To Do @endif</td>
</tr>
@endforeach
</tbody>
</table>
</div>
6.Création d'actions Livewire pour mettre à jour et supprimer des éléments
Enfin, nous devons être en mesure de supprimer des éléments et de les marquer comme "done" (ou "undone"). Nous pouvons les implémenter assez facilement en utilisant les actions Livewire.
Tout d'abord, mettez à jour votre contrôleur de composant Todo/Show pour y inclure trois méthodes courtes : markAsDone, markAsToDo et deleteItem :
<?php
//app/Http/Livewire/Todo/Show.php
namespace App\Http\Livewire\Todo;
use App\Models\TodoItem;
use Livewire\Component;
class Show extends Component
{
protected $listeners = ['saved'];
public function render()
{
$list = TodoItem::whereUserId(auth()->user()->id)->get()->sortByDesc('created_at');
return view('livewire.todo.show', [ 'list' => $list ]);
}
public function saved()
{
$this->render();
}
public function markAsDone(TodoItem $item)
{
$item->done = true;
$item->save();
}
public function markAsToDo(TodoItem $item)
{
$item->done = false;
$item->save();
}
public function deleteItem(TodoItem $item)
{
$item->delete();
}
}
Enfin, mettez à jour votre vue pour inclure des boutons opérationnels pour ces actions :
{{-- //resources/views/livewire/todo/show.blade.php --}}
<div>
<table class="table-auto w-full">
<thead>
<tr>
<th class="px-4 py-2">Item</th>
<th class="px-4 py-2">Status</th>
<th class="px-4 py-2">Actions</th>
</tr>
</thead>
<tbody>
@foreach ($list as $item)
<tr @if($loop->even)class="bg-grey"@endif>
<td class="border px-4 py-2">{{ $item->description }}</td>
<td class="border px-4 py-2">@if($item->done)Done @else To Do @endif</td>
<td class="border px-4 py-2">
@if($item->done)
<button wire:click="markAsToDo({{ $item->id }})" class="bg-red-100 text-red-600 px-6 rounded-full">
Mark as "To Do"
</button>
@else
<button wire:click="markAsDone({{ $item->id }})" class="bg-gray-800 text-white px-6 rounded-full">
Mark as "Done"
</button>
@endif
<button wire:click="deleteItem({{ $item->id }})" class="bg-red-100 text-red-600 px-6 rounded-full">
Delete Permanently
</button>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
Et voilà ! Comme ça, sans écrire aucun Javascript, vous avez des composants dynamiques qui appellent des contrôleurs externes et se rafraîchissent automatiquement. C'est ce que je préfère dans tout ça !
download :todoliste
Cool