草庐IT

php - 多个路由的相同 Laravel 资源 Controller

coder 2024-04-11 原文

我正在尝试使用 trait 作为我的 Laravel 资源 Controller 的类型提示。

Controller 方法:

public function store(CreateCommentRequest $request, Commentable $commentable)

其中Commentable是我的 Eloquent 模型使用的特征类型提示。
Commentable特性看起来像这样:
namespace App\Models\Morphs;

use App\Comment;

trait Commentable
{
   /**
    * Get the model's comments.
    *
    * @return \Illuminate\Database\Eloquent\Relations\MorphMany
    */
    public function Comments()
    {
        return $this->morphMany(Comment::class, 'commentable')->orderBy('created_at', 'DESC');
    }
}

在我的路由中,我有:
Route::resource('order.comment', 'CommentController')
Route::resource('fulfillments.comment', 'CommentController')

订单和履行都可以有注释,因此它们使用相同的 Controller ,因为代码是相同的。

然而,当我发帖到 order/{order}/comment ,我收到以下错误:

Illuminate\Contracts\Container\BindingResolutionException
Target [App\Models\Morphs\Commentable] is not instantiable.



这可能吗?

最佳答案

所以你想避免订单和履行资源 Controller 的重复代码,并且有点枯燥。好的。

特征不能被打字提示

如马修 stated ,您不能输入提示特征,这就是您收到绑定(bind)解析错误的原因。除此之外,即使它是 typehintable,容器也会混淆它应该实例化哪个模型,因为有两个 Commentable可用型号。但是,我们稍后会谈到。

接口(interface)与特征

拥有一个伴随特征的界面通常是一个很好的做法。除了接口(interface)可以进行类型提示这一事实之外,您还遵守了 Interface Segregation原则,“如果需要”是一种很好的做法。

interface Commentable 
{
    public function comments();
}

class Order extends Model implements Commentable
{
    use Commentable;

    // ...
}

现在它是类型提示的。让我们来看看容器混淆问题。

上下文绑定(bind)

Laravel 的容器支持 contextual binding .这就是明确告诉它何时以及如何将抽象解析为具体的能力。

您为 Controller 获得的唯一区别因素是路线。我们需要以此为基础。类似的东西:

# AppServiceProvider::register()
$this->app
    ->when(CommentController::class)
    ->needs(Commentable::class)
    ->give(function ($container, $params) {
        // Since you're probably utilizing Laravel's route model binding, 
        // we need to resolve the model associated with the passed ID using
        // the `findOrFail`, instead of just newing up an empty instance.

        // Assuming this route pattern: "order|fullfilment/{id}/comment/{id}"
        $id = (int) $this->app->request->segment(2);

        return $this->app->request->segment(1) === 'order'
            ? Order::findOrFail($id)
            : Fulfillment::findOrFail($id);
     });

您基本上是在告诉容器 CommentController需要 Commentable例如,首先检查路由,然后实例化正确的可评论模型。

非上下文绑定(bind)也可以:

# AppServiceProvider::register()
$this->app->bind(Commentable::class, function ($container, $params) {
    $id = (int) $this->app->request->segment(2);

    return $this->app->request->segment(1) === 'order'
        ? Order::findOrFail($id)
        : Fulfillment::findOrFail($id);
});

错误的工具

我们刚刚通过引入不必要的复杂性来消除重复的 Controller 代码,这更糟。 ?



尽管它有效,但它很复杂,不可维护,非通用,最糟糕的是,依赖于 URL。它使用了错误的工具来完成这项工作,这是完全错误的。

遗产

消除这些问题的正确工具就是继承。引入一个抽象的基本评论 Controller 类并从中扩展两个浅层。

# App\Http\Controllers\CommentController
abstract class CommentController extends Controller
{
    public function store(CreateCommentRequest $request, Commentable $commentable) {
        // ...
    }

    // All other common methods here...
}

# App\Http\Controllers\OrderCommentController
class OrderCommentController extends CommentController
{
    public function store(CreateCommentRequest $request, Order $commentable) {
        return parent::store($commentable);
    }
}

# App\Http\Controllers\FulfillmentCommentController
class FulfillmentCommentController extends CommentController
{
    public function store(CreateCommentRequest $request, Fulfillment $commentable) {
        return parent::store($commentable);
    }
}

# Routes
Route::resource('order.comment', 'OrderCommentController');
Route::resource('fulfillments.comment', 'FulfillCommentController');

简单、灵活、可维护。

啊,错误的语言

没那么快:

Declaration of OrderCommentController::store(CreateCommentRequest $request, Order $commentable) should be compatible with CommentController::store(CreateCommentRequest $request, Commentable $commentable).



尽管覆盖方法参数在构造函数中工作得很好,但它根本不适用于其他方法!构造函数是 special cases .

我们可以在父类和子类中删除类型提示,并继续使用普通 ID 继续我们的生活。但在这种情况下,由于 Laravel 的隐式模型绑定(bind)仅适用于类型提示,因此我们的 Controller 不会自动加载模型。

好吧,也许在一个更好的世界。



?更新:见PHP 7.4对类型变化的支持 ?

显式路由模型绑定(bind)

那我们怎么办?

如果我们明确告诉路由器如何加载我们的 Commentable模型,我们可以只使用单独的 CommentController类(class)。 Laravel 的 explicit model binding通过将路由占位符(例如 {order})映射到模型类或自定义解析逻辑来工作。所以,虽然我们正在使用我们的单 CommentController我们可以根据他们的路线占位符为订单和履行使用单独的模型或解析逻辑。因此,我们放弃类型提示并依赖占位符。

对于资源 Controller ,占位符名称取决于您传递给 Route::resource 的第一个参数。方法。做一个 artisan route:list找出答案。

好的,让我们一起做:

# App\Providers\RouteServiceProvider::boot()
public function boot()
{
    // Map `{order}` route placeholder to the \App\Order model
    $this->app->router->model('order', \App\Order::class);

    // Map `{fulfillment}` to the \App\Fulfilment model
    $this->app->router->model('fulfillment', \App\Fulfilment::class);

    parent::boot();
}

您的 Controller 代码将是:

# App\Http\Controllers\CommentController
class CommentController extends Controller
{
    // Note that we have dropped the typehint here:
    public function store(CreateCommentRequest $request, $commentable) {
        // $commentable is either an \App\Order or a \App\Fulfillment
    }

    // Drop the typehint from other methods as well.
}

并且路由定义保持不变。

它比第一个解决方案更好,因为它不依赖于与很少更改的路由占位符相反的易于更改的 URL 段。它也是通用的,因为所有 {order} s 将解析为 \App\Order模型和所有 {fulfillment} s 到 App\Fulfillment .

我们可以改变第一个解决方案以使用路由参数而不是 URL 段。但是当 Laravel 提供给我们时,没有理由手动完成它。

是的,我知道,我也感觉不舒服。

关于php - 多个路由的相同 Laravel 资源 Controller ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46128476/

有关php - 多个路由的相同 Laravel 资源 Controller的更多相关文章

  1. ruby-on-rails - Rails 3 中的多个路由文件 - 2

    Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

  2. ruby-on-rails - 在 Ruby 中循环遍历多个数组 - 2

    我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代

  3. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

    我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

  4. ruby-on-rails - 渲染另一个 Controller 的 View - 2

    我想要做的是有2个不同的Controller,client和test_client。客户端Controller已经构建,我想创建一个test_clientController,我可以使用它来玩弄客户端的UI并根据需要进行调整。我主要是想绕过我在客户端中内置的验证及其对加载数据的管理Controller的依赖。所以我希望test_clientController加载示例数据集,然后呈现客户端Controller的索引View,以便我可以调整客户端UI。就是这样。我在test_clients索引方法中试过这个:classTestClientdefindexrender:template=>

  5. ruby-on-rails - Rails 应用程序中的 Rails : How are you using application_controller. rb 是新手吗? - 2

    刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr

  6. ruby-on-rails - rails : How to make a form post to another controller action - 2

    我知道您通常应该在Rails中使用新建/创建和编辑/更新之间的链接,但我有一个情况需要其他东西。无论如何我可以实现同样的连接吗?我有一个模型表单,我希望它发布数据(类似于新View如何发布到创建操作)。这是我的表格prohibitedthisjobfrombeingsaved: 最佳答案 使用:url选项。=form_for@job,:url=>company_path,:html=>{:method=>:post/:put} 关于ruby-on-rails-rails:Howtomak

  7. ruby - 多个属性的 update_column 方法 - 2

    我有一个具有一些属性的模型:attr1、attr2和attr3。我需要在不执行回调和验证的情况下更新此属性。我找到了update_column方法,但我想同时更新三个属性。我需要这样的东西:update_columns({attr1:val1,attr2:val2,attr3:val3})代替update_column(attr1,val1)update_column(attr2,val2)update_column(attr3,val3) 最佳答案 您可以使用update_columns(attr1:val1,attr2:val2

  8. ruby-on-rails - 在 ruby​​ .gemspec 文件中,如何指定依赖项的多个版本? - 2

    我正在尝试修改当前依赖于定义为activeresource的gem:s.add_dependency"activeresource","~>3.0"为了让gem与Rails4一起工作,我需要扩展依赖关系以与activeresource的版本3或4一起工作。我不想简单地添加以下内容,因为它可能会在以后引起问题:s.add_dependency"activeresource",">=3.0"有没有办法指定可接受版本的列表?~>3.0还是~>4.0? 最佳答案 根据thedocumentation,如果你想要3到4之间的所有版本,你可以这

  9. ruby - 如果指定键的值在数组中相同,如何合并哈希 - 2

    我有一个这样的哈希数组:[{:foo=>2,:date=>Sat,01Sep2014},{:foo2=>2,:date=>Sat,02Sep2014},{:foo3=>3,:date=>Sat,01Sep2014},{:foo4=>4,:date=>Sat,03Sep2014},{:foo5=>5,:date=>Sat,02Sep2014}]如果:date相同,我想合并哈希值。我对上面数组的期望是:[{:foo=>2,:foo3=>3,:date=>Sat,01Sep2014},{:foo2=>2,:foo5=>5:date=>Sat,02Sep2014},{:foo4=>4,:dat

  10. ruby - 安装 Ruby 时遇到问题(无法下载资源 "readline--patch") - 2

    当我尝试安装Ruby时遇到此错误。我试过查看this和this但无济于事➜~brewinstallrubyWarning:YouareusingOSX10.12.Wedonotprovidesupportforthispre-releaseversion.Youmayencounterbuildfailuresorotherbreakages.Pleasecreatepull-requestsinsteadoffilingissues.==>Installingdependenciesforruby:readline,libyaml,makedepend==>Installingrub

随机推荐