如何在LaravelEloquent中创建一对多关系
作为本系列的先决条件设置的演示 Laravel 应用程序包含一个用于存储链接的数据库表。 在本教程中,您将修改此初始数据库结构以包含第二个表,您将使用该表将链接组织到 列表 中。
对于我们将在本系列中使用的链接和列表示例,每个链接只是一个列表的一部分,但每个列表可以有多个链接。 这种关系也称为一对多关系。
当一个项目(我们将其称为类型 A)可以链接到多个类型为 B 的项目时,就会发生一对多关系,但相反的情况不成立: B 类型的项目只能链接到 一个 类型的 A 项目。 将此场景转换为当前的演示应用模型,A为list
类型,B为link
类型。
创建 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_at
和 updated_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 模型在数据库中插入新记录。