【问题标题】:Laravel Eloquent and Namespacing IssuesLaravel Eloquent 和命名空间问题
【发布时间】:2014-09-03 02:09:39
【问题描述】:

我正在构建一个CMS package for Laravel

我在这个包中的所有models 都绑定到 IoC 容器并从 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 都可以绑定到单个其他模型,例如页面或博客帖子。

为了以 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 说“页面”而不是“\Angel\Core\Page”,并且需要从 IoC 的“页面”中解析,而不是硬编码到特定的命名空间类。

我的尝试:

根据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 为空,因此不会从 IoC 解析任何内容。

非常感谢您提供的任何指导!

【问题讨论】:

    标签: php mysql laravel laravel-4 eloquent


    【解决方案1】:
    public function linkable()
    {
        return $this->morphTo(null, App::make($this->linkable_type));
    }
    

    这在任何情况下都不起作用,因为morphTo()Illuminate\Database\Eloquent\Model 期待

    1. 多态关系的名称('linkable')
    2. 要变形的对象的类型(“页面”,不是Page 的实例)
    3. 要变形的对象的id

    如果没有提供它们,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 而不是 @987654335 @对象。

    问题特别是$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-model 中覆盖整个函数并调整相关部分:

    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();
    }
    

    【讨论】:

    • 你是个天才。非常感谢你的帮助。你说我们不能为 name 传入 null 因为它不能推导出来,但是this made me think that we it could!
    • 虽然我使用的是 Laravel 4.1,它有 this morphTo
    • 老鼠! Quasdunk,当我急切加载这种关系时,它不再起作用,因为 $this->linkable_type 在那种情况下确实为空。我想当急切加载时,Eloquent 在填充属性/列之前调用关系方法。 :( 我将继续寻找可行的解决方案。
    • @Leng 是的,这可能是我所说的副作用之一 :) 但我想如果你只是覆盖模型中的整个函数并只更改相关部分而不是仅仅更改它应该可以工作如前所述提取延迟加载部分。我刚刚在这里测试过,它对我来说很好用(Laravel 4.2.6)。
    • //编辑 - 如果我急切地从页面加载菜单项,它将不起作用。抱歉,以错误的方式测试它:-P
    猜你喜欢
    • 2014-09-07
    • 1970-01-01
    • 2017-12-13
    • 1970-01-01
    • 2020-02-23
    • 2012-01-27
    • 2023-03-21
    • 2014-03-15
    • 2015-04-24
    相关资源
    最近更新 更多