我可以找到一些有关此问题的讨论,但没有明确的解决方案。这里有两个链接,虽然我将在这里讨论我自己的问题。
对于已经熟悉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时要使用的类名的多态关系,但我找不到任何关于这应该如何在文档,源代码或谷歌中工作的参考。
任何帮助?
答案 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
我不会使用真实的类名(Order
和Staff
)来避免可能的重复。 <{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,
]);
答案 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()