Php Laravel-为路线添加其他信息
目前我正在从事一个项目,我们正在尝试创建一个RESTful API。此API使用一些默认类,例如Php Laravel-为路线添加其他信息,php,laravel,rest,api,routes,Php,Laravel,Rest,Api,Routes,目前我正在从事一个项目,我们正在尝试创建一个RESTful API。此API使用一些默认类,例如ResourceController,用于在需要时可以覆盖的基本行为 假设我们有一个API资源路由: Route::apiResource('posts', 'ResourceController'); 此路由将使用资源控制器: namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Repositories\Res
ResourceController
,用于在需要时可以覆盖的基本行为
假设我们有一个API资源路由:
Route::apiResource('posts', 'ResourceController');
此路由将使用资源控制器
:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Repositories\ResourceRepository;
class ResourceController extends Controller
{
/**
* The resource class.
*
* @var string
*/
private $resourceClass = '\\App\\Http\\Resources\\ResourceResource';
/**
* The resource model class.
*
* @var string
*/
private $resourceModelClass;
/**
* The repository.
*
* @var \App\Repositories\ResourceRepository
*/
private $repository;
/**
* ResourceController constructor.
*
* @param \Illuminate\Http\Request $request
* @return void
*/
public function __construct(Request $request)
{
$this->resourceModelClass = $this->getResourceModelClass($request);
$this->repository = new ResourceRepository($this->resourceModelClass);
$exploded = explode('\\', $this->resourceModelClass);
$resourceModelClassName = array_last($exploded);
if (!empty($resourceModelClassName)) {
$resourceClass = '\\App\\Http\\Resources\\' . $resourceModelClassName . 'Resource';
if (class_exists($resourceClass)) {
$this->resourceClass = $resourceClass;
}
}
}
...
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->validate($request, $this->getResourceModelRules());
$resource = $this->repository->create($request->all());
$resource = new $this->resourceClass($resource);
return response()->json($resource);
}
/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
$resource = $this->repository->show($id);
$resource = new $this->resourceClass($resource);
return response()->json($resource);
}
...
/**
* Get the model class of the specified resource.
*
* @param \Illuminate\Http\Request $request
* @return string
*/
private function getResourceModelClass(Request $request)
{
if (is_null($request->route())) return '';
$uri = $request->route()->uri;
$exploded = explode('/', $uri);
$class = str_singular($exploded[1]);
return '\\App\\Models\\' . ucfirst($class);
}
/**
* Get the model rules of the specified resource.
*
* @param \Illuminate\Http\Request $request
* @return string
*/
private function getResourceModelRules()
{
$rules = [];
if (method_exists($this->resourceModelClass, 'rules')) {
$rules = $this->resourceModelClass::rules();
}
return $rules;
}
}
$registrar = new ResourceRegistrar($this->app['router']);
$this->app->bind('Illuminate\Routing\ResourceRegistrar', function () use ($registrar) {
return $registrar;
});
正如您可能知道的,我们没有使用模型路由绑定,而是使用存储库来执行逻辑
您还可以看到,我们使用了一些脏逻辑,getResourceModelClass()
,来确定在上/与上执行逻辑所需的模型类。这种方法不是很灵活,并且限制了应用程序的目录结构(非常讨厌)
解决方案可以是在注册路由时添加一些关于模型类的信息。这可能看起来像:
Route::apiResource('posts', 'ResourceController', [
'modelClass' => Post::class
]);
然而,看起来这是不可能的
有没有人对如何使这项工作或如何使我们的逻辑更加清晰和灵活有任何建议。灵活性和易用性是重要因素。最好的方法是将
ResourceController
重构为一个抽象类,并为每个资源使用一个单独的控制器来扩展它
我很确定在路由文件中没有传递上下文信息的方法
但是,您可以将存储库的数量添加到您的控制器。这通常是一个很好的实践,但是依靠URL来解决这个问题是非常困难的
您必须将所有依赖项放在构造函数中:
public function __construct(string $modelPath, ResourceRepository $repo // ...)
{
$this->resourceModelClass = $this->modelPath;
$this->repository = $repo;
// ...
}
并在服务提供商中执行此操作:
use App\Repositories\ResourceRepository;
use App\Http\Controllers\ResourceController;
// ... model imports
// ...
public function boot()
{
if (request()->path() === 'posts') {
$this->app->bind(ResourceRepository::class, function ($app) {
return new ResourceRepository(new Post);
});
$this->app->when(ResourceController::class)
->needs('$modelPath')
->give(Post::class);
} else if (request()->path() === 'somethingelse') {
// ...
}
}
这将给您带来更多的灵活性,但同样地,依赖纯URL路径是有问题的
我刚刚展示了一个绑定模型路径和绑定Repo实例的示例,但是如果你沿着这条路走下去,你会希望将所有实例化移出控制器构造函数。在Laravel的源代码中进行了大量搜索和深入研究后,我发现了传递给路由的选项句柄中的方法 进一步的搜索使我找到了其他人已经设法扩展此注册器并添加一些自定义功能的地方 我的自定义注册器看起来像:
<?php
namespace App\Http\Routing;
use Illuminate\Routing\ResourceRegistrar as IlluResourceRegistrar;
class ResourceRegistrar extends IlluResourceRegistrar
{
/**
* Get the action array for a resource route.
*
* @param string $resource
* @param string $controller
* @param string $method
* @param array $options
* @return array
*/
protected function getResourceAction($resource, $controller, $method, $options)
{
$action = parent::getResourceAction($resource, $controller, $method, $options);
if (isset($options['model'])) {
$action['model'] = $options['model'];
}
return $action;
}
}
此自定义注册器允许以下操作:
Route::apiResource('posts', 'ResourceController', [
'model' => Post::class
]);
最后,我们可以得到我们的模型类:
$resourceModelClass = $request->route()->getAction('model');
没有黑客的url解析逻辑了 谢谢你的回答。扩展资源控制器确实是一种选择。然而,我们是懒惰的开发人员,不喜欢在添加新内容时重复操作。我们希望能够添加一条新路线,就是这样。我们可能需要考虑扩展route类或其他东西。