Laravel: 从一个控制器动态获取嵌套资源

Laravel: Dynamically retrieve nested resource from one controller

案例

我有两个对象,UserItem(现在但以后可能会更多)我可以使用以下 urls 模式附加文件:

/users/:user_id/attachments/:id
/items/:item_id/attachments/:id

为了保持 DRY,我只想使用一个唯一的控制器 (AttachmentsController) 来处理所有情况。

问题

使用独特的控制器,嵌套资源可以是 UserItem(或我想附加到它的任何其他对象)。有没有办法使用 RouteRequest 从该控制器动态检索这些资源(用户、项目...等...)?

理想情境

我的理想用例是能够检索 url 模式,例如 /users/:user_id/attachments/:id 这样我就可以动态查找要使用的对象(请参阅下面的 Rails 解决方案).当然,如果存在更好的解决方法,我会加入。

我也在考虑做一些类似 mixin 的事情,它可以动态检索该对象:

trait ResourceFinder
{
    public function behaveableFrom(Request $request, $id)
    {
        $patternWords = explode('/', $request->path());
        $classname = ucfirst(str_singular($words[0]));
        return $classname::findOrFail($id);
    }
}

等效Rails解

有关信息,我使用以下代码段在 Ruby 和 Rails 上介绍了完全相同的案例:

module Behaveable
  module ResourceFinder
    def behaveable
      klass, param = behaveable_class
      klass.find(params[param.to_sym]) if klass
    end
  end

  def behaveable_class
    params.each do |name, _value|
      if name =~ /(.+)_id$/
        model = name.match(%r{([^\/.]*)_id$})
        return model[1].classify.constantize, name
      end
    end
    nil
  end
end

建议?

你可以使用路由资源控制器。

Route::resource('user.attachments', 'AttachmentsController');

http://laravel.com/docs/5.1/controllers#restful-nested-resources 但是你想使用一个控制器,所以你有这样的选择: 1) 为 restful 控制器中的每个方法创建一些检查器来检查这是 Item 还是 User 2)用需要的方法创建一些AttachmentsBaseController,然后创建 2 个其他控制器 User\ItemController 将扩展基本控制器。通过这种方式,您可以定义基本工作流,如果某些内容会发生变化,则取决于 User\Item 您可以在相关控制器中重新定义所需的方法。

我终于找到了一个使用 mixin 的非常干净的解决方案,正如我在原始 post 中所说的那样。

routes.php

foreach (['users', 'menus'] as $resource) {
    Route::resource("$resource.attachments", 'AttachmentsController', ['except' => ['create', 'edit']]);
}

App\Http\Controllers\Traits\BehaveableFinder.php

<?php

namespace App\Http\Controllers\Traits;

use Illuminate\Http\Request;

trait BehaveableFinder
{
    /**
     * Get the behaveable object.
     *
     * @param  Request  $request
     * @param  int  $id
     * @return \Illuminate\Database\Eloquent\Model
     */
    public function behaveableFrom(Request $request, $behaveableId)
    {
        $context = explode('/', $request->path())[0];
        $class = 'App\Models\' . ucfirst(str_singular($context));
        return $class::findOrFail($behaveableId);
    }
}

App\Http\Controllers\AttachmentsController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Http\Requests;
use App\Http\Controllers\Controller;
use App\Http\Controllers\Traits\BehaveableFinder;

class AttachmentsController extends Controller
{
    use BehaveableFinder;

    /**
     * Get attachable's medias.
     *
     * GET http://domain.com/:attachable/:attachableId/attachments
     *
     * @param  Request  $request
     * @param  int      $attachableId
     * @return Response
     */
    public function index(Request $request, $attachableId)
    {
        $attachable = $this->behaveableFrom($request, $attachableId);
        return response()->json($attachable->medias, 200);
    }

    // ...rest of crud methods works the same way
}

这里的优点是这个 mixin 然后可以在任何其他多态上下文中使用,例如评论(可评论对象)。这种方法的局限性在于它仅适用于深度为 2 的嵌套级别。但是,通常最好避免在 REST 设计中使用过多的嵌套级别。

因为我认为总有改进的余地,如果您有任何其他方法,我很乐意看到,所以欢迎提出建议。