Laravel: 从一个控制器动态获取嵌套资源
Laravel: Dynamically retrieve nested resource from one controller
案例
我有两个对象,User
和 Item
(现在但以后可能会更多)我可以使用以下 urls 模式附加文件:
/users/:user_id/attachments/:id
/items/:item_id/attachments/:id
为了保持 DRY,我只想使用一个唯一的控制器 (AttachmentsController
) 来处理所有情况。
问题
使用独特的控制器,嵌套资源可以是 User
或 Item
(或我想附加到它的任何其他对象)。有没有办法使用 Route
或 Request
从该控制器动态检索这些资源(用户、项目...等...)?
理想情境
我的理想用例是能够检索 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 设计中使用过多的嵌套级别。
因为我认为总有改进的余地,如果您有任何其他方法,我很乐意看到,所以欢迎提出建议。
案例
我有两个对象,User
和 Item
(现在但以后可能会更多)我可以使用以下 urls 模式附加文件:
/users/:user_id/attachments/:id
/items/:item_id/attachments/:id
为了保持 DRY,我只想使用一个唯一的控制器 (AttachmentsController
) 来处理所有情况。
问题
使用独特的控制器,嵌套资源可以是 User
或 Item
(或我想附加到它的任何其他对象)。有没有办法使用 Route
或 Request
从该控制器动态检索这些资源(用户、项目...等...)?
理想情境
我的理想用例是能够检索 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 设计中使用过多的嵌套级别。
因为我认为总有改进的余地,如果您有任何其他方法,我很乐意看到,所以欢迎提出建议。