简体   繁体   English

Laravel Eloquent和命名空间问题

[英]Laravel Eloquent and Namespacing Issues

I am building a CMS package for Laravel . 我正在为Laravel构建一个CMS包

All of my models in this package are bound to, and resolved from, the IoC container so that they can be easily overwritten in any individual deployment of the package. 此软件包中的所有模型都与IoC容器绑定并解析,以便在软件包的任何单独部署中轻松覆盖它们。

For non-polymorphic relationships, this has worked a charm. 对于非多态关系,这有点魅力。

For instance, a Page has many PageModules , so its relationship changed from: 例如,一个Page有很多PageModules ,所以它的关系改为:

// \Angel\Core\Page

public function modules()
{
    return $this->hasMany('PageModule');
}

to: 至:

// \Angel\Core\Page

public function modules()
{
    return $this->hasMany(App::make('PageModule'));
}

But I haven't been able to figure out how to do the same thing with polymorphic relationships. 但是我无法弄清楚如何用多态关系做同样的事情。

For instance, Menus contain MenuItems , and each MenuItem can be tied to a single other model, such as a Page or BlogPost. 例如,菜单包含MenuItems ,每个MenuItem可以绑定到单个其他模型,例如Page或BlogPost。

To accomplish this the Laravel way, I've added the following relationship to MenuItem: 为了实现这个Laravel方式,我为MenuItem添加了以下关系:

// \Angel\Core\MenuItem

public function linkable()
{
    return $this->morphTo();
}

And this relationship to LinkableModel , which all models such as Page and BlogPost extend: 而这种关系LinkableModel ,其全系车型,如网页和博客帖子延伸:

// \Angel\Core\LinkableModel

public function menuItem()
{
    return $this->morphOne(App::make('MenuItem'), 'linkable');
}

And the menus_items table (which MenuItems use) has these rows: menus_items表(MenuItems使用)包含以下行:

linkable_type      |  linkable_id
-------------------|--------------
\Angel\Core\Page   |   11
\Angel\Core\Page   |   4

This works great, but I need the linkable_type to say 'Page' instead of '\\Angel\\Core\\Page', and to be resolved from the IoC's 'Page' instead of being hard-coded to a particular namespaced class. 这很好用,但是我需要linkable_type来表示'Page'而不是'\\ Angel \\ Core \\ Page',并且要从IoC的'Page'中解析而不是硬编码到特定的命名空间类。

What I've Tried: 我试过的:

According to this question , it should be as easy as defining a $morphClass property to the linkable() classes, like so: 根据这个问题 ,它应该像为$morphClass ()类定义$morphClass属性一样容易,如下所示:

// \Angel\Core\Page
protected $morphClass = 'Page';

But when I apply this, and change the menus_items table to look like this: 但是当我应用它时,将menus_items表更改为menus_items所示:

linkable_type  |  linkable_id
---------------|--------------
Page           |   11
Page           |   4

...I simply get a Class 'Page' not found. ...我只是Class 'Page' not found. error whenever linkable() is called on MenuItem. 在MenuItem上调用linkable()时出错。

This is the exact line in Eloquent that throws the error. 这是Eloquent中引发错误的确切行。

So, I dug into Eloquent and thought I might be able to get away with something like this: 所以,我挖到了Eloquent,并认为我可以逃脱这样的事情:

// \Angel\Core\MenuItem

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

...this feels so close, but alas: Eloquent calls linkable() before it's filled the rest of the MenuItem's attributes / columns, so $this->linkable_type is null and therefore will not resolve anything from the IoC. ...感觉非常接近,但是唉:Eloquent在填充MenuItem的其余属性/列之前调用linkable(),因此$ this-> linkable_type为null,因此无法解决IoC中的任何问题。

Thank you so much in advance for any guidance you might have! 非常感谢您提供任何指导!

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

This will not work in any case, because morphTo() in Illuminate\\Database\\Eloquent\\Model expects 这在任何情况下都不起作用,因为Illuminate \\ Database \\ Eloquent \\ Model中的 morphTo()需要

  1. The name of the polymorphic relationship ('linkable') 多态关系的名称 ('linkable')
  2. The type of the object to be morphed ('Page', not an instance of Page ) 要变形的对象的类型 ('Page',而不是Page的实例)
  3. The id of the object to be morphed 要变形的对象的id

If they are not provided, Laravel is smart enough to guess them and then accordingly return a Illuminate\\Database\\Eloquent\\MorphTo object. 如果没有提供它们,Laravel足够聪明地猜测它们,然后返回一个Illuminate\\Database\\Eloquent\\MorphTo对象。

Also, $this->linkable_type and $this->linkable_id should actually not be null in that context. 此外, $this->linkable_type$this->linkable_id在该上下文中实际上不应为null

Let's have a quick look at the relevant part of the morphTo() function: 让我们快速浏览一下morphTo()函数的相关部分:

$instance = new $class;

return new MorphTo(
    $instance->newQuery(), $this, $id, $instance->getKeyName(), $type, $name
);

Note : This is the code from version 4.2.6, the code linked above seems to be from a later version and is slightly different and the function returns a BelongsTo instead of a MorphTo object. :这是4.2.6版本的代码,上面链接的代码似乎是从一个更高版本和略有不同,函数返回一个BelongsTo ,而不是MorphTo对象。

The problem is specifically the $instance = new $class; 问题特别是$instance = new $class; - the class is simply instanciated and not resolved. - 班级只是实例化而没有解决。 But you can just grab that part of the magic and handle it yourself: 但是你可以抓住那部分魔法并自己处理它:

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

This should actually work (haven't tested it), but I'm not sure about any side effects it might cause in some edge cases. 这实际上应该工作(没有测试过),但我不确定它可能会在某些边缘情况下产生任何副作用。

Or maybe you could also just override the whole function in your MenuItem -model and just adjust the relevant part: 或者也许您也可以覆盖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
        );
    }
}

Note : This works well for lazy-loading, but does not work for eager-loading. 注意 :这适用于延迟加载,但不适用于预先加载。 An issue has been raised seeking a solution for eager-loading here. 已经提出了一个问题,在这里寻求急切加载的解决方案。

The relationship would then be as usual: 然后这种关系将照常进行:

public function linkable()
{
    return $this->morphTo();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM