什么是多态关系?我们如何善加运用?

This article was peer reviewed by Christopher Vundi and Christopher Pitt. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!

本文由克里斯托弗·冯迪 ( Christopher Vundi)克里斯托弗·皮特 ( Christopher Pitt)进行了同行评审。 感谢所有SitePoint的同行评审人员使SitePoint内容达到最佳状态!



While I was working on an application for a client, I had to implement a new module that entails the following:

在为客户开发应用程序时,我必须实现一个包含以下内容的新模块:

  • Users ask for a budget quotation for a certain task.

    用户要求某项任务的预算报价。
  • Every task has a location.

    每个任务都有一个位置。
  • Professionals can subscribe to different zones.

    专业人士可以订阅不同的区域。
  • A zone can be a region or a city.

    区域可以是地区或城市。

Now, let’s neglect the core application and try to implement this single module to see what we can achieve here.

现在,让我们忽略核心应用程序,并尝试实现此单个模块,以了解我们可以在此处实现的目标。

脚手架应用 (Scaffolding Application)

I assume you have your development environment already set up. If not, you can check this Homestead Improved Quick Tip or just use the official Homestead Box.

我假设您已经设置了开发环境。 如果没有,您可以查看此Homestead改进的快速提示,或仅使用官方的Homestead Box

Go ahead and create a new Laravel project using the Laravel installer or via Composer.

继续使用Laravel安装程序或通过Composer创建一个新的Laravel项目。

laravel new demo

Or

要么

composer create-project --prefer-dist laravel/laravel demo

Edit your .env file with your database credentials.

使用数据库凭据编辑.env文件。

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=homestead
DB_USERNAME=homestead    
DB_PASSWORD=secret

创建迁移 (Creating Migrations)

Before creating our migrations, we should talk about Eloquent polymorphic relations and how they can benefit us in this case! Polymorphism is often used when an object can have different forms (shapes). In our case, professional users subscribe to different zones and they get notified whenever a new job is posted in this area.

在创建迁移之前,我们应该讨论雄辩的多态关系以及在这种情况下它们如何使我们受益! 当对象可以具有不同的形式(形状)时,通常使用多态。 在我们的情况下,专业用户订阅不同的区域,并且每当在该区域发布新作业时,他们都会收到通知。

Lets start by creating the zones table.

让我们从创建zones表开始。

php artisan make:model Zone --migration

This creates a migration file, but we do need to add a bit of code to it before it’s complete, as demonstrated by:

这将创建一个迁移文件,但是我们需要在迁移文件完成之前添加一些代码,如以下所示:

// database/migrations/2016_12_02_130436_create_zones_table.php

class CreateZonesTable extends Migration
{
    public function up()
    {
        Schema::create('zones', function (Blueprint $table) {
            $table->integer('user_id')->unsigned();

            $table->integer('zone_id')->unsigned();
            $table->string('zone_type');
        });
    }

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

Next, we create the cities and regions tables.

接下来,我们创建城市和地区表。

php artisan make:model Region --migration
// database/migrations/2016_12_02_130701_create_regions_table.php

class CreateRegionsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('regions', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
        });

    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::drop('regions');
    }
}
php artisan make:model City --migration
// database/migrations/2016_12_02_130709_create_cities_table.php

class CreateCitiesTable extends Migration
{
    public function up()
    {
        Schema::create('cities', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name', 255);
            $table->integer('postal_code')->unsigned();

            $table->integer('region_id')->unsigned()->nullable();
        });
    }

    public function down()
    {
        Schema::drop('cities');
    }
}

We could’ve achieved the same result by making a many to many relation with the cities and regions table. However, the zones table will act as an abstract class for the two other tables. The zone_id will hold an id for a city or a region and using the zone_type value Eloquent will decide which model instance to return.

通过与城市和地区表建立多对多关系,我们可以达到相同的结果。 但是,zones表将充当其他两个表的抽象类zone_id将保存一个城市或地区的ID, zone_type使用zone_type值将确定要返回的模型实例。

创建模型 (Creating Models)

Eloquent has the ability to guess the related table fields, but I decided not to use it just to explain how to map database fields to models. I will explain them as we go along!

Eloquent可以猜测相关的表字段,但我决定不使用它只是为了解释如何将数据库字段映射到模型。 我将在前进的过程中向他们解释!

// app/User.php

class User extends Authenticatable
{
    // ...

    public function cities()
    {
        return $this->morphedByMany(City::class, 'zone', 'zones', 'user_id', 'zone_id');
    }

    public function regions()
    {
        return $this->morphedByMany(Region::class, 'zone', 'zones', 'user_id', 'zone_id');
    }
}

The morphedByMany method is similar to the manyToMany one. We specify the related model, the mapping field name (used for zone_type and zone_id), related table name, current model foreign key and the morphed relation key. We could’ve automated this by letting Eloquent guess field names, if you take a look at the documentation you’ll see that we can name the fields as zoneable_id and zoneable_type, then only specify the mapped field name (return $this->morphedByMany(City::class, 'zoneable').

morphedByMany方法类似于manyToMany之一。 我们指定相关模型,映射字段名称(用于zone_typezone_id ),相关表名称,当前模型外键和变形关系键。 我们可以通过雄辩地猜测字段名称来实现自动化,如果您看一下文档,您会发现我们可以将字段命名为zoneable_idzoneable_type ,然后仅指定映射的字段名称( return $this->morphedByMany(City::class, 'zoneable' )。

// app/Region.php

class Region extends Model
{
    // ...

    public function cities()
    {
        return $this->hasMany(City::class);
    }

    public function users()
    {
        return $this->morphMany(User::class, 'zones', 'zone_type', 'zone_id');
    }
}

We can quickly guess the parameters definition from the above code. We specify the related table name, morphed relation type and ID.

我们可以从上面的代码中快速猜测参数定义。 我们指定相关的表名,变形的关系类型和ID。

// app/City.php

class City extends Model
{
    // ...

    public function users()
    {
        return $this->morphMany(User::class, 'zones', 'zone_type', 'zone_id');
    }
}

Now that everything is set up, we can start testing our relations and see if everything is working as expected. We should first seed our tables to save some time. You can grab the database seeder’s code from the GitHub repository.

既然一切都准备就绪,我们就可以开始测试我们的关系,看看一切是否按预期进行。 我们应该首先为表添加种子,以节省一些时间。 您可以从GitHub存储库中获取数据库种子程序的代码。

使用关系 (Using Relations)

We can attach cities and regions to users using syncing, attaching, and detaching Eloquent methods. Here’s an example:

我们可以使用同步,附加和分离口才方法将城市和地区附加到用户。 这是一个例子:

$user = App\User::find(1);

$user->cities()->sync(App\City::limit(3)->get());

This will attach three cities to the selected user, and we can do the same for regions.

这会将三个城市附加到所选用户,我们可以对区域执行相同的操作。

$user = App\User::find(1);

$user->regions()->sync(App\Region::limit(3)->get());
Eloquent Polymorphism relations

If we inspect our database now to see what was saved, we can see the following:

如果现在检查数据库以查看保存了什么,则可以看到以下内容:

mysql> select * from zones;
+---------+---------+------------+
| user_id | zone_id | zone_type  |
+---------+---------+------------+
|       1 |       1 | App\City   |
|       1 |       2 | App\City   |
|       1 |       3 | App\City   |
|       1 |       1 | App\Region |
|       1 |       2 | App\Region |
|       1 |       3 | App\Region |
+---------+---------+------------+
6 rows in set (0.00 sec)

We can also detach regions if they exist.

我们还可以分离区域(如果存在)。

$user = App\User::find(1);

$user->regions()->detach(App\Region::limit(3)->get());

Now, we can fetch user cities and regions as we would do on a normal many to many relationship:

现在,我们可以像正常的多对多关系一样获取用户城市和地区:

$user = App\User::find(1);

dd($user->regions, $user->cities);

We could add a new method called zones which will merge cities from regions with individual selected cities.

我们可以添加一种称为zones的新方法,该方法将合并区域中的城市与各个选定的城市。

class User extends Authenticatable
{
    // ...

    public function zones()
    {
        return $this->regions->pluck("cities")->flatten()->merge($this->cities);
    }
}

The pluck method will get a collection of cities from each region, which be then flattened (merge all the collections into one) and merged with the user selected cities. You can read more about collections in the documentation, and if you want to learn more, I recommend this Refactoring to Collections book from Adam Wathan.

采摘方法将获得每个地区的城市集合,然后将其展平(将所有集合合并为一个)并与用户选择的城市合并。 您可以在文档中阅读有关集合的更多信息,如果您想了解更多信息,我建议您阅读 Adam Wathan的这本“重构到集合”书

结论 (Conclusion)

Even though polymorphic relations are rarely used in applications, Eloquent makes it’s easy to deal with related tables and returning the correct data. If you have any questions or comments about Eloquent or Laravel in general, you can post them below and I’ll do my best to answer them!

尽管在应用程序中很少使用多态关系,但Eloquent使得处理相关表和返回正确数据变得容易。 如果您对Eloquent或Laravel总体上有任何疑问或意见,可以将其发布在下面,我会尽力回答他们!

翻译自: https://www.sitepoint.com/what-are-polymorphic-relations-and-how-do-we-use-them-with-eloquent/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值