I've been designing and coding my Laravel API boilerplate for couple days now, and I'd like to hear some advices/improvement hints from you guys!
I'm pretty satisfied with the result, but I'm also aware, there might be (and surely are) some things that can be improved.
So, basic concept:
Every controller should extend my App\Http\Controllers\ApiController (actually, I'm thinking about moving this controller to App\Support\Http\Controllers\ApiController):
<?php
namespace App\Http\Controllers;
use App\Support\Http\Controller;
use Illuminate\Http\Request;
abstract class ApiController extends Controller
{
protected static $repository = null;
protected static $transformer = null;
protected static $storeRequest = null;
protected static $updateRequest = null;
public function index()
{
$instances = $this->repository()->all();
$transformed = $this->transformer()->collection($instances);
return response()->ok($transformed);
}
public function show($id)
{
$instance = $this->repository()->show($id);
$transformed = $this->transformer()->item($instance);
return response()->ok($transformed);
}
public function store(Request $request)
{
$this->validateStoreRequest();
$this->repository()->store($request->input());
return response()->created();
}
public function update(Request $request, $id)
{
$this->validateStoreRequest();
$this->repository()->update($id, $request->input());
return response()->updated();
}
public function destroy($id)
{
$this->repository()->destroy($id);
return response()->destroyed();
}
private function validateStoreRequest()
{
app(
static::$storeRequest ?: app('naming')->parse(static::class)->storeRequest()
);
}
private function validateUpdateRequest()
{
app(
static::$storeRequest ?: app('naming')->parse(static::class)->updateRequest()
);
}
private function transformer()
{
return app(
static::$transformer ?: app('naming')->parse(static::class)->transformer()
);
}
private function repository()
{
return app(
static::$repository ?: app('naming')->parse(static::class)->repository()
);
}
}
As you can see, every "entity" should have it's own transformer with should extends my App\Support\Transformer:
<?php
namespace App\Support;
abstract class Transformer
{
abstract public function item($item);
public function collection($collection)
{
$transformed = [];
foreach($collection as $item) {
$transformed[] = $this->item($item);
}
return $transformed;
}
}
Transfomer::item($item) basically returns a tranformed array ( looking forward to implicit return types in PHP7!! )
Next, every "entity" should have it's own repository, which should extend my App\Support\Repository:
<?php
namespace App\Support;
abstract class Repository
{
public function all()
{
$model = $this->model();
return $model::all();
}
public function show($id)
{
$model = $this->model();
return $model::findOrFail($id);
}
public function store(array $attributes)
{
$model = $this->model();
return $model::create($attributes);
}
public function update($id, array $attributes)
{
$model = $this->model();
return $model::findOrFail($id)
->update($attributes);
}
public function destroy($id)
{
$model = $this->model();
return $model::findOrFail($id)
->delete();
}
private function model()
{
return app('naming')->parse(static::class)
->model();
}
}
And of course, every "entity" must have it's own model, but I haven't created any BaseModel class yet. I do not find it necessary right now.
As you can see in the ApiController, I've created some custom response macros that correspond to HTTP status codes.
You might be curious about app('naming'), but all that does, is that is transforms any class name (also full namespaced) to corresponding class for given "entity".
Also, any validation is managed through Laravel's FormRequest class.
All in all, are there any approaches, that you consider badly implemented? Anything that breaks SOLID principals? Anything I could improve? Any breaks PSR-2 formatting?
Thank you, for all your responses. Looking forward to your suggestions :)