Laravel 8

Créer un blog – les données

On a vu dans le précédent article la présentation générale du projet de blog et on a aussi créé un Laravel 8 de base avec l’authentification avec Laravel Breeze. On va maintenant se lancer et on va commencer par s’intéresser aux données. Que va contenir notre base ? Quelles tables nous sont nécessaires avec quelles colonnes ? Que va-t-on avoir comme relations ? Quelle sont les données de populations qu’on va générer pour que notre blog fonctionne dès le départ ? Ce sont les bonnes questions à se poser quand on démarre un projet, même si ça évolue forcément en cours de route

Vous pouvez télécharger le code final de cet article ici.

Les données de base de Laravel

Pour le moment avec notre Laravel de base on a ces migrations :

Donc :

  • une migration pour la table users
  • une migration pour la table password_resets pour le renouvellement des mots de passe
  • une migration pour la table failed_jobs pour les tâches qui ont échoué

Comme on va utiliser le système de notifications de Laravel pour notre blog (on va même un peu le détourner) on va ajouter la migration correspondante :

php artisan notifications:table

Les utilisateurs

Par défaut la table users comporte ces colonnes :

Schema::create('users', function (Blueprint $table) {
    $table->id();
    $table->string('name');
    $table->string('email')->unique();
    $table->timestamp('email_verified_at')->nullable();
    $table->string('password');
    $table->rememberToken();
    $table->timestamps();
});

On va avoir besoin d’autres informations :

  • role : pour savoir quelles sont les permissions de l’utilisateur : administrateur, rédacteur ou simple utilisateur. Ce n’est pas la peine d’utiliser un package pour ce genre de chose.
  • valid : pour valider un utilisateur au niveau des commentaires, le premier commentaire d’un utilisateur n’apparaît pas tout de suite, il faut que ce soit validé par l’administrateur ou l’auteur de l’article, les commentaires suivants par contre apparaitront tout de suite, c’est une façon d’éviter les trolls.

On ajoute donc ces deux colonnes :

Schema::create('users', function (Blueprint $table) {
    ...
    $table->enum('role', array('user', 'redac', 'admin'))->default('user');
    $table->boolean('valid')->default(false);
    ...
});

On a déjà un modèle User. Toutefois on va ajouter les deux colonnes qu’on a créées dans la propriété $fillable :

protected $fillable = [
    'name',
    'email',
    'password',
    'role', 
    'valid',
];

C’est facile à oublier et ensuite on cherche le bug un bon moment 🙂

On dispose déjà d’un factory pour cette table (database/factories/UserFactory.php). On va juste le compléter pour mettre la colonne valid à true :

return [
    'name' => $this->faker->name,
    'email' => $this->faker->unique()->safeEmail,
    'email_verified_at' => now(),
    'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi',
    'remember_token' => Str::random(10),
    'valid' => true,
];

Les catégories

Les articles vont être organisées en catégories, on va donc créer une table pour mémoriser ces informations. On va créer simultanément le modèle et la migration :

php artisan make:model Category -m

Pour les catégories on va avoir besoin de deux informations :

  • title : le titre
  • slug : c’est le texte qui va apparaître dans l’url et qui ne doit comporter que des caractères autorisés

D’autre part on ne va pas utiliser les timestamps, quel intérêt de savoir quand une catégorie a été créée ou modifiée ? On a donc :

Schema::create('categories', function (Blueprint $table) {
    $table->id();
    $table->string('title')->unique();
    $table->string('slug')->unique();
});

Pour le modèle on va supprimer le trait HasFactory qui ne nous servira pas et on crée la propriété $fillable :

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Category extends Model
{
    protected $fillable = [
        'title', 
        'slug',
    ];

    public $timestamps = false;
}

Les étiquettes (tags)

Les articles vont être aussi identifiables avec des étiquettes, ce qui permet une sélection transversale alors que les catégories créent une organisation verticale. On crée le modèle et la migration :

php artisan make:model Tag -m

Pour les étiquettes on va avoir besoin de deux informations comme pour les cétégories :

  • tag : le titre
  • slug : c’est le texte qui va apparaître dans l’url et qui ne doit comporter que des caractères autorisés

D’autre part on ne va pas non plus utiliser les timestamps, quel intérêt de savoir quand une étiquette a été créée ou modifiée ? On a donc :

Schema::create('tags', function(Blueprint $table) {
    $table->id();
    $table->string('tag', 50)->unique();
    $table->string('slug')->unique();
});

Pour le modèle on va aussi supprimer le trait HasFactory qui ne nous servira pas et on crée la propriété $fillable :

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Tag extends Model {

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = ['tag'];

    public $timestamps = false;
}

Les articles (posts)

Là c’est le gros morceau évidemment ! On crée le modèle, la migration et le factory :

php artisan make:model Post -mfc

 

On va avoir besoin de pas mal d’informations :

  • title : le titre
  • slug
  • seo_title : le titre pour le SEO
  • meta_keywords : les mots clés pour le SEO
  • meta_description : la description pour le SEO
  • excerpt : le texte d’introduction (celui qui apparaît en résumé sur la page d’accueil)
  • body : le corps de l’article
  • active : pour savoir si l’article est publié
  • image : pour le nom de l’image réduite pour la page d’accueil

 

Schema::create('posts', function(Blueprint $table) {
    $table->id();
    $table->timestamps();
    $table->string('title');
    $table->string('slug')->unique();
    $table->string('seo_title')->nullable();
    $table->text('excerpt');
    $table->text('body');
    $table->text('meta_description');
    $table->text('meta_keywords');
    $table->boolean('active')->default(false);
    $table->string('image')->nullable();
});

Pour le factory on va prévoir le remplissage automatique de quelques colonnes :

public function definition()
{
    return [
        'meta_description' => $this->faker->sentence($nbWords = 6, $variableNbWords = true),
        'meta_keywords' => implode(',', $this->faker->words($nb = 3, $asText = false)),
        'excerpt' => $this->faker->paragraph($nbSentences = 4, $variableNbSentences = true),
        'body' => $this->faker->paragraphs($nb = 8, $asText = true),
        'active' => true,
    ];
}

On gèrera titre, slug et image directement dans la population.

Pour le modèle on va créer la propriété $fillable et aussi prévoir le trait Notifiable :

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use App\Events\ModelCreated;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Notifications\Notifiable;

class Post extends Model
{
    use HasFactory, Notifiable;

    protected $fillable = [
        'title', 
        'slug', 
        'seo_title', 
        'excerpt', 
        'body', 
        'meta_description', 
        'meta_keywords', 
        'active', 
        'image', 
        'user_id',
    ];
}

Les relations

Arrivé à ce stade il est temps de s’intéresser aux relations. On va avoir :

  • un utilisateur peut avoir plusieurs articles (et réciproquement un article appartient à un seul utilisateur)
  • une catégorie peut avoir plusieurs articles et réciproquement un article peut appartenir à plusieurs catégories
  • un article peut avoir plusieurs étiquettes et une étiquette peut appartenir à plusieurs articles

Un utilisateur a plusieurs articles

Il faut mettre une clé étrangère dans la table posts, donc dans la migration :

public function up()
{
    Schema::create('posts', function(Blueprint $table) {
        ...
        $table->foreignId('user_id')
              ->constrained()
              ->onDelete('cascade')
              ->onUpdate('cascade');
    });
}

J’ai opté pour une cascade en cas de suppression ou de modification.

Au niveau des modèles on ajoute la relation dans User :

public function posts()
{
    return $this->hasMany(Post::class);
}

Et dans Post :

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

Relation entre catégories et articles

Là c’est une relation many to many, il faut donc une table pivot :

php artisan make:migration category_post_table

<?php

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

class CategoryPostTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('category_post', function(Blueprint $table) {
            $table->id();
            $table->foreignId('category_id')
                  ->constrained()
                  ->onDelete('cascade')
                  ->onUpdate('cascade');
            $table->foreignId('post_id')
                  ->constrained()
                  ->onDelete('cascade')
                  ->onUpdate('cascade');
        });
    }

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

On a évidemment dans cette table deux clés étrangères :

  • category_id : pour référencer la catégorie
  • post_id : pour référencer l’article

On ajoute la relation dans Category :

public function posts()
{
    return $this->belongsToMany(Post::class);
}

Et dans Post :

public function categories()
{
    return $this->belongsToMany(Category::class);
}

Relation entre étiquettes et articles

Là c’est encore une relation many to many, il faut donc une table pivot :

php artisan make:migration post_tag_table

<?php

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

class PostTagTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('post_tag', function(Blueprint $table) {
            $table->id();
            $table->foreignId('post_id')
                  ->constrained()
                  ->onDelete('cascade')
                  ->onUpdate('cascade');
            $table->foreignId('tag_id')
                  ->constrained()
                  ->onDelete('cascade')
                  ->onUpdate('cascade');
        });
    }

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

 

On a évidemment aussi dans cette table deux clés étrangères :

  • tag_id : pour référencer l’étiquette
  • post_id : pour référencer l’article

On ajoute la relation dans Tag :

public function posts()
{
   return $this->belongsToMany(Post::class);
}

Et dans Post :

public function tags()
{
    return $this->belongsToMany(Tag::class);
}

Les commentaires

Pour les commentaires on a quelque chose d’un peu spécial parce on a un système hiérarchique, on a des commentaires de commentaire, et des des commentaires de commentaire de commentaire, et ainsi de suite. Pour gérer ça on va utiliser le package NestedSet. Il faut d’abord l’installer :

composer require kalnoy/nestedset

On crée le modèle avec factory et migration :

php artisan make:model Comment -mfc

Pour la migration on a pas besoin de grand chose, juste une colonne body pour le texte du commentaire. Par contre on va ajouter les colonnes nécessaires pour la gestion des relations entre les commentaires, mais le package va bien nous aider. En plus il faut relier cette table aux utilisateurs et articles :

<?php

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

class CreateCommentsTable extends Migration {

    public function up()
    {
        Schema::create('comments', function(Blueprint $table) {
            $table->id();
            $table->timestamps();
            $table->text('body');
            $table->nestedSet();
            $table->foreignId('user_id')
                  ->constrained()
                  ->onDelete('cascade')
                  ->onUpdate('cascade');
            $table->foreignId('post_id')
                  ->constrained()
                  ->onDelete('cascade')
                  ->onUpdate('cascade');
        });
    }

    public function down()
    {
        Schema::dropIfExists('comments');
    }
}

 

Pour le factory on renseigne juste body :

public function definition()
{
    return [
        'body' => $this->faker->paragraph($nbSentences = 4, $variableNbSentences = true),
    ];
}

Pour le modèle Comment on va ajouter le trait NodeTrait pour le package, et aussi Notifiable. D’autre part on crée la propriété $fillable.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Kalnoy\Nestedset\NodeTrait;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Notifications\Notifiable;

class Comment extends Model
{
    use NodeTrait, HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'body', 
        'post_id', 
        'user_id', 
    ];
}

Pour les relations on a :

  • un utilisateur a plusieurs commentaires et un commentaire appartient à un utilisateur
  • un article a plusieurs commentaires et un commentaire appartient à un article

Donc dans User :

public function comments()
{
    return $this->hasMany(Comment::class);
}

Dans Post :

public function comments()
{
    return $this->hasMany(Comment::class);
}

Dans Post on va ajouter une relation spécifique pour récupérer les commentaires valides (en fait les commentaires qui ont été rédigés par un utilisateur validé) :

public function validComments()
{
    return $this->comments()->whereHas('user', function ($query) {
        $query->whereValid(true);
    });
}

Et dans Comment on précise l’appartenance à l’utilisateur et à l’article :

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

public function post()
{
    return $this->belongsTo(Post::class);
}

Une petite visualisation de la situation :

La population

Maintenant qu’on a créé les migrations essentielles vous pouvez lancer la migration pour voir si tout se passe bien :

php artisan migrate

Vous devez obtenir ces 11 tables :

Vous pouvez aussi vérifier que vous avez les bonnes colonnes dans chacune de ces tables.

On ajoutera utltérieurement d’autres tables pour les contacts, les pages… Mais pour le moment on va s’en passer et on va faire tourner le blog.

Pour nos essais on va avoir besoin de données dans les tables, on va utiliser pour ça la population (seeder) avec ce fichier :

Pour le moment la fonction run est vide (on a juste un peu de code commenté) :

public function run()
{
    // \App\Models\User::factory(10)->create();
}

Les utilisateurs

On va créer un administrateur, 2 rédacteurs et 3 utilisateurs de base :

use App\Models\{ User, Contact, Post, Comment, Page };
use Illuminate\Support\Facades\DB;

...

User::withoutEvents(function () {
    // Create 1 admin
    User::factory()->create([
        'role' => 'admin',
    ]);
    // Create 2 redactors
    User::factory()->count(2)->create([
        'role' => 'redac',
    ]);
    // Create 3 users
    User::factory()->count(3)->create();
});

$nbrUsers = 6;

J’utilise withoutEvents pour inhiber les événements, pour l’instant on n’en a pas mais on en mettra en place plus tard, du coup ça n’aura pas d’impact sur la population.

Les catégories

On crée 3 catégories :

DB::table('categories')->insert([
    [
        'title' => 'Category 1',
        'slug' => 'category-1'
    ],
    [
        'title' => 'Category 2',
        'slug' => 'category-2'
    ],
    [
        'title' => 'Category 3',
        'slug' => 'category-3'
    ],
]);

$nbrCategories = 3;

Les étiquettes

On prévoit 6 étiquettes :

DB::table('tags')->insert([
    ['tag' => 'Tag1', 'slug' => 'tag1'],
    ['tag' => 'Tag2', 'slug' => 'tag2'],
    ['tag' => 'Tag3', 'slug' => 'tag3'],
    ['tag' => 'Tag4', 'slug' => 'tag4'],
    ['tag' => 'Tag5', 'slug' => 'tag5'],
    ['tag' => 'Tag6', 'slug' => 'tag6']
]);

$nbrTags = 6;

Les articles

On crée 9 articles qu’on attribue aux 2 rédacteurs :

Post::withoutEvents(function () {
    foreach (range(1, 2) as $i) {
        Post::factory()->create([
            'title' => 'Post ' . $i,
            'slug' => 'post-' . $i,
            'seo_title' => 'Post ' . $i,
            'user_id' => 2,
            'image' => 'img0' . $i . '.jpg',
        ]);
    }
    foreach (range(3, 9) as $i) {
        Post::factory()->create([
            'title' => 'Post ' . $i,
            'slug' => 'post-' . $i,
            'seo_title' => 'Post ' . $i,
            'user_id' => 3,
            'image' => 'img0' . $i . '.jpg',
        ]);
    }
});

$nbrPosts = 9;

Pour les images je pars du principe qu’on a des images qui s’appellent img00, img01, img02…

On va attacher des étiquettes et des catégories aux articles :

// Tags attachment
$posts = Post::all();
foreach ($posts as $post) {
    if ($post->id === 9) {
        $numbers=[1,2,5,6];
        $n = 4;
    } else {
        $numbers = range (1, $nbrTags);
        shuffle ($numbers);
        $n = rand (2, 4);
    }
    for($i = 0; $i < $n; ++$i) {
        $post->tags()->attach($numbers[$i]);
    }
}

// Set categories
foreach ($posts as $post) {
    if ($post->id === 9) {
        DB::table ('category_post')->insert ([
            'category_id' => 1,
            'post_id' => 9,
        ]);
    } else {
        $numbers = range (1, $nbrCategories);
        shuffle ($numbers);
        $n = rand (1, 2);
        for ($i = 0; $i < $n; ++$i) {
            DB::table ('category_post')->insert ([
                'category_id' => $numbers[$i],
                'post_id' => $post->id,
            ]);
        }
    }
}

Les commentaires

On commence par créer des commentaires de premier niveau :

foreach (range(1, $nbrPosts - 1) as $i) {
    Comment::factory()->create([
        'post_id' => $i,
        'user_id' => rand(1, $nbrUsers),
    ]);
}

Pour les commentaires des autres niveaux le package permet d’utiliser des tableaux, ce qui est bien pratique :

$faker = \Faker\Factory::create();

Comment::create([
    'post_id' => 2,
    'user_id' => 3,
    'body' => $faker->paragraph($nbSentences = 4, $variableNbSentences = true),

    'children' => [
        [
            'post_id' => 2,
            'user_id' => 4,
            'body' => $faker->paragraph($nbSentences = 4, $variableNbSentences = true),

            'children' => [
                [
                    'post_id' => 2,
                    'user_id' => 2,
                    'body' => $faker->paragraph($nbSentences = 4, $variableNbSentences = true),
                ],
            ],
        ],
    ],
]);

Comment::create([
    'post_id' => 2,
    'user_id' => 6,
    'body' => $faker->paragraph($nbSentences = 4, $variableNbSentences = true),

    'children' => [
        [
            'post_id' => 2,
            'user_id' => 3,
            'body' => $faker->paragraph($nbSentences = 4, $variableNbSentences = true),
        ],
        [
            'post_id' => 2,
            'user_id' => 6,
            'body' => $faker->paragraph($nbSentences = 4, $variableNbSentences = true),

            'children' => [
                [
                    'post_id' => 2,
                    'user_id' => 3,
                    'body' => $faker->paragraph($nbSentences = 4, $variableNbSentences = true),
                
                    'children' => [
                        [
                            'post_id' => 2,
                            'user_id' => 6,
                            'body' => $faker->paragraph($nbSentences = 4, $variableNbSentences = true),
                        ],
                    ],
                ],
            ],
        ],
    ],
]);

Comment::create([
    'post_id' => 4,
    'user_id' => 4,
    'body' => $faker->paragraph($nbSentences = 4, $variableNbSentences = true),

    'children' => [
        [
            'post_id' => 4,
            'user_id' => 5,
            'body' => $faker->paragraph($nbSentences = 4, $variableNbSentences = true),

            'children' => [
                [   'post_id' => 4,
                    'user_id' => 2,
                    'body' => $faker->paragraph($nbSentences = 4, $variableNbSentences = true),
                ],
                [
                    'post_id' => 4,
                    'user_id' => 1,
                    'body' => $faker->paragraph($nbSentences = 4, $variableNbSentences = true),
                ],
            ],
        ],
    ],
]);

Il ne reste plus qu’à lancer la population :

php artisan db:seed

Vérifiez dans vos tables que vous obtenez bien les données attendues.

Conclusion

On dispose maintenant des tables et des données pour commencer à coder notre frontend, ce que nous feraons dans le prochain article. Il faudra aussi s’occuper des medias avec un nouveau package.

Print Friendly, PDF & Email

49 commentaires

  • WinnerK

    Bonjour coach j’ai suivi tous les cours à la lettre mais au niveau du migrate, je tombe sur cette erreur:

    Illuminate\Database\QueryException

    SQLSTATE[HY000] [2002] Aucune connexion n’a pu être établie car l’ordinateur cible l’a expressément refusée (SQL: select * from information_schema.tables where table_schema = laravel and table_name = migrations and table_type = ‘BASE TABLE’)

    at C:\wamp64\www\monblog\vendor\laravel\framework\src\Illuminate\Database\Connection.php:712
    708▕ // If an exception occurs when attempting to run a query, we’ll format the error
    709▕ // message to include the bindings with SQL, which will make this exception a
    710▕ // lot more helpful to the developer instead of just the database’s errors.
    711▕ catch (Exception $e) {
    ➜ 712▕ throw new QueryException(
    713▕ $query, $this->prepareBindings($bindings), $e
    714▕ );
    715▕ }
    716▕ }

    1 C:\wamp64\www\monblog\vendor\laravel\framework\src\Illuminate\Database\Connectors\Connector.php:70
    PDOException::(« SQLSTATE[HY000] [2002] Aucune connexion n’a pu être établie car l’ordinateur cible l’a expressément refusée »)

    2 C:\wamp64\www\monblog\vendor\laravel\framework\src\Illuminate\Database\Connectors\Connector.php:70
    PDO::__construct(« mysql:host=127.0.0.1;port=3306;dbname=laravel », « root », «  », [])

  • devotion1

    bonjour coach, je reviens pour solliciter votre aide une fois de plus,
    j’ai suivis avec detaille les etapes et j’ai cette erreur après avoir taper :

    php artisan db:seed

    Illuminate\Database\QueryException

    SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry ‘Category 1’ for key ‘categories_title_unique’ (SQL: insert into `categories` (`slug`, `title`) values (category-1, Category 1), (category-2, Category 2), (category-3, Category 3))

    at C:\laragon\www\objets\vendor\laravel\framework\src\Illuminate\Database\Connection.php:703
    699▕ // If an exception occurs when attempting to run a query, we’ll format the error
    700▕ // message to include the bindings with SQL, which will make this exception a
    701▕ // lot more helpful to the developer instead of just the database’s errors.
    702▕ catch (Exception $e) {
    ➜ 703▕ throw new QueryException(
    704▕ $query, $this->prepareBindings($bindings), $e
    705▕ );
    706▕ }
    707▕ }

    1 C:\laragon\www\objets\vendor\laravel\framework\src\Illuminate\Database\Connection.php:492
    PDOException::(« SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry ‘Category 1’ for key ‘categories_title_unique' »)

    2 C:\laragon\www\objets\vendor\laravel\framework\src\Illuminate\Database\Connection.php:492
    PDOStatement::execute()

      • devotion1

        bonjour chef, voici la reponse lorque le tape

        λ php artisan migrate:fresh –seed
        Dropped all tables successfully.
        Migration table created successfully.
        Migrating: 2014_10_12_000000_create_users_table
        Migrated: 2014_10_12_000000_create_users_table (547.73ms)
        Migrating: 2014_10_12_100000_create_password_resets_table
        Migrated: 2014_10_12_100000_create_password_resets_table (517.68ms)
        Migrating: 2019_08_19_000000_create_failed_jobs_table
        Migrated: 2019_08_19_000000_create_failed_jobs_table (479.09ms)
        Migrating: 2019_12_14_000001_create_personal_access_tokens_table
        Migrated: 2019_12_14_000001_create_personal_access_tokens_table (744.92ms)
        Migrating: 2021_10_27_102135_create_notifications_table
        Migrated: 2021_10_27_102135_create_notifications_table (978.47ms)
        Migrating: 2021_10_27_105211_create_categories_table
        Migrated: 2021_10_27_105211_create_categories_table (747.38ms)
        Migrating: 2021_10_27_144539_create_tags_table
        Migrated: 2021_10_27_144539_create_tags_table (624.37ms)
        Migrating: 2021_10_27_145251_create_posts_table
        Migrated: 2021_10_27_145251_create_posts_table (1,166.64ms)
        Migrating: 2021_10_27_161252_category_post_table
        Migrated: 2021_10_27_161252_category_post_table (4,385.08ms)
        Migrating: 2021_10_27_164406_post_tag_table
        Migrated: 2021_10_27_164406_post_tag_table (2,018.66ms)
        Migrating: 2021_10_27_165859_create_commets_table
        Migrated: 2021_10_27_165859_create_commets_table (1,963.57ms)

        Illuminate\Database\QueryException

        SQLSTATE[42S22]: Column not found: 1054 Unknown column ‘updated_at’ in ‘field list’ (SQL: insert into `posts` (`meta_descr
        iption`, `meta_keywords`, `excerpt`, `body`, `active`, `title`, `slug`, `seo_title`, `user_id`, `image`, `updated_at`, `crea
        ted_at`) values (Sunt autem illum dignissimos dolores voluptas repellat molestias. Iusto quis voluptas dicta nulla beatae ex
        quasi. Mollitia error voluptates consequatur sunt. Necessitatibus in magnam animi ut. Facilis sit beatae excepturi velit. E
        os incidunt velit iste facere., nisi,consequatur,sit, Iste maxime nam optio ut accusantium magni. Eveniet architecto possimu
        s quo. Non quia ut omnis magnam. Id est iure eos autem ut est vero. Non blanditiis vero nemo qui., Aut expedita nulla suscip
        it. Aut et voluptatem omnis consequuntur. Nihil est voluptatem aut porro. Voluptatem sunt distinctio quia. Earum officia quo
        s corrupti dignissimos labore dolorum ratione., 1, Post 1, post-1, Post 1, 2, img01.jpg, 2021-12-22 09:20:08, 2021-12-22 09:
        20:08))

        at C:\laragon\www\objets\vendor\laravel\framework\src\Illuminate\Database\Connection.php:703
        699▕ // If an exception occurs when attempting to run a query, we’ll format the error
        700▕ // message to include the bindings with SQL, which will make this exception a
        701▕ // lot more helpful to the developer instead of just the database’s errors.
        702▕ catch (Exception $e) {
        ➜ 703▕ throw new QueryException(
        704▕ $query, $this->prepareBindings($bindings), $e
        705▕ );
        706▕ }
        707▕ }

        • A column was not found: You might have forgotten to run your migrations. You can run your migrations using `php artisan
        migrate`.
        https://laravel.com/docs/master/migrations#running-migrations

        1 C:\laragon\www\objets\vendor\laravel\framework\src\Illuminate\Database\Connection.php:486
        PDOException::(« SQLSTATE[42S22]: Column not found: 1054 Unknown column ‘updated_at’ in ‘field list' »)

        2 C:\laragon\www\objets\vendor\laravel\framework\src\Illuminate\Database\Connection.php:486
        PDO::prepare(« insert into `posts` (`meta_description`, `meta_keywords`, `excerpt`, `body`, `active`, `title`, `slug`,
        `seo_title`, `user_id`, `image`, `updated_at`, `created_at`) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) »)

  • devotion1

    Salut, je suis un apprenti au développement web.
    j’ai suivi à la lettre les étapes mais j’ai eu une erreur.
    je besoin de votre expertise

    Illuminate\Database\QueryException

    SQLSTATE[HY000] [2002] Aucune connexion n’a pu être établie car l’ordinateur ci
    le l’a expressément refusée (SQL: SHOW FULL TABLES WHERE table_type = ‘BASE TABL
    ‘)

    at C:\xampp\htdocs\freecod\projets\tuto\blog\vendor\laravel\framework\src\Illum
    nate\Database\Connection.php:692
    688▕ // If an exception occurs when attempting to run a query, we’ll
    ormat the error
    689▕ // message to include the bindings with SQL, which will make thi
    exception a
    690▕ // lot more helpful to the developer instead of just the databas
    ‘s errors.
    691▕ catch (Exception $e) {
    ➜ 692▕ throw new QueryException(
    693▕ $query, $this->prepareBindings($bindings), $e
    694▕ );
    695▕ }
    696▕ }

    1 C:\xampp\htdocs\freecod\projets\tuto\blog\vendor\laravel\framework\src\Illu
    inate\Database\Connectors\Connector.php:70
    PDOException::(« SQLSTATE[HY000] [2002] Aucune connexion n’a pu être établie
    car l’ordinateur cible l’a expressément refusée »)

    2 C:\xampp\htdocs\freecod\projets\tuto\blog\vendor\laravel\framework\src\Illu
    inate\Database\Connectors\Connector.php:70
    PDO::__construct(« mysql:host=127.0.0.1;port=3306;dbname=laravel », « root »,  »
    , [])

      • devotion1

        voici le fichier .env

        APP_NAME=Laravel
        APP_ENV=local
        APP_KEY=base64:MOgspTew72U835sbv/scUxfmbi8sYjGcbSiif95U4uE=
        APP_DEBUG=true
        APP_URL=http://localhost

        LOG_CHANNEL=stack
        LOG_DEPRECATIONS_CHANNEL=null
        LOG_LEVEL=debug

        DB_CONNECTION=mysql
        DB_HOST=127.0.0.1
        DB_PORT=3306
        DB_DATABASE=laravel
        DB_USERNAME=root
        DB_PASSWORD=

        BROADCAST_DRIVER=log
        CACHE_DRIVER=file
        FILESYSTEM_DRIVER=local
        QUEUE_CONNECTION=sync
        SESSION_DRIVER=file
        SESSION_LIFETIME=120

        MEMCACHED_HOST=127.0.0.1

        REDIS_HOST=127.0.0.1
        REDIS_PASSWORD=null
        REDIS_PORT=6379

        MAIL_MAILER=smtp
        MAIL_HOST=mailhog
        MAIL_PORT=1025
        MAIL_USERNAME=null
        MAIL_PASSWORD=null
        MAIL_ENCRYPTION=null
        MAIL_FROM_ADDRESS=null
        MAIL_FROM_NAME= »${APP_NAME} »

        AWS_ACCESS_KEY_ID=
        AWS_SECRET_ACCESS_KEY=
        AWS_DEFAULT_REGION=us-east-1
        AWS_BUCKET=
        AWS_USE_PATH_STYLE_ENDPOINT=false

        PUSHER_APP_ID=
        PUSHER_APP_KEY=
        PUSHER_APP_SECRET=
        PUSHER_APP_CLUSTER=mt1

  • Fiston

    Bonjour,

    Merci beaucoup pour ce site qui m’aide beaucoup dans mon apprentissage !!

    Je rencontre un problème que je ne parviens pas à résoudre, peut être pourras-tu m’aider..

    Lorsque je lance la migration j’ai un message d’erreur, j’ai pourtant tout double checké et en desespoir de cause j’ai même copier coller tes fichiers pensant que j’avais fait une boulette mais rien n’y fait.

    Voici l’erreur retournée:
    PHP Fatal error: Cannot declare class CreateNotificationsTable, because the name is already in use in C:\laragon\www\monblog\database\migrations\2021_08_10_030312_create_notifications_table.php on line 7

    Symfony\Component\ErrorHandler\Error\FatalError

    Cannot declare class CreateNotificationsTable, because the name is already in use

    Merci d’avance pour ton aide 🙂

  • khadidja

    Bonjour,
    j’ai suivit les étapes jusqu’à la migration, j’ai rencontré cette erreur : La table ‘posts’ existe déjà, veuillez m’aider ou se trouve l’erreur . plus de détaill :
    SQLSTATE[42S01]: Base table or view already exists: 1050 La table ‘posts’ existe déjà (SQL: create table `posts` (`id` bigint unsigned not null auto_increment primary key, `created_at` timestamp null, `updated_at` timestamp
    null, `title` varchar(255) not null, `slug` varchar(255) not null, `ceo-title` varchar(255) null, `excerpt` varchar(255) not null, `body` varchar(255) not null, `meta-discription` text not null, `meta-keywords` text not null,
    `active` tinyint(1) not null default ‘0’, `image` varchar(255) null, `user_id` bigint unsigned not null) default
    character set utf8mb4 collate ‘utf8mb4_unicode_ci’)

    at C:\wamp\www\Courses\monblog\vendor\laravel\framework\src\Illuminate\Database\Connection.php:692
    688▕ // If an exception occurs when attempting to run a query, we’ll format the error
    689▕ // message to include the bindings with SQL, which will make this exception a
    690▕ // lot more helpful to the developer instead of just the database’s errors.
    691▕ catch (Exception $e) {
    ➜ 692▕ throw new QueryException(
    693▕ $query, $this->prepareBindings($bindings), $e
    694▕ );
    695▕ }
    696▕

    1 C:\wamp\www\Courses\monblog\vendor\laravel\framework\src\Illuminate\Database\Connection.php:485
    PDOException::(« SQLSTATE[42S01]: Base table or view already exists: 1050 La table ‘posts’ existe déjà »)

    2 C:\wamp\www\Courses\monblog\vendor\laravel\framework\src\Illuminate\Database\Connection.php:485
    PDOStatement::execute()

  • fifi

    Bonjour Bestmomo et merci pour ce tutoriel,
    J’ai un problème avec la migration ( J’ai ajouté le mot de passe: DB_PASSWORD=motdepasse)
    PS C:\laragon\www\monblog> php artisan migrate

    Illuminate\Database\QueryException

    SQLSTATE[HY000] [1045] Access denied for user ‘root’@’localhost’ (using password: YES) (SQL: select * from information_schema.tables where table_schema = monblog and table_name = migrations and table_type = ‘BASE TABLE’)

    at C:\laragon\www\monblog\vendor\laravel\framework\src\Illuminate\Database\Connection.php:678
    674▕ // If an exception occurs when attempting to run a query, we’ll format the error
    675▕ // message to include the bindings with SQL, which will make this exception a
    676▕ // lot more helpful to the developer instead of just the database’s errors.
    677▕ catch (Exception $e) {
    ➜ 678▕ throw new QueryException(
    679▕ $query, $this->prepareBindings($bindings), $e
    680▕ );
    681▕ }
    682▕

    1 C:\laragon\www\monblog\vendor\laravel\framework\src\Illuminate\Database\Connectors\Connector.php:70
    PDOException::(« SQLSTATE[HY000] [1045] Access denied for user ‘root’@’localhost’ (using password: YES) »)

    2 C:\laragon\www\monblog\vendor\laravel\framework\src\Illuminate\Database\Connectors\Connector.php:70
    PDO::__construct(« mysql:host=127.0.0.1;port=3306;dbname=monblog », « root », « motdepasse », [])

  • DaniBlou

    Bonjour

    Merci deja pour cette formation quentu nous offre et pour ta disponibilite

    en fait j’ai un soucis quand je veux installer laravel Breeze

    j’ai cette erreur

    PHP Warning: require(/home/daniel/BlogPro/vendor/autoload.php): Failed to open stream: No such file or directory in /home/daniel/BlogPro/artisan on line 18
    PHP Fatal error: Uncaught Error: Failed opening required ‘/home/daniel/BlogPro/vendor/autoload.php’ (include_path=’.:/usr/share/php’) in /home/daniel/BlogPro/artisan:18
    Stack trace:

    j’ai essaiye tout ce dont j’ai vu mais pas de solution. je compte sur toi pour m’aider
    merci d’avance

  • Junior Ndono Jb

    bonjour Best MOmo et merci pour ce tutoriel. Pour ma part j’ai un petit problème avec la commande **composer require kalnoy/nestedset** elle m’affiche une erreur du style
    ** [InvalidArgumentException]
    Could not find package kalnoy/nestedset.

    Did you mean one of these?
    kalnoy/nestedset
    nerio/mongo-nestedset**
    S’il vous plait j’ai besoin d’aide.

  • Michel

    Bonjour,

    Pour mon travail, j’ai besoin de construire un blog, cependant je suis un amateur aussi je vous remercie pour votre titoriel que je trouve passionnant et j’espère y arriver.

    Arrivé à ce point, je rencontre un message d’erreur:
    C:\laragon\www\monblog
    λ php artisan db:seed

    Illuminate\Database\QueryException

    SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry ‘Category 1’ for key ‘categories_title_unique’ (SQL: insert into `categories` (`slug`, `title`) values (category-1, Category 1), (category-2, Category 2), (category-3, Category 3))

    at C:\laragon\www\monblog\vendor\laravel\framework\src\Illuminate\Database\Connection.php:678
    674▕ // If an exception occurs when attempting to run a query, we’ll format the error
    675▕ // message to include the bindings with SQL, which will make this exception a
    676▕ // lot more helpful to the developer instead of just the database’s errors.
    677▕ catch (Exception $e) {
    ➜ 678▕ throw new QueryException(
    679▕ $query, $this->prepareBindings($bindings), $e
    680▕ );
    681▕ }
    682▕

    1 C:\laragon\www\monblog\vendor\laravel\framework\src\Illuminate\Database\Connection.php:471
    PDOException::(« SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry ‘Category 1’ for key ‘categories_title_unique' »)

    2 C:\laragon\www\monblog\vendor\laravel\framework\src\Illuminate\Database\Connection.php:471
    PDOStatement::execute()

    Dans mes tables j’ai les utilisateurs et les 3 catégories; les tables tag, post, comment sont vides…

    Quand je me connecte, http://monblog.test/login. J’arrive à l’identification, j’ai choisi l’utilisateur id 2 et là j’arrive au dashbord de Laravel.

    Que dois je faire, svp ?

    Encore merci pour votre aide.

    • bestmomo

      Bonjour,

      Comme la population (seeder) impose des enregistrements, une fois que la base a été remplie de données si on veut refaire une population il faut rafraichir la base :
      php artisan migrate:fresh --seed
      En ce qui concerne la page qui s’affiche à l’issue du login pour le moment c’est la dashboard de Laravel, ça sera changé quand l’authentification adaptée au blog sera mise en place.

  • qanielutis

    Bonjour et merci pour tes formations ca m’aide enormement.
    J’ai un probleme au niveau UTILISATEURS (On va créer un administrateur, 2 rédacteurs et 3 utilisateurs de base 🙂
    J’aimerai savoir dans quel dossier(fichier) on insere les code? Je suis un peu embrouille a ce niveau.

      • webwatson

        Je l’avais mis vu que j’ai suivi toute les étapes.

        *Post.php*

        namespace App\Models;
        use Illuminate\Database\Eloquent\Model;
        use App\Events\ModelCreated;
        use Illuminate\Database\Eloquent\Factories\HasFactory;
        use Illuminate\Notifications\Notifiable;

        class Post extends Model
        {
        use HasFactory, Notifiable;

        *Comment.php*

        namespace App\Models;

        use Illuminate\Database\Eloquent\Model;
        use Kalnoy\Nestedset\NodeTrait;
        use Illuminate\Database\Eloquent\Factories\HasFactory;
        use Illuminate\Notifications\Notifiable;

        class Comment extends Model
        {
        use NodeTrait, HasFactory, Notifiable;

  • gil

    Hello,
    J’avais vu, mais je m’étais dit que c’était fait exprès pour que les lecteurs réfléchissent 🙂

    Non je blague, mais il est vrai que c’est un peu le problème des tutoriaux, on a tendance à ne faire que du copier-coller.
    Une idée d’ailleurs pourrait être de présenter des tutos en ayant en certains endroits des choses expendables-collapsable, cachées par défaut. Pas partout, mais parfois de dire « faites ceci », ou « créer ceci en en ajoutant 2 champs, un label ‘title’ et un booléen ‘valid’ « .
    (maintenant, c’est juste une idée)

    Et du coup (même si ça revient plutôt bien), je vais faire tes anciens tuto laravel 7 et , qui m’obligeront à réfléchir !

  • eric.lecathelinais@gmail.com

    Salut,

    Déjà merci pour ton blog sur laravel dans lequel j’apprends plein de chose

    Sinon juste une petite correction sur cette page
    php artisan migrate au lieu de php artisan:migrate

    Continue comme ça, c’est top 😉

Laisser un commentaire