LinKeeper une alternative à LinkTree avec Laravel et Filament - Commençons à jouer avec FilamentPHP
Par HappyToDev · 9 mins · Tutorial
This article also exists in English version 🇬🇧

Ok, maintenant la base de votre application est installée.

Nous pouvons nous plonger dans quelque chose de plus concret.

Ce dont nous avons besoin pour gérer nos liens.

Rappelez-vous ce que j'ai dit dans la première partie :

  • un utilisateur peut s'identifier, se connecter, se déconnecter et gérer son compte
  • un utilisateur peut voir la liste des liens qui lui sont associés
  • un utilisateur peut ajouter, modifier ou supprimer un lien
  • un utilisateur peut avoir un lien partageable pour partager ses liens avec le monde entier
  • un lien a un nom, un statut (publié ou non) et évidemment une url. Nous ajouterons peut-être d'autres options plus tard.

un utilisateur peut s'identifier, se connecter, se déconnecter et gérer son compte

Cela est fait nativement avec Filament. En fait, pas complètement, on peut se connecter, se déconnecter mais pas s'enregistrer ni même gérer son propre compte.

Signin view

Mais vous verrez qu'il est très facile d'ajouter une page d'inscription et une page de profil pour les utilisateurs.

Dans app/Providers/Filament/AdminPanelProvider.php

Path to AdminPanelProvider.php

il suffit d'ajouter 2 lignes sous l'appel à la fonction login() pour implémenter les deux fonctionnalités :

class AdminPanelProvider extends PanelProvider
{
    public function panel(Panel $panel): Panel
    {
        return $panel
            ->default()
            ->id('admin')
            ->path('admin')
            ->login()
            // Lesson 2 : here we add registration and profile page
            ->registration() // registration
            ->profile()      // profile management
            ->colors([
                'primary' => Color::Amber,
            ])
            ->discoverResources(in: app_path('Filament/Resources'), for: 'App\\Filament\\Resources')
            ->discoverPages(in: app_path('Filament/Pages'), for: 'App\\Filament\\Pages')
            ->pages([
                Pages\Dashboard::class,
            ])
            ->discoverWidgets(in: app_path('Filament/Widgets'), for: 'App\\Filament\\Widgets')
            ->widgets([
                Widgets\AccountWidget::class,
                Widgets\FilamentInfoWidget::class,
            ])
            ->middleware([
                EncryptCookies::class,
                AddQueuedCookiesToResponse::class,
                StartSession::class,
                AuthenticateSession::class,
                ShareErrorsFromSession::class,
                VerifyCsrfToken::class,
                SubstituteBindings::class,
                DisableBladeIconComponents::class,
                DispatchServingFilamentEvent::class,
            ])
            ->authMiddleware([
                Authenticate::class,
            ]);
    }
}

Retournez sur la page de login : http://linkeeper.com/admin/login et vous verrez

Signin page with register link

que le lien pour l'inscription a été ajouté.

Si vous vous connectez, vous verrez sous votre menu le nouveau sous-menu 'Profil'

Image description

Il vous permet de modifier votre pseudo, votre adresse e-mail et de changer votre mot de passe.

Image description

Donc, après cette étape très rapide, nous pouvons vérifier la première fonctionnalité :

✅ un utilisateur peut s'identifier, se connecter, se déconnecter et gérer son compte

  • un utilisateur peut voir la liste des liens qui lui sont associés
  • un utilisateur peut ajouter, modifier ou supprimer un lien
  • un utilisateur peut avoir un lien partageable pour partager ses liens avec le monde entier
  • un lien a un nom, un statut (publié ou non) et évidemment un lien. Nous ajouterons peut-être d'autres options plus tard.

un lien a un nom, un statut (publié ou non) et évidemment une url

Je sais que ce n'est pas dans l'ordre de la liste, mais d'abord c'est moi qui écris, ensuite j'ai besoin de définir la table links et ses champs avant qu'un utilisateur puisse ajouter un lien, n'est-ce pas ?

Alors laissez-moi faire ! 🤣

Réfléchissons au schéma de la table

Ok, quels seront les champs de notre table links ?

Bien sûr un id, une url, une courte description, un statut published (publié ou non), une clé étrangère user_id et enfin les classiques created_at et updated_at.

Le user_id sert à définir la relation entre un utilisateur et un lien.

En fait, nous pouvons résumer cette relation par :

C'est pourquoi nous utilisons une relation one-to-many.

Qu'en pensez-vous, y a-t-il quelque chose qui manque ? Dites-moi dans les commentaires si vous pensez que j'ai oublié quelque chose.

Ok, maintenant nous devons créer la migration pour la table links.

php artisan make:migration create_links_table
Image description

À présent nous devons éditer cette migration pour ajouter les champs à la table. Donc, éditons cette migration :

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('links', function (Blueprint $table) {
            $table->id();
            $table->string('url');
            $table->string('description', 255)->nullable();
            $table->boolean('published')->default(false);
            $table->foreignId('user_id')->constrained('users')->onDelete('cascade');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('links');
    }
};

Lançons la migration

Ok, il est temps de migrer. Pour cela, nous allons utiliser (encore) Artisan.

php artisan migrate

Rappelez vous que si vous faites une erreur ou que vous voulez modifier votre le schéma de votre table, vous pouvez rollback votre migration avec :

php artisan migrate:rollback

Et si vous voulez voir le statis de vos migrations, vous pouvez utiliser :

php artisan migrate:status

Regardez ce screenshot capturé avant de lancer la dernière migration.

Image description

On peut y voir deux choses :

Définir le modèle et la relation

L'étape suivante consiste à définir le modèle Link et à adapter le modèle User.

Nous utiliserons à nouveau Artisan :

php artisan make:model Link
Image description

Nous devons maintenant indiquer au modèle qu'un lien appartient à un utilisateur.

Ajoutez cette fonction à la classe de modèle Link :

    public function user()
    {
        return $this->belongsTo(User::class);
    }

Pour terminer avec le modèle, nous devons mettre à jour le modèle User en lui ajoutant une fonction links

    public function links()
    {
        return $this->hasMany(Link::class);
    }

Nous avons besoin de fausses données avec lesquelles jouer

Après avoir défini notre schéma de base de données, faisons quelque chose de très important.

Créons de fausses données pour jouer avec notre application. De grâce, n'utilisez pas les données de production de votre base de données pendant le développement, pour de nombreuses raisons telles que l'éthique, la confidentialité et la sécurité.

Nous sommes des professionnels, alors agissons en conséquence.

Les factories

Par défaut, Laravel est livré avec la factory User, nous avons donc juste besoin de créer une factory Link.

php artisan make:factory Link

Dans la fonction definition générée, on ajoute ce code :

    public function definition(): array
    {
        return [
            'url' => fake()->url(),
            'description' => fake()->sentence(5),
            'published' => fake()->boolean(80),
        ];
    }

Les factories utilisent Faker pour générer des données fausses mais très proches de données réelles.

Dans ce code, nous générons :

Le seeder

Laravel est livré avec database/seeders/DatabaseSeeder.php

Chemin vers DatabaseSeeder.php

Nous devons l'éditer pour utiliser notre usine.

Voici le code :

    /**
     * Seed the application's database.
     */
    public function run(): void
    {
        User::factory()->create([
            'name' => 'Happytodev',
            'email' => 'happytodev@gmail.com',
            'email_verified_at' => now(),
            'password' => Hash::make('Password*'),
            'remember_token' => Str::random(10),
        ]);

        User::factory()
            ->count(10)
            ->create()
            ->each(function ($user) {
                Link::factory()
                    ->count(rand(2, 10))
                    ->create(['user_id' => $user->id]);
            });
    }

Quelques explications :

Et maintenant, la magie peut commencer.

Revenons dans votre terminal, et lançons simplement cette commande :

php artisan db:seed
Seeding database

Allez dans votre visualateur de base de données préféré, et vérifiez le contenu des tables users et links.

Vous devriez voir quelque chose comme ceci :

Links table

Tinker

Si vous voulez vérifier que tout fonctionne bien, allons faire une ballade dans Tinker.

php artisan tinker

Tinker nous permet de jouer avec nos données et de vérifier que la relation fonctionne correctement.

User::find(6)->links()->get(['id', 'url', 'published', 'user_id']);

Ici, nous demandons de trouver tous les liens pour l'utilisateur avec id = 6. Nous demandons également de ne récupérer que les champs intéressants.

Tinker query results

En voyant ces résultats, nous pouvons immédiatement améliorer quelque chose.

Et si nous ne voulions que les liens publiés ?

On peut alors procéder comme suit :

> User::find(6)->links()->where('published', 1)->get(['id', 'url', 'published', 'user_id']) ;

Mais il y a une meilleure façon de faire cela et de ne pas se répéter plus tard. Nous pouvons utiliser des scopes.

Un scope peut ajouter une série de requêtes à votre requête. Les scopes peuvent être globaux ou locaux. Je vous laisse consulter [la documentation] (https://laravel.com/docs/11.x/eloquent#query-scopes) pour en savoir plus.

Ici, nous allons utiliser un scope local.

Allez dans votre modèle link et ajoutez cette fonction :

    /**
     * Scope a query to only include published links.
     */
    public function scopePublished(Builder $query): void
    {
        $query->where('published', 1);
    }

Maintenant, si on utilise la requête suivante dans Tinker :

User::find(6)->links()->published()->get(['id', 'url', 'published', 'user_id']);

on obtiendra uniquement les liens publiés pour l'utilisateur avec l'id = 6.

Tinker query using scope

Et voilà, on peut valider un autre point sur notre liste :

✅ un utilisateur peut s'identifier, se connecter, se déconnecter et gérer son compte

  • un utilisateur peut voir la liste des liens qui lui sont associés
  • un utilisateur peut ajouter, modifier ou supprimer un lien
  • un utilisateur peut avoir un lien partageable pour partager ses liens avec le monde entier

✅ un lien a un nom, un statut (publié ou non) et évidemment un lien. Nous ajouterons peut-être d'autres options plus tard.

Conclusion

Nous allons nous arrêter là pour aujourd'hui.

Dans ce chapitre plus long, nous avons fait les premiers pas pour découvrir :

Comme d'habitude, j'attends vos commentaires ci-dessous.

A bientôt.

This article also exists in English version 🇬🇧

#laravel #filamentphp #php

Ce contenu t'a plu ou appris quelque chose ? 👀

Si la réponse est oui, alors tu peux peut-être me supporter 💪 ?

Il y a plusieurs façon de me supporter et certaines sont gratuites, je t'invite à les découvrir dans ma page dédiée 👇