[英]Laravel - Add additional information to route
目前,我正在一個項目中嘗試創建RESTful API。 該API使用一些默認類(例如ResourceController
)來實現基本行為,這些基本行為可以在需要時被覆蓋。
假設我們有一個API資源路由:
Route::apiResource('posts', 'ResourceController');
該路由將利用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;
}
}
如您所知,我們沒有使用模型路由綁定,而是使用存儲庫來執行邏輯。
您還可以看到,我們使用了一些骯臟的邏輯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實例的示例,但是如果您走這條路,則需要將所有實例化移出Controller構造函數。
在Laravel的源代碼中進行了大量搜索和深入研究后,我發現ResourceRegistrar
的getResourceAction
方法處理傳遞給該路線的選項。
進一步的搜索將我帶到了這篇文章中 ,其他人已經設法擴展了此注冊器,並添加了一些自定義功能。
我的自定義注冊商看起來像:
<?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;
}
}
不要忘記綁定AppServiceProvider
:
$registrar = new ResourceRegistrar($this->app['router']);
$this->app->bind('Illuminate\Routing\ResourceRegistrar', function () use ($registrar) {
return $registrar;
});
該自定義注冊商允許以下操作:
Route::apiResource('posts', 'ResourceController', [
'model' => Post::class
]);
最后,我們能夠獲得模型類:
$resourceModelClass = $request->route()->getAction('model');
不再有駭客的網址解析邏輯!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.