【问题标题】:How can I transform / cast a PHP parent object to a child object?如何将 PHP 父对象转换/转换为子对象?
【发布时间】:2018-05-02 16:01:32
【问题描述】:

原因

我有一个带有包含 slug 的表的旧系统。

当这些记录匹配时,它代表某种具有布局 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
}

但是在我的控制器中,为了知道我需要哪种布局,我已经必须查询:

网址:/{slug}

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

if ($page->layout_id === 6) {
    //transform $page (Page) to an ArticlePage
}

因此,我得到了一个 Page 的实例,但我想将其转换或转换为 ArticlePage 的实例以加载它的附加关系。我怎样才能做到这一点?

【问题讨论】:

标签: php laravel eloquent


【解决方案1】:

你需要在 Laravel 中查看 Polymorphic relations 来实现这一点。多态关系允许您根据字段的 type 检索不同的模型。在你的 Slug 模型中,你需要这样的东西:

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

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

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

现在,每当您使用 page 关系时,Laravel 都会检查类型并返回正确的模型。

注意:我不能 100% 确定参数等,我还没有测试过,但你应该可以从文档中解决。


如果您的layout_idPage 模型上,我看到的唯一解决方案是向您的Page 模型添加一个方法,该方法能够您现有的页面转换为@ 987654330@ 或其他页面类型,基于其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的实例

【讨论】:

  • 这是一个优雅的解决方案。也就是说,如果你当然是多态关系:)
  • 有点晚了,但这个解决方案的唯一缺陷(对我来说)是 layoutID 在 Page 模型本身上,而不是在 Slug 上。
  • @ThomasMoors 查看我的更新答案,如果多态关系不适合您,希望对您有所帮助
【解决方案2】:

据我所知,没有内置函数。 但是你可以做这样的事情。

  $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 的关系 page() 上创建一个自动执行此操作的静态方法可能是明智的。

【讨论】:

  • $page->getAttributes() 可以替换为 $page->toArray() 这将利用访问器和类型转换。例如,如果您的属性转换为 json,则 getAttributes 的使用会将值作为字符串获取,从而破坏数据类型。
猜你喜欢
  • 2021-08-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-10
  • 1970-01-01
  • 2022-01-20
  • 2015-12-10
  • 1970-01-01
相关资源
最近更新 更多