Laravel:返回多态关系的命名空间所有者

时间:2015-01-09 17:31:23

标签: laravel laravel-4 polymorphism eloquent

我可以找到一些有关此问题的讨论,但没有明确的解决方案。这里有两个链接,虽然我将在这里讨论我自己的问题。

Github Issues

Laravel.io discussion

简单说明

对于已经熟悉Laravel多态关系的人来说,这是对我的问题的简单解释。

当使用$ morphClass时,在尝试查找多态关系的所有者时,将$ morphClass的内容作为变体“type”保存在数据库中用于类名。这会导致错误,因为$ morphClass的整个点是它不是类的完全命名空间名称。

如何定义多态关系应该使用的类名?

更详细的说明

这是一个更详细的解释,准确地解释了我正在尝试做什么以及为什么我试图用例子来做。

在Laravel中使用多态关系时,无论保存为什么,数据库中的“morph_type”类型都被认为是类。

所以在这个例子中:

class Photo extends Eloquent {

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

}

class Staff extends Eloquent {

    public function photos()
    {
        return $this->morphOne('Photo', 'imageable');
    }

}

class Order extends Eloquent {

    public function photos()
    {
        return $this->morphOne('Photo', 'imageable');
    }

}

数据库看起来像这样:

staff

 - id - integer
 - name - string

orders

 - id - integer
 - price - integer

photos

 - id - integer
 - path - string
 - imageable_id - integer
 - imageable_type - string

现在第一排照片可能如下所示:

  

ID,路径,imageable_id,imageable_type

     

1,image.png,1,工作人员

现在,我可以从职员模型访问照片,也可以从照片模型访问职员。

//Find a staff member and dump their photos
$staff = Staff::find(1);

var_dump($staff->photos);

//Find a photo and dump the related staff member
$photo = Photo::find(1);

var_dump($photo->imageable);

到目前为止一切顺利。但是,当我命名它们时,我遇到了一个问题。

namespace App/Store;
class Order {}

namespace App/Users;
class Staff {}

namespace App/Photos;
class Photo {}

现在我的数据库中保存的是:

  

ID,路径,imageable_id,imageable_type

     

1,image.png,1,应用/用户/工作人员

但我不希望如此。将完整的命名空间类名保存在数据库中是一个糟糕的想法!

幸运的是,Laravel可以选择设置$ morphClass变量。像这样:

class Staff extends Eloquent {

    protected $morphClass = 'staff';

    public function photos()
    {
        return $this->morphOne('Photo', 'imageable');
    }

}

现在我的数据库中的行看起来像这样,真棒!

  

ID,路径,imageable_id,imageable_type

     

1,image.png,1,工作人员

获得一名工作人员的照片绝对正常。

//Find a staff member and dump their photos
$staff = Staff::find(1);

//This works!
var_dump($staff->photos);

然而,找到照片所有者的多态魔术不起作用:

//Find a photo and dump the related staff member
$photo = Photo::find(1);

//This doesn't work!
var_dump($photo->imageable);

//Error: Class 'staff' not found

据推测,必须有一种方法来告知使用$ morphClass时要使用的类名的多态关系,但我找不到任何关于这应该如何在文档,源代码或谷歌中工作的参考。

任何帮助?

6 个答案:

答案 0 :(得分:44)

有两个简单的方法 - 一个在下面,另一个在@ lukasgeiter的答案中,由Taylor Otwell提出,我肯定建议检查:

// app/config/app.php or anywhere you like
'aliases' => [
    ...
    'MorphOrder' => 'Some\Namespace\Order',
    'MorphStaff' => 'Maybe\Another\Namespace\Staff',
    ...
]

// Staff model
protected $morphClass = 'MorphStaff';

// Order model
protected $morphClass = 'MorphOrder';

完成

$photo = Photo::find(5);
$photo->imageable_type; // MorphOrder
$photo->imageable; // Some\Namespace\Order

$anotherPhoto = Photo::find(10);
$anotherPhoto->imageable_type; // MorphStaff
$anotherPhoto->imageable; // Maybe\Another\Namespace\Staff

我不会使用真实的类名(OrderStaff)来避免可能的重复。 <{1}}被称为MorphXxxx的可能性非常小,因此非常安全。

这比存储命名空间更好(我不介意数据库中的外观,但是如果你改变某些东西会不方便 - 比如说App\Models\User使用{{ 1}}等等因为你只需要通过别名配置交换实现

然而,也有缺点 - 你不会知道,模型是什么,只需检查数据库 - 以防它对你来说很重要。

答案 1 :(得分:22)

我喜欢@JarekTkaczyks解决方案,我建议你使用那个。但是,为了完整起见,泰勒在github

上简要提到了另一种方式

您可以为imageable_type属性添加属性访问器,然后使用“classmap”数组来查找正确的类。

class Photo extends Eloquent {

    protected $types = [
        'order' => 'App\Store\Order',
        'staff' => 'App\Users\Staff'
    ];

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

    public function getImageableTypeAttribute($type) {
        // transform to lower case
        $type = strtolower($type);

        // to make sure this returns value from the array
        return array_get($this->types, $type, $type);

        // which is always safe, because new 'class'
        // will work just the same as new 'Class'
    }

}

注意,您仍然需要morphClass属性来从关系的另一侧查询。

答案 2 :(得分:18)

使用Laravel 5.2(或更高版本)时,您可以使用新功能morphMap来解决此问题。只需将其添加到boot中的app/Providers/AppServiceProvider函数:

即可
Relation::morphMap([
    'post' => \App\Models\Post::class,
    'video' => \App\Models\Video::class,
]);

更多相关信息:https://nicolaswidart.com/blog/laravel-52-morph-map

答案 3 :(得分:3)

让laravel将它放入db - namespace和all。如果您需要除了使数据库更漂亮之外的其他东西的短类名,那么为以下内容定义一个访问器:

<?php namespace App\Users;

class Staff extends Eloquent {

    // you may or may not want this attribute added to all model instances
    // protected $appends = ['morph_object'];

    public function photos()
    {
        return $this->morphOne('App\Photos\Photo', 'imageable');
    }

    public function getMorphObjectAttribute()
    {
        return (new \ReflectionClass($this))->getShortName();
    }

}

我总是回到这样的场景中的原因是Laravel经过了很好的测试,并且在大多数情况下都能按预期工作。如果你不必要,为什么要打架 - 特别是如果你只是被数据库中的命名空间所困扰。我同意这样做并不是一个好主意,但我也觉得我可以花更多的时间来克服它并处理域代码。

答案 4 :(得分:2)

为了急切加载多态关系,例如if (dt.columns.Contains("Security")) { .... }else{ // Show error in fist row, fist column return dt.Rows[0][0].ToString(); } ,如果type为空,则返回null是必要的。

Photo::with('imageable')->get();

答案 5 :(得分:0)

这是从雄辩模型中获取变形类名称(别名)的方法:

(new Post())->getMorphClass()