是否有可能动态设置模型之间的关系?例如,我有一个名为Page
的模型,我想在不实际更改其文件的情况下添加关系banners()
。那么是否存在类似以下方式:
Page::createRelationship('banners', function(){
$this->hasMany('banners');
});
或者类似的东西?由于它们已经使用魔术方法获取,也许我可以动态添加关系?谢谢!
Model::resolveRelationUsing()
来实现此功能。
我已经为此添加了一个包i-rocky/eloquent-dynamic-relation
如果有人仍在寻找解决方案,这里有一个。如果你认为这是个不好的主意,请告诉我。
trait HasDynamicRelation
{
/**
* Store the relations
*
* @var array
*/
private static $dynamic_relations = [];
/**
* Add a new relation
*
* @param $name
* @param $closure
*/
public static function addDynamicRelation($name, $closure)
{
static::$dynamic_relations[$name] = $closure;
}
/**
* Determine if a relation exists in dynamic relationships list
*
* @param $name
*
* @return bool
*/
public static function hasDynamicRelation($name)
{
return array_key_exists($name, static::$dynamic_relations);
}
/**
* If the key exists in relations then
* return call to relation or else
* return the call to the parent
*
* @param $name
*
* @return mixed
*/
public function __get($name)
{
if (static::hasDynamicRelation($name)) {
// check the cache first
if ($this->relationLoaded($name)) {
return $this->relations[$name];
}
// load the relationship
return $this->getRelationshipFromMethod($name);
}
return parent::__get($name);
}
/**
* If the method exists in relations then
* return the relation or else
* return the call to the parent
*
* @param $name
* @param $arguments
*
* @return mixed
*/
public function __call($name, $arguments)
{
if (static::hasDynamicRelation($name)) {
return call_user_func(static::$dynamic_relations[$name], $this);
}
return parent::__call($name, $arguments);
}
}
class MyModel extends Model {
use HasDynamicRelation;
}
MyModel::addDynamicRelation('some_relation', function(MyModel $model) {
return $model->hasMany(SomeRelatedModel::class);
});
MyModel
,因为它位于外部,这就是为什么我认为最好的方法是在包中创建一个trait并在您的模型中使用它。 - Amir Hassan Azimi从 Laravel 7 开始,动态关联被官方支持。您可以使用 Model::resolveRelationUsing() 方法。
https://laravel.com/docs/7.x/eloquent-relationships#dynamic-relationships
您可以使用宏
调用来处理您的动态关系,例如:
您应该在服务提供者的启动方法中编写此代码。
\Illuminate\Database\Eloquent\Builder::macro('yourRelation', function () {
return $this->getModel()->belongsTo('class');
});
Model::first()->yourRelation
,您必须改用 Model::first()->yourRelation()->get()
。 - Juan Rangel注意,对于每种关系,您都有一个底层模型(所有模型可以在这里找到),这实际上就是您在关系方法中返回的内容:
public function hasMany($related, $foreignKey = null, $localKey = null)
{
$foreignKey = $foreignKey ?: $this->getForeignKey();
$instance = new $related;
$localKey = $localKey ?: $this->getKeyName();
return new HasMany($instance->newQuery(), $this, $instance->getTable().'.'.$foreignKey, $localKey);
}
因此,您需要在模型中定义一个方法,该方法接受关系类型和模型,创建一个新的HasMany实例(如果hasMany是所需的关系),然后返回它。
这有点复杂,因此您可以使用:
简单方式
您可以创建一个中间模型(即PageRelationship),用于存储页面与其他模型之间的所有关系。可能的表结构如下:
+-------------+---------+------------------+-------------+
| relation_id | page_id | foreign_model_id | model_class |
+-------------+---------+------------------+-------------+
| 1 | 2 | 225 | Banner |
| 2 | 2 | 223 | Banner |
| 3 | 2 | 12 | Button |
+-------------+---------+------------------+-------------+
如果有人正在寻找 Laravel 8 的答案:
假设我在模型的一个方法中定义了我的关系:
public function relationships()
{
return [
'user' => $this->belongsTo(User::class, 'user_id'),
];
}
resolveRelationUsing
方法。我通过迭代模型文件夹并检查包含上述方法的所有模型来完成此操作: foreach ((new Filesystem)->allFiles(app_path('Models')) as $file) {
$namespace = 'App\\Models\\' . str_replace(['/', '.php'], ['\\', ''], $file->getRelativePathname());
$class = app($namespace);
if (method_exists($class, 'relationships')) {
foreach ($class->relationships() as $key => $relationship) {
$class->resolveRelationUsing($key, function () use ($class, $key) {
return $class->relationships()[$key];
});
}
}
}
Model
类上有一个setRelation
方法,但它不是静态的,只能在实例上调用。是否有类似的方法可以自动设置所有创建的实例之间的关系? - Giedrius