Laravel Eloquent和命名空间问题

时间:2014-07-12 16:15:34

标签: php mysql laravel laravel-4 eloquent

我正在构建CMS package for Laravel

此程序包中的所有models都绑定到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可以绑定到一个其他模型,例如Page或BlogPost。

为了实现这个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来说“Page”'而不是' \ Angel \ Core \ Page'而是要从IoC' Page'而不是硬编码到特定的命名空间类。

我尝试过的事情:

根据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为null,因此不会解决IoC中的任何问题。

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

1 个答案:

答案 0 :(得分:4)

public function linkable()
{
    return $this->morphTo(null, App::make($this->linkable_type));
}

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

  1. 多态关系的名称('linkable')
  2. 要变形的对象的类型('Page',而不是Page的实例)
  3. 要变形的对象的 id
  4. 如果没有提供它们,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而不是MorphTo对象。

    问题特别是$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 - 模型中的整个功能,只需调整相关部分:

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