如何将PHP父对象转换/强制转换为子对象?

如何将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

原因

我有一个遗留系统,有一个包含slug的表

当这些记录匹配时,它表示具有布局ID的某种页面

因为这些页面可能有不同的资源需求,所以这取决于可以连接哪些表的布局ID

我使用拉威尔的雄辩模型

我想要的是有一个子模型,它保存布局特定的关系

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会将值转换为字符串,从而损坏数据类型。