LinKeeper une alternative à LinkTree avec Laravel et Filament - Commençons à jouer avec FilamentPHP

10 avril 2024 Temps de lecture : 3 min Tutoriels happytodev happytodev
LinKeeper une alternative à LinkTree avec Laravel et Filament - Commençons à jouer avec FilamentPHP

TL;DR

Dans cette petite série, nous allons apprendre les bases de Laravel à travers une petite application LinKeeper. Son but est de garder vos liens et de les partager avec le monde entier.

Partie de la série

LinKeeper une alternative à LinkTree avec Laravel et Filament

⭐ En vedette

Dans cette petite série, nous allons apprendre les bases de Laravel à travers une petite application LinKeeper. Son but est de garder vos liens et de les partager avec le monde entier.

2 articles
2
LinKeeper une alternative à LinkTree avec Laravel et Filament - Commençons à jouer avec FilamentPHP (actuel)
10 avril 2024

Table des matières

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.

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

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 :

  • un utilisateur peut avoir 0, 1 ou plusieurs liens
  • un lien (unique par son id) appartient à un et un seul utilisateur

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 :

  • tout d'abord la migration pour la table links n'a pas encore été exécutée
  • deuxièmement les trois premières migrations ont été lancées dans la même séquence : la séquence numéro [1]

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 :

  • une url
  • une description basée sur une phrase de 5 mots. En fait, comme je laisse le second paramètre de la méthode sentence par défaut (true), sentence va générer des phrases d'environ 5 mots. Cela peut être quatre, cinq, six ou même sept mots.
  • true ou false pour le champ published avec un pourcentage minimum de true de 80%.

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 :

  • D'abord je crée mon propre utilisateur pour me connecter facilement à chaque fois avec mon propre email
  • La deuxième partie fait ceci
    • créer 10 faux utilisateurs
    • pour chaque utilisateur créer entre 2 et 10 liens, lors de la création nous demanadons à l'application que les champs user_id doivent être remplis avec le user->id qui vient d'être crée par la boucle each.

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 :

  • table users

Users table

  • table links

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 :

  • comment ajouter des fonctionnalités à la page de connexion dans Filament
  • les modèles
  • les factories
  • les seeders
  • Tinker
  • les scopes

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

A bientôt.

HappyToDev

À propos de l'auteur

HappyToDev

Hello moi c'est Fred, mais vous me connaissez plutôt via mon pseudo : HappyToDev.

Ma bio

Mari et 2 fois papa 💪

Ex de la Marine Nationale 🫡

Passionné d'informatique depuis mes 10 ans, j'ai le code dans la peau. Je suis toujours partant pour un laravel new 😉

Créateur des newsletters 🗞️ :

Créateur de Framework Heroes 🦸🏽‍♀️🦸🏻‍♂️

J'avais envie de faire ce site depuis plusieurs mois pour donner naissance à un job board spécialisé sur les devs utilisant des frameworks. J'espère que le concept vous plaira et que vous m'aiderez à le développer en m'apportant de nouvelles idées.

Précision utile : pour les devs c'est gratuit et vous pouvez y créer votre page profile et la partager avec une url unique. Voici la mienne en exemple

Jeter un oeil à Framework Heroes

Créateur de GiftKeepr 🎁

Une question simple : recevez vous des cadeaux qui ne vous plaisent pas à votre anniversaire, à Noël ou lors d'autres occasions ?

GiftKeepr est là pour que cela n'arrive plus jamais. Et c'est super simple !

  • Créez votre profil
  • Renseignez les cadeaux que vous souhaitez
  • Communiquez votre adresse GiftKeepr à votre entourage
  • Recevez les bons cadeaux pour les occasions que vous avez défini
  • Easy peasy !!

Jeter un oeil à GiftKeepr