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

时间:2018-04-30 10:01:31

标签: php laravel eloquent

原因

我有一个包含slugs的表的遗留系统。

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

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

我使用Laravel的Eloquent模型。

我想要的是拥有一个包含布局特定关系的子模型。

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的实例来加载它的附加关系。我怎样才能做到这一点?

2 个答案:

答案 0 :(得分:4)

你需要调查Laravel中的Polymorphic relations来实现这一目标。多态关系允许您根据字段的类型检索不同的模型。在Slug模型中,您需要这样的内容:

public function page()
{
    return $this->morphTo('page', 'layout_id', 'id');
}

在您的某个服务提供商中,例如AppServiceProvider您需要提供变形图来告诉Laravel将某些ID映射到某些模型类。例如:

Relation::morphMap([
    1 => Page::class,
    // ...
    6 => ArticlePage::class,
]);

现在,无论何时使用page关系,Laravel都会检查类型并为您提供正确的模型。

注意:我对参数等不是100%肯定,我没有测试过,但您应该可以从文档中解决这个问题。

如果您的layout_id位于Page模型上,我看到的唯一解决方案是向您的Page模型中添加能够转换的模式您的现有页面基于其ArticlePage属性的layout_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.');
    }
}

这样做的目的是查找基于layout_id属性的映射,然后创建一个正确类型的新类,并使用您正在创建的页面填充其属性。这应该是你需要的,如果你看看Laravel的Illuminate\Database\Eloquent::newFromBuilder()方法,Laravel在创建新模型实例时会调用它,你可以看到发生了什么以及我如何获得上面的代码。您应该能够像这样使用它:

$page = Slug::where('slug', $slug)
            ->first()
            ->page
            ->toLayoutPage();

这将为您提供ArticlePage

的实例

答案 1 :(得分:0)

据我所知,没有内置功能。 但你可以这样做。

  $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的关系页面()上自动执行此操作。