我的个人博客应用最重要的两个部分是:博客文章和项目,前面已经实现了文章列表和文章详情的展示,现在来实现项目相关的功能,包括项目和里程碑。

数据库表设计

一个项目会有多个里程碑,所以项目和里程碑的关系是一对多的关系,需要设计两个数据表:

  1. projects表,项目信息表,对应的数据模型 Project
  2. milestones表,项目的里程碑信息表,对应的数据模型 Milestone

    字段设计

    接下来需要整理好 projects表和 Milestones表的字段名称和类型。

projects表:

字段名称 描述 类型 加索引缘由
id 自增 ID unsigned big int 主键
name 项目名称 varchar
description 项目描述 text
cover 项目封面 varchar

milestones表:

字段名称 描述 类型 加索引缘由
id 自增 ID unsigned big int 主键
title 标题 varchar
content 里程碑内容 text
project_id 项目 ID unsigned big int 外键

创建模型

执行如下命令创建项目的模型、迁移文件和数据工厂:

  1. php artisan make:model Project -mf

执行如下命令创建里程碑的模型、迁移文件和数据工厂:

php artisan make:model Milestone -mf

根据前面整理好的字段修改数据库迁移文件:
{timestamp}_create_projects_table.php

.
.
.  
public function up()
{
    Schema::create('projects', function (Blueprint $table) {
        $table->id();
        $table->string('name');
        $table->text('description');
        $table->string('cover');
        $table->timestamps();
    });
}
.
.
.

{timestamp}_create_milestones_table.php

.
.
.  
public function up()
{
    Schema::create('projects', function (Blueprint $table) {
        $table->id();
        $table->string('title');
        $table->text('content');
        $table->unsignedBigInteger('project_id');
        $table->timestamps();
    });
}
.
.
.

一对多

在创建里程碑的时候,里程碑必须对应一个项目,而该项目可以拥有多个里程碑,这便是数据模型关联中经常谈到的一对多关系。

Eloquent 模型让关联的管理和处理变得更加简单,可在模型中将 Eloquent 关联定义为函数,如下,我可在里程碑模型中,指明一个里程碑属于一个项目。

app/Models/MileStone.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Milestone extends Model
{
    use HasFactory;

    public function project(){
        return $this->belongsTo(Project::class);
    }
}

在项目模型中,指明一个项目拥有多个里程碑。

app/Models/Project.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Project extends Model
{
    use HasFactory;

    public function milestones(){
        return $this->hasMany(Milestone::class);
    }
}

填充数据

前面我们已经创建了项目和里程碑的数据工厂文件,现在分别修改:

database/factories/ProjectFactory.php

<?php

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;


class ProjectFactory extends Factory
{
    public function definition()
    {
        return [
            'name'=>$this->faker->sentence(),
            'description'=>$this->faker->text(),
            'cover'=>$this->faker->imageUrl()
        ];
    }
}

database/factories/MilestoneFactory.php

<?php

namespace Database\Factories;

use App\Models\Project;
use Illuminate\Database\Eloquent\Factories\Factory;

class MilestoneFactory extends Factory
{

    public function definition()
    {
        $project_ids = Project::pluck('id')->toArray();
        return [
            'project_id'=>$this->faker->randomElement($project_ids),
            'title'=>$this->faker->sentence(),
            'content'=>$this->faker->text()
        ];
    }
}

上面的代码中,使用 pluck方法取出所有项目的 id 字段,使用 toArray方法转换为一个数组赋值给 $projectIds变量。 project_id字段的值使用 randomElement方法随机从$projectIds数组取出一个。

创建一个 ProjectsSeeder 文件来对项目假数据进行批量生成:

php artisan make:seeder ProjectsSeeder

生成 20 条假数据:

database/seeders/ProjectsSeeder.php

<?php

namespace Database\Seeders;

use App\Models\Project;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;

class ProjectSeeder extends Seeder
{

    public function run()
    {
        Project::factory()->count(20)->create();
    }
}

创建一个 MileStonesSeeder 文件来对里程碑假数据进行批量生成:

php artisan make:seeder MilestonesSeeder

生成 100 条假数据:

database/seeders/ProjectsSeeder.php

<?php

namespace Database\Seeders;

use App\Models\Milestone;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;

class MilestonesSeeder extends Seeder
{

    public function run()
    {
        Milestone::factory()->count(100)->create();
    }
}

接下来需要在 DatabaseSeeder 类中指定调用项目和里程碑数据填充文件。

database/seeders/DatabaseSeeder.php

<?php

namespace Database\Seeders;

use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;

class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        $this->call(ColumnsSeeder::class);
        $this->call(ArticlesSeeder::class);
        $this->call(ProjectsSeeder::class);
        $this->call(MilestonesSeeder::class);
    }
}

最后让对数据库进行重置和填充。

php artisan migrate:refresh --seed