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

nafvub8i  于 2023-02-25  发布在  其他
关注(0)|答案(8)|浏览(138)

我可以找到一些关于这个问题的讨论,但没有明确的解决方案。这里有两个链接,虽然我会在我自己的问题中涵盖一切。
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,路径,可成像ID,可成像类型
1,图片. png,1,职员
现在,我可以从Staff模型访问照片,也可以从Photo模型访问Staff成员。

//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,路径,可成像ID,可成像类型
1,image.png,1,应用程序/用户/员工
但是我不想那样,把完整的命名空间类名保存在数据库中是个糟糕的主意!
幸运的是,Laravel有一个设置$morphClass变量的选项,如下所示:

class Staff extends Eloquent {

    protected $morphClass = 'staff';

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

}

现在,数据库中的行将如下所示,这太棒了!
ID,路径,可成像ID,可成像类型
1,图像. 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时通知多态关系使用什么类名,但是我在文档、源代码或Google中找不到任何关于这应该如何工作的参考。
有人帮忙吗?

lndjwyie

lndjwyie1#

有两个简单的方法-一个在下面,另一个在@lukasgeiter的答案由泰勒奥特韦尔建议,我肯定建议检查以及:

// 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)来避免可能的重复,因为被称为MorphXxxx的可能性很小,所以非常安全。
比存储名称空间更好(我不介意数据库中的外观,但是如果你改变了一些东西,比如用Cartalyst\Sentinel\User代替App\Models\User等等,这会很不方便),因为你所需要的只是通过别名配置交换实现
然而,也有缺点-你不会知道,什么是模型,只是检查数据库-如果它对你很重要。

c8ib6hqw

c8ib6hqw2#

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

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

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

bmvo0sr5

bmvo0sr53#

我喜欢@JarekTkaczyks解决方案,我建议您使用这个解决方案,但是,为了完整起见,Taylor在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);
        // for Laravel5.7 or later
        return \Arr::get($this->types, $type, $type);

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

}

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

ewm0tg9j

ewm0tg9j4#

这是从Eloquent模型获取变形类名(别名)的方法:

(new Post())->getMorphClass()
zmeyuzjn

zmeyuzjn5#

让laravel把它放到db - namespace中,如果你除了让你的数据库更漂亮之外还需要一个短类名,那么就定义一个accessor,如下所示:

<?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已经经过了很好的测试,并且在大多数情况下都能像预期的那样工作。如果没有必要,为什么要与框架抗争呢?特别是如果你只是对数据库中的名称空间感到恼火的话。我同意这样做不是一个好主意,但我也觉得我可以把时间花在更有用的事情上,克服它,处理域代码。

pbossiut

pbossiut6#

为了快速加载多态关系,例如Photo::with('imageable')->get();,如果type为空,则需要返回null。

class Photo extends Eloquent {

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

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

    public function getImageableTypeAttribute($type) {
        // Illuminate/Database/Eloquent/Model::morphTo checks for null in order
        // to handle eager-loading relationships
        if(!$type) {
            return null;
        }

        // 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'
    }

}
gfttwv5a

gfttwv5a7#

一个简单的解决方案是将数据库中imageable_type列中的值作为完整类命名空间的别名:

// app/config/app.php 
'aliases' => [
...
'Order' => 'Some\Namespace\Order',
'Staff' => 'Maybe\Another\Namespace\Staff',
...
]
jqjz2hbq

jqjz2hbq8#

对于那些使用更高版本的Laravel的人,我只是在重新查看AppServiceProvider并试图理解这里的答案时偶然发现了这个问题。
我在我的项目中使用Laravel 8,为了给变形添加别名,使用了Illuminate\Database\Eloquent\Relations\Relation中的Relation类。在同一个类中,有一个名为Relation::getMorphedModel($alias)的函数,其中$aliasAppServiceProvider中提供的模型的别名。
可以使用该函数立即访问类及其命名空间。
下面是一个例子:
[AppServiceProvider]

use Illuminate\Database\Eloquent\Relations\Relation;

// Uses alias instead of their path names
Relation::morphMap([
    'user' => 'App\User',
    'booking' => 'App\Booking',
    'additional_order' => 'App\AdditionalOrder',
]);

[控制器/型号]

use Illuminate\Database\Eloquent\Relations\Relation;

echo Relation::getMorphedModel('user');
// Returns "App\User"

相关问题