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_type
和zone_id
),相关表名称,当前模型外键和变形关系键。 我们可以通过雄辩地猜测字段名称来实现自动化,如果您看一下文档,您会发现我们可以将字段命名为zoneable_id
和zoneable_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](https://i-blog.csdnimg.cn/blog_migrate/298d1fd72e3a9ec7d69e70fc4806e849.gif)
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/