如何最好地链接到 Laravel 中的多态关系
Posted
技术标签:
【中文标题】如何最好地链接到 Laravel 中的多态关系【英文标题】:How Best to Link to a Polymorphic Relation in Laravel 【发布时间】:2016-10-12 15:22:47 【问题描述】:模型 A
与模型 X
、Y
、Z
具有多态关系。 A
中的相关字段为:
poly_id
(整数外键)poly_type
(字符串App\X
、App\Y
或App\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->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 中的多态关系的主要内容,如果未能解决你的问题,请参考以下文章