我正在构建CMS package for Laravel。
此程序包中的所有models都绑定到IoC容器并从中解析,以便在程序包的任何单独部署中轻松覆盖它们。
对于非多态关系,这很有魅力。
例如,Page有很多PageModules,所以它的关系改变了:
// \Angel\Core\Page
public function modules()
{
return $this->hasMany('PageModule');
}
为:
// \Angel\Core\Page
public function modules()
{
return $this->hasMany(App::make('PageModule'));
}
但我还没有弄清楚如何用多态关系做同样的事情。
例如,菜单包含MenuItems,每个MenuItem可以绑定到一个其他模型,例如Page或BlogPost。
为了实现这个Laravel方式,我已经将以下关系添加到MenuItem:
// \Angel\Core\MenuItem
public function linkable()
{
return $this->morphTo();
}
这与LinkableModel的关系,所有模型如Page和BlogPost都延伸:
// \Angel\Core\LinkableModel
public function menuItem()
{
return $this->morphOne(App::make('MenuItem'), 'linkable');
}
menus_items
表(MenuItems使用的表)包含以下行:
linkable_type | linkable_id
-------------------|--------------
\Angel\Core\Page | 11
\Angel\Core\Page | 4
这很有效,但我需要linkable_type
来说“Page”'而不是' \ Angel \ Core \ Page'而是要从IoC' Page'而不是硬编码到特定的命名空间类。
我尝试过的事情:
根据this question,它应该像为linkable()类定义$morphClass
属性一样简单,如下所示:
// \Angel\Core\Page
protected $morphClass = 'Page';
但是当我应用它时,将menus_items
表更改为如下所示:
linkable_type | linkable_id
---------------|--------------
Page | 11
Page | 4
...只要在MenuItem上调用linkable(),我就会收到Class 'Page' not found.
错误。
This is the exact line in Eloquent that throws the error.
所以,我挖到了Eloquent,并认为我可以逃脱这样的事情:
// \Angel\Core\MenuItem
public function linkable()
{
return $this->morphTo(null, App::make($this->linkable_type));
}
...这感觉非常接近,但是唉:Eloquent在它填充了MenuItem的其他属性/列之前调用了linkable(),所以$ this-> linkable_type为null,因此不会解决IoC中的任何问题。
非常感谢您提供任何指导!
答案 0 :(得分:4)
public function linkable()
{
return $this->morphTo(null, App::make($this->linkable_type));
}
这在任何情况下都不起作用,因为Illuminate\Database\Eloquent\Model中的morphTo()
期待
Page
的实例)如果没有提供它们,Laravel足够聪明地猜测它们,然后相应地返回Illuminate\Database\Eloquent\MorphTo
个对象。
此外,$this->linkable_type
和$this->linkable_id
实际上不应该是null
。
让我们快速浏览一下morphTo()
函数的相关部分:
$instance = new $class;
return new MorphTo(
$instance->newQuery(), $this, $id, $instance->getKeyName(), $type, $name
);
注意:这是版本4.2.6中的代码,上面链接的代码似乎来自更高版本,略有不同,函数返回
BelongsTo
而不是MorphTo
对象。
问题特别是$instance = new $class;
- 该类只是实例化而未解决。但你可以抓住那部分魔法并自己处理它:
public function linkable()
{
$instance = App::make($this->linkable_type);
$id = 'linkable_id';
$type = 'linkable_type';
$name = 'linkable';
return new MorphTo(
$instance->newQuery(), $this, $id, $instance->getKeyName(), $type, $name
);
}
这实际上应该有效(尚未测试过),但我不确定它可能会在某些边缘情况下产生任何副作用。
或许您也可以覆盖MenuItem
- 模型中的整个功能,只需调整相关部分:
public function morphTo($name = null, $type = null, $id = null)
{
if (is_null($name))
{
list(, $caller) = debug_backtrace(false);
$name = snake_case($caller['function']);
}
list($type, $id) = $this->getMorphs($name, $type, $id);
//eager loading
if (is_null($class = $this->$type))
{
return new MorphTo(
$this->newQuery(), $this, $id, null, $type, $name
);
}
// normal lazy loading
else
{
// this is the changed part
$instance = \App::make($class); // new $class;
return new MorphTo(
$instance->newQuery(), $this, $id, $instance->getKeyName(), $type, $name
);
}
}
注意:这适用于延迟加载,但不适用于预先加载。 An issue has been raised seeking a solution for eager-loading here.
然后这种关系会像往常一样:
public function linkable()
{
return $this->morphTo();
}