我想使用Laravel Eloquent Polymorphic Relationships但是它似乎没有设置为使用我的表结构。
基本上我有一个gamedata表,其中包括所有不同类型的gamedata(国家,联赛,球队,球员等)。对于每种类型,我有多个表,信息由game_id分隔。因此,gamedata表中的国家“England”会有一行,在NATIONAL表中有7个对应的行,其中包含来自7个不同game_ids的数据。
我希望能够从gamedata表中选择一些行,并根据它的类型从相应的表中选择相应的行。
这对于一对一的关系来说很容易,但似乎不可能与一对多的关系。
这是gamedata表。
CREATE TABLE `gamedata` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`data_type` varchar(16) COLLATE utf8mb4_unicode_ci NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `data_id` (`data_id`,`type`),
FULLTEXT KEY `name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
然后有很多像这样的表(为了便于阅读,删除了大量列):
CREATE TABLE `nations` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`game_id` tinyint(3) unsigned NOT NULL,
`gamedata_id` int(10) unsigned NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`short_name` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
/* more specific columns removed for ease of reading */
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `leagues` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`game_id` tinyint(3) unsigned NOT NULL,
`gamedata_id` int(10) unsigned NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`short_name` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
/* more specific columns removed for ease of reading */
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
CREATE TABLE `teams` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`game_id` tinyint(3) unsigned NOT NULL,
`gamedata_id` int(10) unsigned NOT NULL,
`name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`short_name` varchar(64) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
/* more specific columns removed for ease of reading */
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
所以gamedata表上的某些行可能如下所示:
(144, 'nation', 'Some Nation'),
(145, 'nation', 'Another Nation'),
(146, 'league', 'Some League'),
(147, 'league', 'Another League'),
(148, 'team', 'Some Team'),
(149, 'team', 'Another Team');
因此,我应该能够从“data_type”列和“data_id”列执行多态关系,以从相应的表中获取相应的行。
但内置关系(morphTo,morphMany,morphedByMany)等似乎都无法处理它。
似乎我想要的是morphTo()
关系,但似乎只限于返回一个相关模型。接受多个模型的所有关系都需要定义特定的模型。
// This would work fine if I only wanted one related model. "data_type" being the class and "id" corresponding to "gamedata_id" on relevent table.
$this->morphTo('data');
// These require me to be explicit about the class instantiating rather than using from the "data_type" column
$this->morphMany(???, 'data');
$this->morphToMany(???, 'data');
$this->morphedByMany(???, 'data');
有没有办法使用现有的Laravel关系来做到这一点?或者有一种简单的方法可以根据我的需要创建自己的关系类吗?
答案 0 :(得分:0)
我认为我通过扩展morphTo
类来提出自定义解决方案。
namespace App;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Relations\MorphTo;
/**
* @mixin \Illuminate\Database\Eloquent\Builder
*/
class MorphHasMany extends MorphTo
{
/**
* Get the results of the relationship.
*
* @return mixed
*/
public function getResults()
{
return $this->ownerKey ? $this->query->get() : null;
}
/**
* Match the results for a given type to their parents.
*
* @param string $type
* @param \Illuminate\Database\Eloquent\Collection $results
* @return void
*/
protected function matchToMorphParents($type, Collection $results)
{
$ownerKeyName = $this->ownerKey ?: $results->first()->getKeyName();
foreach ($results->groupBy($ownerKeyName) as $ownerKey => $result) {
if (isset($this->dictionary[$type][$ownerKey])) {
foreach ($this->dictionary[$type][$ownerKey] as $model) {
$model->setRelation($this->relation, $result);
}
}
}
}
}
morphTo()类已经返回结果集合,但是使用first()
或多个setRelation()
实例表示只有一个集合。通过重载getResults()
和matchToMorphParents()
我可以修改此行为以允许设置集合。
为了定义关系,我需要一个自定义的morphHasMany()
方法。这可以添加到扩展Model.php
的基础Eloquent\Model
。
/**
* Define a polymorphic has many relationship.
*
* @param string $name
* @param string $type
* @param string $id
* @param string $ownerKey
* @return MorphHasMany
*/
public function morphHasMany($name = null, $type = null, $id = null, $ownerKey = null)
{
// If no name is provided, we will use the backtrace to get the function name
// since that is most likely the name of the polymorphic interface. We can
// use that to get both the class and foreign key that will be utilized.
$name = $name ?: $this->guessBelongsToRelation();
list($type, $id) = $this->getMorphs(
Str::snake($name), $type, $id
);
// If the type value is null it is probably safe to assume we're eager loading
// the relationship. In this case we'll just pass in a dummy query where we
// need to remove any eager loads that may already be defined on a model.
if (empty($class = $this->{$type})) {
return new MorphHasMany($this->newQuery()->setEagerLoads([]), $this, $id, $ownerKey, $type, $name);
} else {
$instance = $this->newRelatedInstance(
static::getActualClassNameForMorph($class)
);
return new MorphHasMany($instance->newQuery(), $this, $id, $ownerKey ?? $instance->getKeyName(), $type, $name);
}
}
然后只需像通常的morphTo()
方法一样定义它。
public function data()
{
return $this->morphHasMany();
}
或者就我而言:
public function data()
{
return $this->morphHasMany('data', 'data_type', 'id', 'gamedata_id');
}
到目前为止没有任何问题,但当然我将来会遇到一些问题。