如何在LaravelEloquent中创建一对多关系

来自菜鸟教程
跳转至:导航、​搜索

作为本系列的先决条件设置的演示 Laravel 应用程序包含一个用于存储链接的数据库表。 在本教程中,您将修改此初始数据库结构以包含第二个表,您将使用该表将链接组织到 列表 中。

对于我们将在本系列中使用的链接和列表示例,每个链接只是一个列表的一部分,但每个列表可以有多个链接。 这种关系也称为一对多关系。

当一个项目(我们将其称为类型 A)可以链接到多个类型为 B 的项目时,就会发生一对多关系,但相反的情况不成立: B 类型的项目只能链接到 一个 类型的 A 项目。 将此场景转换为当前的演示应用模型,Alist类型,Blink类型。

创建 LinkList 模型

首先,您需要创建一个模型和一个数据库表来表示链接的 List。 然后,您将更新现有的 Link 模型和表以包含两个模型之间的关系。 因为术语 List 是为 PHP 内部保留的,所以您将无法使用该术语命名您的新模型。 您可以将此新模型称为 LinkList

首先,确保您在应用程序目录中:

cd ~/landing-laravel

使用 artisan 创建一个新模型:

docker-compose exec app php artisan make:model LinkList

这将在 app/Model 目录中生成一个新的模型类:

app/Model/LinkList.php

重命名现有的 LinkList CLI 命令

如果您查看 app/Console/Commands 目录,您会注意到已经有一个名为 LinkList.php 的类文件。 不要将这与您刚刚创建的 Eloquent 模型相混淆。 此类包含一个 CLI 命令,该命令通过 artisan 列出数据库中的所有链接。

为避免将来出现混淆,现在是将该类及其命令签名重命名为不同名称的好时机。 在这种情况下,请使用类名 LinkShow,因为该名称也描述了类的作用。 要将 app/Console/Commands/LinkList.php 文件重命名为另一个有效名称,请在终端中运行以下命令:

mv app/Console/Commands/LinkList.php app/Console/Commands/LinkShow.php

然后,在代码编辑器中打开文件 app/Console/Commands/LinkShow.php 将类名从 LinkList 更改为 LinkShow,并将命令签名从 link:list 更改为 link:show,如以下代码清单中突出显示的行。 这是完成后文件的外观:

应用程序/控制台/命令/LinkShow.php

<?php
 
namespace App\Console\Commands;
 
use App\Models\Link;
use Illuminate\Console\Command;
 
class LinkShow extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'link:show';
 
    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'List links saved in the database';
 
    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }
 
    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        $headers = [ 'id', 'url', 'description' ];
        $links = Link::all(['id', 'url', 'description'])->toArray();
        $this->table($headers, $links);
 
        return 0;
    }
}

完成后保存并关闭文件。 要检查一切是否按预期工作,请运行新重命名的 link:show artisan 命令:

docker-compose exec app php artisan link:show

您将收到如下输出:

Output+----+-------------------------------------------------+----------------------------------+
| id | url                                             | description                      |
+----+-------------------------------------------------+----------------------------------+
| 1  | https://digitalocean.com/community              | DigitalOcean Community           |
| 2  | https://digitalocean.com/community/tags/laravel | Laravel Tutorias at DigitalOcean |
| 3  | https://digitalocean.com/community/tags/php     | PHP Tutorials at DigitalOcean    |
+----+-------------------------------------------------+----------------------------------+

为 LinkList 模型创建迁移

您使用前面的 artisan make:model 命令生成的新 app/Model/LinkList.php 类包含新 Eloquent 类的通用代码。 与 Doctrine 等其他 ORM 不同,Eloquent 不会改变数据库结构,只处理数据本身。 Eloquent 模型通常是精益的,类属性自动从模型的表结构中推断出来。

这种使用 Eloquent 仅处理数据的方法意味着您不需要为 LinkList 类设置任何属性,因为它们将从该模型的数据库表结构中推断出来。

结构数据库操作通常通过 数据库迁移 在 Laravel 中处理。 迁移允许开发人员以编程方式定义对数据库的结构更改,例如创建、修改和删除表。

您现在将创建一个新迁移以在数据库中设置 lists 表。

Laravel 默认包含的 artisan 命令行工具包含几个辅助方法来引导新组件,例如控制器、模型、迁移等。 要使用 artisan 创建新的迁移,请运行:

docker-compose exec app php artisan make:migration create_link_lists_table
Output Created Migration: 2021_07_07_152554_create_link_lists_table

此命令将在 Laravel 应用程序的 database/migrations 目录下生成一个新文件,使用基于当前日期和时间的自动生成名称以及迁移名称。 该文件包含您将修改以设置 lists 表的通用代码。

使用您的代码编辑器,打开生成的迁移文件。 该文件当前如下所示:

数据库/迁移/2021_07_07_152554_create_link_lists_table.php

<?php

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

class CreateLinkListsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('link_lists', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
        });
    }

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

当使用 artisan migrate 命令执行迁移时,将运行 up() 方法。 这是您的表定义所在,默认情况下,它创建一个 id 主键字段和两个时间戳字段(created_atupdated_at),用 [ 定义X172X] 模式方法。 这些字段分别在创建和更新模型时由 Eloquent 自动填充。 down() 方法在使用 artisan rollback 命令回滚迁移时调用,通常执行代码以删除表或恢复结构更改。

您将更改 up 方法以包含以下字段:

  • title:表示此列表标题的字符串
  • description:表示List描述的字符串
  • slug:基于标题的唯一短字符串,通常用于创建用户友好的 URL

在一对多关系中,many 端(在这种情况下对应于 links 表)是保存列引用(或外键)到其他元素(对应于 lists 表)。 这意味着您必须稍后修改 links 表,以便包含一个将该表链接到 lists 表的引用字段。

另一方面,lists 表不需要任何特殊字段来引用其链接。

将迁移文件中的当前内容替换为以下代码:

数据库/迁移/2021_07_07_152554_create_link_lists_table.php

<?php

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

class CreateLinkListsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('link_lists', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
            $table->string('title', 60);
            $table->string('slug', 60)->unique();
            $table->text('description')->nullable();
        });
    }

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

完成后保存文件。

更新链接迁移

接下来,在代码编辑器中打开现有的 links 迁移文件。 在演示项目中,您将在以下路径中找到迁移:

2020_11_18_165241_create_links_table.php

首先,在文件开头和最后 use 行之后包含一个 use 指令,指向 LinkList 类的完全限定类名:

…
use Illuminate\Support\Facades\Schema;
use App\Models\LinkList;
...

接下来,在表定义中包含以下行,在 up 方法中和设置 description 字段的行之后:

$table->text('description');
$table->foreignIdFor(LinkList::class);

foreignIdFor() 方法为引用的 Eloquent 模型创建一个外键列。 它使用默认命名法来设置链接到引用表的主键字段的字段。

完成后,完整的迁移类应该是这样的:

数据库/迁移/2020_11_18_165241_create_links_table.php

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use App\Models\LinkList;

class CreateLinksTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('links', function (Blueprint $table) {
            $table->id();
            $table->string('url', 200);
            $table->text('description');
            $table->foreignIdFor(LinkList::class);
            $table->timestamps();
        });
    }

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

完成编辑后保存文件。 接下来,擦除数据库,然后再次运行迁移命令以使用更新的迁移文件重新创建数据库结构:

docker-compose exec app php artisan db:wipe
docker-compose exec app php artisan migrate

配置 Eloquent 模型关系

数据库表现在已经设置好了,但是你仍然需要配置 Eloquent 模型来定义它们之间的关系。

List 模型(即关系的 one 一侧)上,您将设置一个名为 links 的新方法。 此方法将作为代理访问与每个列表相关的链接,使用父 Illuminate\Database\Eloquent\Model 类中的 hasMany 方法。

在您的代码编辑器中,打开文件 app/Model/LinkList.php。 将当前通用代码替换为以下内容:

应用程序/模型/LinkList.php

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
 
class LinkList extends Model
{
    use HasFactory;
 
    public function links()
    {
        return $this->hasMany(Link::class);
    }
}
 

完成后保存文件。

接下来,您将编辑关系的 many 端以包含对 List 模型的引用,以便链接可以访问它们各自的列表。 这是通过父 Model 类的 belongsTo 方法完成的。 此方法用于定义一对多关系的反面。

在代码编辑器中打开 Link 模型:

app/Model/Link.php

Link.php 文件中的当前内容替换为以下代码:

应用程序/模型/Link.php

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
 
class Link extends Model
{
    public function link_list()
    {
        return $this->belongsTo(LinkList::class);
    }
}
 

完成后保存文件。

随着两个模型的更新,您的数据库现在已完全配置,但它目前是空的。 在本系列的下一部分中,您将学习如何使用 Eloquent 模型在数据库中插入新记录。