3
\$\begingroup\$

I've been designing and coding my Laravel API boilerplate for couple days now, and I'd like to hear some advice/improvement hints!

I'm pretty satisfied with the result, but I'm also aware, there might be (and surely are) some things that can be improved.

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 transformed array (I'm looking forward to implicit return types in PHP7!)

Next, every "entity" should have its 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? Is there anything that breaks SOLID principals? Is there anything I could improve on? Any breaks PSR-2 formatting?

\$\endgroup\$
  • \$\begingroup\$ What Laravel version are you using? \$\endgroup\$ – Marco Aurélio Deleu Nov 4 '15 at 13:29
  • \$\begingroup\$ This is version 5.1.11 \$\endgroup\$ – Jerguš Lejko Nov 4 '15 at 17:05
2
\$\begingroup\$
  • Why $repository and $transformer are static?
  • Seems like $storeRequest and $updateRequest are just validators - do you really need to have them static?
  • No type hinting for methods of abstract class Transformer ?
\$\endgroup\$
  • \$\begingroup\$ * not sure! they are only accessed within object context, so they should not be static * nope, same as 1. * I wanted to be able to pass them both array and collection, but I might change it to only acception Collection \$\endgroup\$ – Jerguš Lejko Sep 29 '15 at 10:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.