Я создаю RESTful API, используя Laravel 5.
Попытка держать контроллеры Http как можно более минимальными, поэтому я использую сервисный уровень (и репозитории) для обработки большей части логики.
Поскольку большинство контроллеров имеют схожие методы (например, show
, index
, update
), я написал некоторые черты, которые обрабатывают каждый. Поскольку они говорят напрямую со службой, я могу использовать их для каждого контроллера.
Например:
<?php namespace API\Http\Controllers\Restful;
trait UpdateTrait
{
protected $updater;
public function update($itemID)
{
if (!$this->updater->authorize($itemID)) {
return response(null, 401);
}
if (!$this->updater->exists($itemID)) {
return response(null, 404);
}
$data = $this->request->all();
$validator = $this->updater->validator($data);
if ($validator->fails()) {
return response($validator->messages(), 422);
}
$this->updater->update($itemID, $data);
return response(null, 204);
}
}
Поскольку все контроллеры имеют одни и те же черты, все они могут зависеть от одного интерфейса.
Например:
<?php namespace API\Services\Interfaces;
interface UpdaterServiceInterface
{
public function validator(array $data);
public function exists($itemID);
public function update($itemID, array $data);
public function authorize($itemID);
}
Однако это вызывает несколько проблем с автоматической инъекцией зависимостей.
1) Я должен использовать привязку к контексту:
$this->app->when("API\Http\Controllers\ThingController")
->needs("API\Services\Interfaces\UpdateServiceInterface")
->give("API\Services\Things\ThingUpdateServiceInterface")
Это само по себе не проблема, хотя это и приводит к довольно большому коду поставщика услуг, что не является идеальным. Тем не менее, это означает, что я не могу использовать метод инъекции, так как разрешение автоматической зависимости не работает для методов контроллера при использовании связывания контекста: я просто возвращаю сообщение could not instantiate API\Services\Interfaces\UpdateServiceInterface
.
Это означает, что конструктор контроллера должен обрабатывать всю инъекцию зависимостей, которая становится довольно грязной:
class ThingsController extends Controller
{
use Restful\IndexTrait,
Restful\ShowTrait,
Restful\UpdateTrait,
Restful\PatchTrait,
Restful\StoreTrait,
Restful\DestroyTrait;
public function __construct(
Interfaces\CollectionServiceInterface $collection,
Interfaces\ItemServiceInterface $item,
Interfaces\CreatorServiceInterface $creator,
Interfaces\UpdaterServiceInterface $updater,
Interfaces\PatcherServiceInterface $patcher,
Interfaces\DestroyerServiceInterface $destroyer
) {
$this->collection = $collection;
$this->item = $item;
$this->creator = $creator;
$this->updater = $updater;
$this->patcher = $patcher;
$this->destroyer = $destroyer;
}
}
Это нехорошо - трудно тестировать, и все эти зависимости должны быть созданы, даже если используется только один из них.
Но я не могу придумать, как лучше обойти его.
Я мог бы использовать более конкретный интерфейс, например. ThingUpdateServiceInterface
, (тогда мне не понадобилось бы контекстное связывание и могло бы непосредственно вставляться в черты), но тогда у меня было бы много интерфейсов, которые отличаются только по имени. Что кажется глупым.
Другая альтернатива, о которой я думал, заключалась в использовании множества меньших контроллеров, поэтому Things\UpdateController
и Things\ShowController
- по крайней мере, так ненужные зависимости не будут возникать каждый раз.
Или, может быть, попытка абстрагироваться от использования черт - это неправильный способ сделать что-то. Черты иногда кажутся похожими на потенциально анти-шаблон.
Любые советы будут оценены.