如何最好地链接到 Laravel 中的多态关系

Posted

技术标签:

【中文标题】如何最好地链接到 Laravel 中的多态关系【英文标题】:How Best to Link to a Polymorphic Relation in Laravel 【发布时间】:2016-10-12 15:22:47 【问题描述】:

模型 A 与模型 XYZ 具有多态关系。 A中的相关字段为:

poly_id(整数外键)poly_type(字符串App\XApp\YApp\Z

给定模型A的实例,我可以成功地使用$a->poly检索X、Y或Z类型的相关对象(例如"id":1,"name":Object X)。

A 的 Blade 模板中,我应该如何生成指向 X 的显示链接,例如“/x/1”?我想到的是URL::route('x.show', $a-poly_>id),但据我所知,我们实际上并没有可用的路线的“x”部分——只有 poly_id、poly_type 和这两个对象。

我错过了什么吗?像采用 poly_type 字符串 'App\X' 并将最后一段和小写字母分开以获得 'x' 的解决方案,但这似乎并不理想,并且可能定义的路线可能是其他东西。

顺便说一句,在 Rails 中,我很确定您可以使用link_to($a->poly),它会神奇地返回 URL '/x/3'。不确定 Laravel 是否可以做到这一点。我试过url($a->poly) 还是不行。

【问题讨论】:

我不知道一个“正确”的方式,但我认为一个相当干净的方法是扩展你上面的想法:在你的刀片中使用 URL::route($a-> poly_type . '.show', $a->poly_id) ,然后在你的路由文件中为每种类型添加一个定义:Route::get('x/id', ['as' => 'App\ X.show', 'uses' => 'XController@show']);想法?如果你不喜欢使用完整的命名空间,你可以使用 $a->poly->getTable(),或者设置 Eloquent 属性 $a->poly->morph_class= "x"。无论如何,您都可以更新 $morph_class 值,以便更轻松地编写自定义查询。 @mattcrowe 谢谢 - 一些好的解决方法。我不知道 getTable - 这似乎最简单,因为它可以引用现有路由 URL::route($a->poly->getTable() . '.show', $a->poly_id) 在我看来,您可以在您可能“发现”的所有模型上设置属性/查找方法,这样您就可以确定路线和标题。或者,您可以在某处创建映射,例如在服务提供者中,可以找到任何给定模型的处理程序? 这个问题的答案对于生成面包屑可能也很有用(查找树,找到每个模型的 URL,无论遇到哪种模型)。 不确定这是否合适,但可以通过计算其他参数和重定向发送到形成正确链接的端点(例如 /redirector/x/2 => /project/a/model-x /2)。这会是什么,一个巨大的转变? 【参考方案1】:

使用 MorphToMany 和 MorphedByMany

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class A extends Model

    public function xs()
    
        return $this->morphedByMany('App\X', 'poly_type');
    

    public function ys()
    
        return $this->morphedByMany('App\Y', 'poly_type');
    

    public function zs()
    
        return $this->morphedByMany('App\Z', 'poly_type');
    

 

路线:

Route::get('/a/show/a/poly', 'AController@index');

和控制器:

class AController extends Controller 

    public function index(A $a, $poly)
    
         dd($a->$poly->first()); // a $poly Object
    

所以,/a/show/1/xs 是有效路径

【讨论】:

morphMap 允许您为每个多态模型存储别名。在模型中使用这些而不是映射会很好。否则这是一个很好的解决方案。 如何在模型中定义一个方法,比如 function poly($model) return $this->morphedByMany('App\' . $model, 'poly_type');并在控制器中使用它作为 dd($a->poly($poly)->first())。因此 /a/show/1/X 或 /a/like/show/Y 等路径将是有效的。它可以在没有一对一映射的情况下工作。【参考方案2】:

这可能看起来过于简单,但您可以使用类似的方法来解决您的问题

我会创建一个类似这样的控制器:

use App\Http\Controllers\Controller;
use A;
use X;
use Y;
use Z;    

class PolyController extends Controller 

public function aPoly(A $a)

    $poly = $a->ploy;
    switch ($ploy)
        case instance of X:
            return response()->redirectToRoute('X route name', $poly);
            break;
        case instance of Y:
            return response()->redirectToRoute('Y route name', $poly);
            break;
       case instance of Z:
            return response()->redirectToRoute('Y route name', $poly);
            break;
       default:
            report(new Exception('Failed to get route for ploy relationship');
   

这将允许您在路由文件中使用以下内容:

Route::get('enter desired url/a', 'PolyController@aPoly')->name('name for poly route');

然后在您的控制器中,您只需执行以下操作:

<a href=" route('name for poly route', $a) ">$a->ploy->name</a>

这就是我想处理这种情况的方式

【讨论】:

这行不通。 $a 是一个 ID; Laravel 将通过使用 A::findOrFail($a)... 为它找到一个对象,这将返回一个 A 类型的模型(如果存在);当我们知道它实际上应该是 x、y 或 z 类型时。如果你不键入提示它,你得到的只是一个整数,不知道模型/多边形类型。 @Kurucu 必须有 A 的模型,否则您将无法创建多态关系。我并不完全反对使用 5.2 的限制,但我相信它会在 5.5 及更高版本中工作。 所以问题 A 是我们已经拥有的东西(我们不需要指向它的链接)。它具有多态的一对多关系,在这种情况下,$a 有一个 $x、一个 $y 和一个 $z。所以问题是,当 $x、$y 或 $z 分别是不同的类型(分别为 X、Y 和 Z)时,如何链接到它们。 现在我明白了这个问题是如何解释的,你的代码就可以工作了。但仅适用于 1:1 的关系。 除非我弄错了,多态关系只能是一对一的。【参考方案3】:

我认为我的解决方案如下。位于多态关系多端的所有表都将具有标识route 或action 的属性(取决于我的想法)。

型号:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Y extends Model

    /**
     * The route name associated with the model.
     *
     * @var string
     */
    protected $routeName= 'path.to.y.show';

然后,您可以使用这样的代码来查找路线,而不管最后的模型是什么:

route($a->poly->routeName, $a->poly)

或者,对于hasMany

@foreach($a->polys as $object) 
    <a href="route($object->routeName, [$object])">But what name?</a>
@endforeach

不过,我不知道这是否可以接受。如果您没有在模型上定义 routeName,那么您会遇到错误。 我也不确定模型应该知道路由!

为了确定要显示的名称,是否应该定义某种从模型返回适当属性的 getGenericNameAttribute?


我正在回答,希望有人有更优雅的解决方案。例如,注册一个服务提供者,然后:

允许定义模型映射的路由(有点像策略?);和 根据传递的模型/模型类确定正确的路由。

只是我不知道该怎么做!

【讨论】:

也许模型可以扩展一个类或使用优雅地处理名称/路由的特征? 想一想,这并不能处理路由嵌套的情况。你也需要某种翻译。例如将(X:Class, ID=1) 变成/grandparent/a/parent/b/x/1【参考方案4】:

Laravel 没有像 Rails 那样的简单解决方案(目前)。那是因为路由名称和模型名称之间没有隐含的联系。有一个命名约定,但它并没有真正应用在代码中。我无法询问模特的网址,我需要致电route('model.show', $model)

这里的一些其他解决方案建议使用重定向控制器,但这作为用户体验(和 SEO)不方便且较差。您最好创建一个可以生成所需路由的辅助函数 - 这样该功能在应用程序的任何位置都可用,并且不依赖于模型或控制器层。

如果您想控制实际访问的页面(即不仅仅是显示路线),那么您可以包装 route 帮助器,它可以执行您想要的操作并生成正确的 URL。

use Illuminate\Database\Eloquent\Model;

function poly_route(string $route, Model $model): string

    return route($model->getTable() . '.' . $route, $model);

这会让你做类似poly_route('show', $poly);

通常,您将在 bootstrap/helpers.php 中放置辅助函数并将该文件注册到您的 composer.json 文件中 - 如果您已经在使用辅助文件。当然,如果你不使用帮助文件,那么这个解决方案可能已经让人觉得很麻烦了。

然后我建议您探索将函数移动到模型上的方法。

class Poly extends Model

    public function route($name)
    
        return route($this->getTable() . '.' . $route, $this);
    

然后您可以直接拨打$poly-&gt;route('show')。同样,这些解决方案都不是完全优雅,但它们可能会击败在您的应用程序中支持每个用例的 if/else 或 switch 案例。希望 Laravel 能够为这类事情提供更好的功能。

【讨论】:

【参考方案5】:

使用 Laravel 无法绑定路由和模型。但我对你的问题有一个想法。

您可以将带有mutators 的自定义属性添加到您的A 模型中,该模型将负责在对象调用时决定使用哪个路由。例如;

A模特;

/**
 * Get the route
 *
 * @return string
 */
public function getRouteAttribute()

    $route = null;
    switch ($this->poly_type)
        case 'App\X':
            $route = 'your_x_route_name';
            break;
        case 'App\Y':
            $route = 'your_y_route_name';
            break;
        case 'App\Z':
            $route = 'your_z_route_name';
            break;
    
    return $route;

我们可以把它想象成工厂方法。

当我们想使用它时,我们可以使用路由跟随方式。

@foreach($a->polys as $object) 
    <a href=" route($a->route, [your parameters.]) ">But what name?</a>
@endforeach

这可能不是完美的做法,但我认为从某一点进行管理很有用。

【讨论】:

以上是关于如何最好地链接到 Laravel 中的多态关系的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 5分离/删除多态关系

Laravel - 渴望加载多态关系的相关模型

如何在laravel中获得多态关系?

Laravel 5中的反向多态关系

Laravel - 反向多态关系

相关模型 eloquent laravel 中的自定义查询