Create Admin Panel with Laravel Backpack Part 3
In today article “Create Admin Panel with Laravel Backpack Part 3” we will talk about how to create a quick and powerful admin panel. For this article we gonna update our models, controllers and requests to be able to perform CRUD.
In the first part we talk about how to setup basic installation and configuration of Laravel Backpack. In the second part we setup the database and create models, controller. We also created routes and menu. Now we will create the basic crud for a book database.
If you didn’t follow the previous article be sure you got them :
- Create admin panel with laravel backpack part 1
- Create admin panel with laravel backpack part 2 (Setup database and create models and controllers)
Setup Models and relationship
The second part we setup database and create models and controllers even routes but if you tried to save a model you will have an error. You get a mass assignment error and we gonna fix that right now.
If you wish to know why you get this error take a look at the laravel docs. The simple explanation is laravel is protecting you from user input in case of the user send you bad request or edit a request.
Authors
Let’s begin with our first model authors and let’s make it look like this.
class Author extends Model { use CrudTrait; /* |-------------------------------------------------------------------------- | GLOBAL VARIABLES |-------------------------------------------------------------------------- */ protected $table = 'authors'; // protected $primaryKey = 'id'; // public $timestamps = false; // protected $guarded = ['id']; protected $fillable = ['title','slug','biography']; // protected $hidden = []; // protected $dates = []; /* |-------------------------------------------------------------------------- | FUNCTIONS |-------------------------------------------------------------------------- */ /* |-------------------------------------------------------------------------- | RELATIONS |-------------------------------------------------------------------------- */ public function books(){ return $this->hasMany('App\Models\Book'); } /* |-------------------------------------------------------------------------- | SCOPES |-------------------------------------------------------------------------- */ /* |-------------------------------------------------------------------------- | ACCESORS |-------------------------------------------------------------------------- */ /* |-------------------------------------------------------------------------- | MUTATORS |-------------------------------------------------------------------------- */ }
Basically here we just add the field we want to be mass-assignable in the field array and we define the relationship with books. To be concise and write clean code it’s recommended to put all you relationship closely, same things with scopes, accesors and mutators.
At this stage we are able to perform basic crud operations but what if we need to make some validation. For example if we want the title no longer than 100 characters and we want it required.
We will do that in the request file create with the models. The request file is located in app/Http/Request/ModelRequest.php
so for the authors model the request file is app/Http/Request/AuthorRequest.php
. Let’s edit it and add some rules and message.
class AuthorRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { // only allow updates if the user is logged in return backpack_auth()->check(); } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'name' => 'required|min:2|max:100', 'slug' => 'required' ]; } /** * Get the validation attributes that apply to the request. * * @return array */ public function attributes() { return [ // ]; } /** * Get the validation messages that apply to the request. * * @return array */ public function messages() { return [ // ]; } }
So it’s the basic laravel validation, the functions rules is where you defined your validation rules. The function messages() is where you define the message to show when the rules is not valid.
This Request is use both for create and update. If you need to setup different rules for update you have to duplicate this request and change the rules. You need to change the request class in the controller too. We will experiment that later, just know it’s simple and possible.

And the list is ready too you can make a search and we will customize those later.

Books
So the others models is basically the same process. First you make fix the fillable array and make relationship.
app/Models/Book.php
class Book extends Model { use CrudTrait; /* |-------------------------------------------------------------------------- | GLOBAL VARIABLES |-------------------------------------------------------------------------- */ protected $table = 'books'; protected $primaryKey = 'id'; // public $timestamps = false; // protected $guarded = ['id']; protected $fillable = ['title','author','author_id','author_bio', 'title_slug','isbn13', 'isbn10','price','format','publisher','pubdate','edition','lexile','subjects','pages', 'dimensions','overview','synopsis','excerpt','toc','editorial_reviews']; // protected $hidden = []; protected $dates = ['pubdate']; /* |-------------------------------------------------------------------------- | FUNCTIONS |-------------------------------------------------------------------------- */ /* |-------------------------------------------------------------------------- | RELATIONS |-------------------------------------------------------------------------- */ public function author(){ return $this->belongsTo('App\Models\Author'); } public function categories(){ return $this->belongsToMany('App\Models\Category','book_category','book_id','category_id'); } public function subcategories(){ return $this->belongsToMany('App\Models\Subcategory','book_category','book_id','subcategory_id'); } /* |-------------------------------------------------------------------------- | SCOPES |-------------------------------------------------------------------------- */ /* |-------------------------------------------------------------------------- | ACCESORS |-------------------------------------------------------------------------- */ /* |-------------------------------------------------------------------------- | MUTATORS |-------------------------------------------------------------------------- */ }
app/Http/Request/BookRequest.php
class BookRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { // only allow updates if the user is logged in return backpack_auth()->check(); } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'title' => 'required|min:2|max:255', 'author' => 'required', 'author_id' => 'required', 'title_slug' => 'required', 'isbn13' => 'required|min:13|max:13', 'isbn10' => 'required|min:10|max:10', 'format' => 'required', 'publisher' => 'required', 'pages' => 'required|integer' ]; } /** * Get the validation attributes that apply to the request. * * @return array */ public function attributes() { return [ // ]; } /** * Get the validation messages that apply to the request. * * @return array */ public function messages() { return [ // ]; } }
A validation example in BookRequest.php
is for isbn13. We know that we only need 13 characters so we set it required and min, max 13.
Subject
app/Models/Subject.php
class Subject extends Model { use CrudTrait; /* |-------------------------------------------------------------------------- | GLOBAL VARIABLES |-------------------------------------------------------------------------- */ protected $table = 'subjects'; protected $primaryKey = 'id'; // public $timestamps = false; // protected $guarded = ['id']; protected $fillable = ['title','slug']; // protected $hidden = []; // protected $dates = []; /* |-------------------------------------------------------------------------- | FUNCTIONS |-------------------------------------------------------------------------- */ /* |-------------------------------------------------------------------------- | RELATIONS |-------------------------------------------------------------------------- */ public function categories(){ return $this->hasMany('App\Models\Category'); } /* |-------------------------------------------------------------------------- | SCOPES |-------------------------------------------------------------------------- */ /* |-------------------------------------------------------------------------- | ACCESORS |-------------------------------------------------------------------------- */ /* |-------------------------------------------------------------------------- | MUTATORS |-------------------------------------------------------------------------- */ }
app/Http/Request/SubjectRequest.php
class SubjectRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { // only allow updates if the user is logged in return backpack_auth()->check(); } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'title' => 'required|min:2|max:255', 'slug' => 'required' ]; } /** * Get the validation attributes that apply to the request. * * @return array */ public function attributes() { return [ // ]; } /** * Get the validation messages that apply to the request. * * @return array */ public function messages() { return [ // ]; } }
Category
app/Models/Category.php
class Category extends Model { use CrudTrait; /* |-------------------------------------------------------------------------- | GLOBAL VARIABLES |-------------------------------------------------------------------------- */ protected $table = 'categories'; protected $primaryKey = 'id'; // public $timestamps = false; // protected $guarded = ['id']; protected $fillable = ['title','subject_id','slug','count']; // protected $hidden = []; // protected $dates = []; /* |-------------------------------------------------------------------------- | FUNCTIONS |-------------------------------------------------------------------------- */ /* |-------------------------------------------------------------------------- | RELATIONS |-------------------------------------------------------------------------- */ public function subject(){ return $this->belongsTo('App\Models\Subject'); } public function subcategories(){ return $this->hasMany('App\Models\Subcategory'); } /* |-------------------------------------------------------------------------- | SCOPES |-------------------------------------------------------------------------- */ /* |-------------------------------------------------------------------------- | ACCESORS |-------------------------------------------------------------------------- */ /* |-------------------------------------------------------------------------- | MUTATORS |-------------------------------------------------------------------------- */ }
app/Http/Requests/CategoryRequest.php
class CategoryRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { // only allow updates if the user is logged in return backpack_auth()->check(); } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'title' => 'required|min:2|max:255', 'subject_id' => 'required', 'slug' => 'required' ]; } /** * Get the validation attributes that apply to the request. * * @return array */ public function attributes() { return [ // ]; } /** * Get the validation messages that apply to the request. * * @return array */ public function messages() { return [ // ]; } }

Subcategory
app/Models/Subcategory.php
class Subcategory extends Model { use CrudTrait; /* |-------------------------------------------------------------------------- | GLOBAL VARIABLES |-------------------------------------------------------------------------- */ protected $table = 'subcategories'; // protected $primaryKey = 'id'; // public $timestamps = false; // protected $guarded = ['id']; protected $fillable = ['title', 'category_id','slug','count']; // protected $hidden = []; // protected $dates = []; /* |-------------------------------------------------------------------------- | FUNCTIONS |-------------------------------------------------------------------------- */ /* |-------------------------------------------------------------------------- | RELATIONS |-------------------------------------------------------------------------- */ public function category(){ return $this->belongsTo('App\Models\Category'); } /* |-------------------------------------------------------------------------- | SCOPES |-------------------------------------------------------------------------- */ /* |-------------------------------------------------------------------------- | ACCESORS |-------------------------------------------------------------------------- */ /* |-------------------------------------------------------------------------- | MUTATORS |-------------------------------------------------------------------------- */ }
app/Http/Requests/SubcategoryRequest.php
class SubcategoryRequest extends FormRequest { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { // only allow updates if the user is logged in return backpack_auth()->check(); } /** * Get the validation rules that apply to the request. * * @return array */ public function rules() { return [ 'title' => 'required|min:2|max:255', 'category_id' => 'required', 'slug' => 'required' ]; } /** * Get the validation attributes that apply to the request. * * @return array */ public function attributes() { return [ // ]; } /** * Get the validation messages that apply to the request. * * @return array */ public function messages() { return [ // ]; } }
Setup Controllers
After editing the models and request our form still not friendly. We would like to edit the column name or adding some calculated columns. To do all of those things we need to edit the controllers. Let’s take an example the model Category it’s related to Subcategory and Subject.

We would like to have a list of subject then choose one. Let’s see how we can do that.
class CategoryCrudController extends CrudController { public function setup() { /* |-------------------------------------------------------------------------- | CrudPanel Basic Information |-------------------------------------------------------------------------- */ $this->crud->setModel('App\Models\Category'); $this->crud->setRoute(config('backpack.base.route_prefix') . '/category'); $this->crud->setEntityNameStrings('category', 'categories'); /* |-------------------------------------------------------------------------- | CrudPanel Configuration |-------------------------------------------------------------------------- */ $this->crud->setColumns(['title', 'subject_id','slug', 'count']); $this->crud->setColumnDetails('subject_id',[ 'name' => 'subject_id', 'type' => 'select', 'label' => 'Subject', 'entity' => 'subject', 'attribute' => 'title', 'model' => 'App\Models\Subject', ]); $this->crud->addField([ 'name' => 'title', 'type' => 'text', 'label' => "Title" ]); $this->crud->addField([ 'name' => 'subject_id', 'type' => 'select2', 'label' => 'Subject', 'entity' => 'subject', 'attribute' => 'title', 'model' => 'App\Models\Subject', 'options' => (function ($query) { return $query->orderBy('title', 'ASC')->get(); }) ]); $this->crud->addField([ 'name' => 'slug', 'type' => 'text', 'label' => "Slug" ]); $this->crud->addField([ 'name' => 'count', 'type' => 'number', 'label' => 'Number of entity' ]); // add asterisk for fields that are required in CategoryRequest $this->crud->setRequiredFields(StoreRequest::class, 'create'); $this->crud->setRequiredFields(UpdateRequest::class, 'edit'); } public function store(StoreRequest $request) { // your additional operations before save here $redirect_location = parent::storeCrud($request); // your additional operations after save here // use $this->data['entry'] or $this->crud->entry return $redirect_location; } public function update(UpdateRequest $request) { // your additional operations before save here $redirect_location = parent::updateCrud($request); // your additional operations after save here // use $this->data['entry'] or $this->crud->entry return $redirect_location; } }
Let’s explain each part to know what is the concern. The first things we change in the setup() function we remove the instruction $this-crud->setFromDb()
. We replace it with this line :
$this->crud->setColumns(['title', 'subject_id','slug', 'count']);
This line tell our controller to use that column in that order to display our list. We can change the order by just changing the place of column in the array. There’s more complex way to order the column we will meet them later.
The second things we do is customize the column named subject_id with this code:
$this->crud->setColumnDetails('subject_id',[ 'name' => 'subject_id', 'type' => 'select', 'label' => 'Subject', 'entity' => 'subject', 'attribute' => 'title', 'model' => 'App\Models\Subject', ]);
In this line of code we tell the subject_id how it should display. The entity attribute is the name of the relationship. The attribute is the value you actually want to show and the model is the model class. You can find the list of column type in the docs.

The third and last things is we customize the form field. We add a field for each column but let take a look at the most complex.
$this->crud->addField([ 'name' => 'subject_id', 'type' => 'select2', 'label' => 'Subject', 'entity' => 'subject', 'attribute' => 'title', 'model' => 'App\Models\Subject', 'options' => (function ($query) { return $query->orderBy('title', 'ASC')->get(); }) ]);
It’s basically the same as the column details the difference is the type. The type is select2 there’s a bunch of field type. You can find them in the docs. The last attribute options tell how to show the list. In this example we make it order the list by name.

You can find the code of this project from our GitHub repository here Link
Conclusion
Here is the end of the article “Create Admin Panel with Laravel Backpack Part 3” witch is the third part of this series. In this article we customize the models and make validations. We update our controllers to edit column name and add custom field. As usual if you get any trouble just leave a comment.
1 Response
[…] (Setup model and relationship )Create admin panel with laravel backpack part 3 […]