如何将PHP父对象转换/强制转换为子对象?
原因 我有一个遗留系统,有一个包含slug的表 当这些记录匹配时,它表示具有布局ID的某种页面 因为这些页面可能有不同的资源需求,所以这取决于可以连接哪些表的布局ID 我使用拉威尔的雄辩模型 我想要的是有一个子模型,它保存布局特定的关系如何将PHP父对象转换/强制转换为子对象?,php,laravel,eloquent,Php,Laravel,Eloquent,原因 我有一个遗留系统,有一个包含slug的表 当这些记录匹配时,它表示具有布局ID的某种页面 因为这些页面可能有不同的资源需求,所以这取决于可以连接哪些表的布局ID 我使用拉威尔的雄辩模型 我想要的是有一个子模型,它保存布局特定的关系 class Page extends Model { // relation 1, 2, 3 that are always present } class ArticlePage extends Page { // relation 4 an
class Page extends Model {
// relation 1, 2, 3 that are always present
}
class ArticlePage extends Page {
// relation 4 and 5, that are only present on an ArticlePage
}
然而,在我的控制器中,为了知道我需要哪种布局,我必须查询:
url:/{slug}
$page = Slug::where('slug', $slug)->page;
if ($page->layout_id === 6) {
//transform $page (Page) to an ArticlePage
}
因此,我得到了一个Page实例,但我想将其转换或强制转换为ArticlePage实例,以加载它的附加关系。如何实现这一点?据我所知,此功能没有内置功能。 但你可以这样做
$page = Slug::where('slug', $slug)->page;
if ($page->layout_id === 6) {
$page = ArticlePage::fromPage($page);
}
然后在ArticlePage上创建静态方法
public static function fromPage(Page $page)
{
$articlePage = new self();
foreach($page->getAttributes() as $key => $attribute) {
$articlePage->{$key} = $attribute;
}
return $articlePage
}
根据您的使用案例,创建一个静态方法,在Slug的关系页()上自动执行此操作可能是明智的。您需要在Laravel中查看以实现此目的。多态关系允许您根据字段的类型检索不同的模型。在您的Slug
型号中,您需要这样的东西:
public function page()
{
return $this->morphTo('page', 'layout_id', 'id');
}
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Page extends Model
{
const LAYOUT_ARTICLE = 6;
protected $layoutMappings = [
// Add your other mappings here
self::LAYOUT_ARTICLE => ArticlePage::class
];
public function toLayoutPage()
{
$class = $this->layoutMappings[$this->layout_id];
if (class_exists($class)) {
return (new $class())
->newInstance([], true)
->setRawAttributes($this->getAttributes());
}
throw new \Exception('Invalid layout.');
}
}
$page = Slug::where('slug', $slug)
->first()
->page
->toLayoutPage();
在您的一个服务提供商中,例如,AppServiceProvider
,您需要提供一个变形映射来告诉Laravel将某些ID映射到某些模型类。例如:
Relation::morphMap([
1 => Page::class,
// ...
6 => ArticlePage::class,
]);
现在,无论何时使用页面
关系,Laravel都会检查类型并返回正确的模型
注意:我对参数等不是100%确定,我还没有测试过,但你应该可以从文档中找到答案
如果您的
layout\u id
位于页面
模型上,我看到的唯一解决方案是向页面
模型添加一个方法,该方法能够根据其layout\u id
属性将现有页面转换为文章页面
,或其他页面类型。您应该能够尝试以下内容:
public function page()
{
return $this->morphTo('page', 'layout_id', 'id');
}
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Page extends Model
{
const LAYOUT_ARTICLE = 6;
protected $layoutMappings = [
// Add your other mappings here
self::LAYOUT_ARTICLE => ArticlePage::class
];
public function toLayoutPage()
{
$class = $this->layoutMappings[$this->layout_id];
if (class_exists($class)) {
return (new $class())
->newInstance([], true)
->setRawAttributes($this->getAttributes());
}
throw new \Exception('Invalid layout.');
}
}
$page = Slug::where('slug', $slug)
->first()
->page
->toLayoutPage();
这将为您提供一个
ArticlePage
的实例,您可能希望了解多态关系:这是一个优雅的解决方案。也就是说,如果你对多态关系感兴趣,当然:)有点晚了,但这个解决方案(对我来说)唯一的缺陷是layoutID在页面模型本身,而不是Slug上。@ThomasMoors看到了我更新的答案,如果多态关系不是你想要的方式,希望这会对你有所帮助$Page->getAttributes()可以替换为$page->toArray(),这将利用访问器和类型转换。例如,如果将属性转换为json,则使用getAttributes会将值转换为字符串,从而损坏数据类型。