CRUD Controller for using in Laravel REST API | One way to get rid of routine controllers

Mikhail Fomichev
5 min readJul 26, 2023

In this tutorial, I will guide you through constructing the controller structure for using in your REST API, free from redundant code. This approach is particularly useful when your task involves creating multiple similar controllers each having a consistent set of features, such as pagination, sorting, various methods for loading relations, role-based rulesets, and more.

Firstly, we must identify recurring functionalities that can be optimized. To keep it simple for this tutorial, we’ll focus on four commonly used features: pagination, sorting, authenticated user, single model naming. Once you grasp the concept, you’re encouraged to broaden the scope and adapt the technique to suit the specific requirements of your project.

This tutorial assumes you’re beyond the beginner stage as a developer, so I won’t be covering the initial steps for setting up the project and tuning the API. Do keep in mind, however, that this methodology can be applied across any Laravel version.

Imagine we have four models: Users, Categories, Posts, and Comments. Each model necessitates CRUD (Create, Read, Update, Delete) operations. While Create, Update, and Delete operations are fairly straightforward, Read — particularly the index() method — may pose some complexity. The index() method should incorporate features like pagination, sorting, and loading relations, understanding the authed user, validating input data for all four models and etc.

We could certainly code these features into each controller for every model; however, there’s a more efficient path. Instead, we can enhance the parent controller with unique features tailored for each model, reducing code repetition and promoting maintainability.

Let’s start coding!

Create CRUD Controller

Navigate to app/Http/Controllers and create there a new one controller CrudController.php with this code

<?php

namespace App\Http\Controllers;

use Auth;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Database\Query\Builder;

class CrudController extends Controller
{
/**
* Using Model instance
*
* @var Model
*/
public $model;

/**
* Now user instance
*
* @var User
*/
public $user;

/**
* Default params for sort.
*
* @var array
*/
public $sort = [
'sortBy' => 'id',
'sortDirection' => 'desc'
];

/**
* Formated data for model
*
* @var array
*/
public $formData;

/**
* Per page param.
*
* @var
*/
public $per_page;

/**
* CRUD constructor
*
* @param $model Model
*/
public function __construct($model)
{
$this->middleware(function ($request, $next) use ($model) {
$this->user = Auth::guard('api')->user(); // You can change for your type of auth user defination
$this->model = $model;
$this->sort = $request->only(['sortDirection', 'sortBy']);
$this->per_page = (int) $request->input('per_page', 25);

return $next($request);
});
}

/**
* Display a listing of the resource.
*
* @return array
*/
public function index()
{
$this->model = $this->model->orderBy($this->sort['sortBy'], $this->sort['sortDirection']);
$this->model = $this->model->paginate($this->per_page);

return $this->model;
}

/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->model = $this->model::create($this->formData);

return $this->model;
}

/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
$this->model = $this->model::findOrfail($id);

return $this->model;
}

/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return Model
*/
public function update(Request $request, $id)
{
$this->model = $this->model::findOrfail($id);
$this->model->update($this->formData);

return $this->model;
}

/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
return $this->model::destroy($id);
}
}

Now we have a basic CRUD controller at our disposal, poised for future enhancements. As you evolve your project, you can readily augment this controller with additional features tailored to your specific needs.

Create Post model

Before we proceed, it’s crucial that we first establish the Post model. You can create this within the app/Models directory, naming the file Post.php. Please remember to set up a corresponding database for the Post model. Alternatively, feel free to utilize any pre-existing model from your project if you prefer.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'title',
'description',
'date_for_publication',
];
}

Create Post Controller

Let’s see on example of PostController.php how to use CRUD controller.

<?php

namespace App\Http\Controllers\Post;

use App\Models\Post;
use Illuminate\Http\Request;
use App\Http\Resources\PostResource;
use App\Http\Controllers\CrudController;

class PostController extends CrudController
{
/**
* Post constructor
*
* @param Post $model
*/
public function __construct(Post $model)
{
parent::__construct($model);
}

/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$this->model = parent::index();

return PostResource::collection($this->model);
}

/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store(Request $request)
{
$this->formData = [
'title' => $request->title,
'description' => $request->description,
'date_for_publication' => $request->date_for_publication,
];

$this->model = parent::store($request);
$this->model->load($this->modelLoads);

return new PostResource($this->model);
}

/**
* Display the specified resource.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function show($id)
{
$this->model = parent::show($id);

return new PostResource($this->model);
}

/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$this->formData = [
'title' => $request->title,
'description' => $request->description,
'date_for_publication' => $request->date_for_publication,
];

$this->model = parent::update($request, $id);
$this->model->load($this->modelLoads);

return new PostResource($this->model);
}

/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
return parent::destroy($id);
}
}

Take a look at our streamlined PostController. Despite its simplicity, it boasts features like pagination, sorting, and user authentication, not to mention the use of singular model naming. It’s also easy to add more features to this controller or others in a snap.

This method gets rid of needless repetition. There’s no more need to constantly specify the model in the code, check the user in each controller, or worry about pagination and sorting.

Furthermore, I use the CrudController to manage loading of related models, connect full-text search engines, validate input data, update shared data, and more.

I hope this tutorial was helpful.
Thank you for your time and attention!

If you’re interested, I could explain how to use a similar strategy for displaying common elements of the front-end.

--

--

Mikhail Fomichev

In love with CRM and CMS systems development for many years. My usual stack: PHP, Python, JavaScript with Laravel and VUE frameworks.