【问题标题】:Laravel: Returning the namespaced owner of a polymorphic relationLaravel:返回多态关系的命名空间所有者
【发布时间】:2015-03-08 02:18:30
【问题描述】:

我可以找到许多关于此的讨论,但没有明确的解决方案。这里有两个链接,虽然我会在这里涵盖我自己的问题中的所有内容。

Github Issues

Laravel.io discussion

简单说明

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

当使用 $morphClass 时,在试图查找多态关系的所有者时,使用 $morphClass 的内容作为 morph“类型”保存在数据库中作为类名。这会导致错误,因为 $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,path,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,path,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,path,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 时,必须有一种方法可以告知多态关系使用什么类名,但我在文档、源代码或 Google 中找不到任何关于这应该如何工作的参考。

有什么帮助吗?

【问题讨论】:

  • 你有没有尝试通过命名空间传递给 $this->morphOne() 模型名称?
  • 会是哪个论点?
  • 首先,返回 $this->morphOne('App/Photos/Photo', 'imageable');
  • 啊,是的,我已经这样做了,抱歉,再次复制和粘贴整个内容似乎不值得,但这没什么区别。问题在于 morphTo() 而不是 morphOne()

标签: laravel laravel-4 polymorphism eloquent


【解决方案1】:

有两种简单的方法 - 一种在下面,另一种在 Taylor Otwell 提出的@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 的可能性很小,所以它非常安全。

比存储命名空间更好(我不介意数据库中的外观,但是如果您更改某些内容会很不方便 - 比如说使用 App\Models\User 而不是 Cartalyst\Sentinel\User 等) 因为您只需要通过别名配置交换实现

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

【讨论】:

  • 啊,这是一个非常好的解决方案。我没有立即测试它,但它很有意义并且看起来应该可以工作。我还没有在其他任何地方找到这样的解决方案:)
  • 我认为以前没有人想出它。
  • 由于花时间想出一个独特的解决方案,因此向您分配了赏金。尽管对于以后阅读本文的任何人来说,查看@lukasgeiter 的另一个答案也是值得的
  • 我在定义 morphOne 关系时总是使用MyClass::class,所以我从来没有遇到过这个问题。但是,我从来没有想过重构对数据库的影响,谢谢!
  • 请注意,Laravel 5.2 包含 MorpMaps:nicolaswidart.com/blog/laravel-52-morph-map
【解决方案2】:

让 laravel 把它放到 db - 命名空间等等。如果除了让你的数据库更漂亮之外,你还需要一些简短的类名,那么为类似的东西定义一个访问器:

<?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 已经过很好的测试,并且在大多数情况下都可以正常工作。如果您不需要,为什么要与框架抗争——特别是如果您只是对数据库中的命名空间感到恼火。我同意这样做不是一个好主意,但我也觉得我可以花时间更有效地克服它并处理域代码。

【讨论】:

  • 您知道这是经过充分测试的 Laravel 中的一个错误吗?有一个方法可以解决这个问题,但是 Eloquent 最终无法处理它:)
  • 它不仅仅是在数据库中的“烦恼”。 Laravel 的主要功能之一是您可以轻松地更改您正在使用的类并删除和删除不同的类。在这种情况下,如果我更改代码的结构,我需要更新数据库,而不仅仅是数据库的结构(在进行更改时很常见),而是行本身中的实际数据。这在应用程序的工作流程/设计中是一个糟糕的流程。
  • 好吧,随你便吧。一个快速更新调用@ db end 解决了更改路径的问题——一旦你部署了命名空间更改的可能性,这些模型中的任何一个都应该是相当低的,并且更改它们的问题可以通过命令行大约 30 毫秒的查询时间来解决.我的观点更多的是——即使它是一个错误,如果你不这样做,花时间去对抗它现在是浪费精力。
  • 另外,如果你觉得这是一个错误(看起来确实如此!)然后提交一个带有修复的拉取请求@github - 或者至少打开一个问题并确认这是没有按预期工作。
【解决方案3】:

我喜欢@JarekTkaczyks 解决方案,我建议您使用那个解决方案。但是,为了完整起见,Taylor 在github 上简要提到了另一种方式

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

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 属性才能从关系的另一端进行查询。

【讨论】:

  • 干杯,我也喜欢你的。你的(或 Taylor 的)可能会更好,因为它都被限制在 Eloquent 模型中,而不是需要使用配置文件。赏金给谁是一个艰难的选择!
  • 谢谢。我也会对@JarekTkaczyk 的回答给予赏金......虽然它仍然不是一个很好的解决方案,而且这个“错误”仍然让我感到困扰。如果我有时间,我会看看我是否能想出办法来修复它并提出拉取请求。如果发生这种情况,我一定会在这里发表评论;)
  • 是的,这也很好,如果有这个就更好了。只需修复访问器以确保字母大小写没有问题。 注意$array['Type'] != $array['type']new 'Type' == new 'type' - 欢迎使用 PHP ;)
  • @JarekTkaczyk 我很清楚这一点。但我不知道我必须以哪种方式“修复”访问器。我没有将 $types 的数组键视为类名,而是区分大小写的标识符。如果我误解了您,请随时提出修改建议...
  • 我必须将'' =&gt; self:class 添加到$types 数组中,否则在第一次保存模型时会报错
【解决方案4】:

为了快速加载多态关系,例如Photo::with('imageable')-&gt;get();,如果类型为空,则必须返回 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'
    }

}

【讨论】:

  • 使用这个方法,我发现$type的值最后多了一个制表符"\t"。
【解决方案5】:

在使用 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

【讨论】:

  • 在哪里添加这个?
  • app/Providers/AppServiceProviderboot函数中。
  • 这对我有用,但我在解开 MorphMap 时遇到了麻烦。也许这对其他人有帮助。如果您在关系中使用 morphmap,例如照片你可以这样使用它:在返回之前添加$relation = Relation::morphMap();以获取地图,然后添加return $this-&gt;morphOne($relation['photo'], 'imagable');。不确定是否有更简单的方法,但像这样它可以在没有找到照片的情况下工作。
  • 另一种设置变形的有用方法。将$table-&gt;morphs('imageable'); 添加到您的照片迁移中。这将为您创建imageable_typeimageable_id,这样更容易编写。在测试设置中找到here
【解决方案6】:

这是您可以从 Eloquent 模型中获取变形类名称(别名)的方式:

(new Post())->getMorphClass()

【讨论】:

    【解决方案7】:

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

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

    【讨论】:

      猜你喜欢
      • 2015-04-25
      • 2014-03-15
      • 2019-11-14
      • 1970-01-01
      • 2020-07-31
      • 1970-01-01
      • 1970-01-01
      • 2019-01-12
      • 2016-06-21
      相关资源
      最近更新 更多