Recently had a project where I needed to get a CRUD admin up and running quick. My go to framework for CRUD is Django Admin. However Python/Django isn’t always an option for various technical or political reasons. In this case it had to be PHP. So I looked high and low for an out of the box framework that would allow editing of tables and thankfully Backpack Crud (docs, github) was there.
What is CRUD?
- C – create
- R – read
- U – update
- D – delete
The dirty secret:
We’re all just building CRUD apps. — I Am Devloper (@iamdevloper) April 13, 2014
I didn’t really want to roll my own admin interface (been there done that), so I went ahead and installed Backpack Crud.
After you get Composer installed, Laravel installed and Backpack Crud installed, you should be ready to start adding models / controllers.
Backpack Crud has pretty solid documentation included a nice getting started page. I’m doing this write up to to document the extra things I ran into (see example below).
First off, as of January 2018, I ran into dependency conflicts with Laravel 5.5 and Backpack Crud 3.3. I ended up going with this in my composer.json file:
"backpack/base": "0.7.24", "backpack/crud": "3.2.27", "laravel/framework": "5.4.*",
How to setup a Backpack Crud model:
- Create a new model/request/controller php artisan backpack:crud {name} (name is singular).
- Setup model, at a minimum the $fillable property.
- Setup request class with validation rules and messages.
- Setup controller with fields, columns and filters.
- Add a new route in web.php referencing the controller.
- Add to link to sidebar (sidebar.blade.php) to new controller.
1) Run the generator:
Say you need CRUD for table of colors with fields: name, sort order and hex code.
First run:
php artisan backpack:crud Color
That will auto generate the model, request and controller files. Then it is up to you to customize the contents of each file. You will also need to setup migrations for the new models.
2) Setup Model (app/models/Color.php):
The model is a straightforward Eloquent model with use CrudTrait;.
What tripped me up at first was you have to set the $fillable property on the model or else nothing shows up! So at a minimum you need to list all the columns you want the user to edit in $fillable.
<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Backpack\CRUD\CrudTrait; class Color extends Model { use CrudTrait; protected $table = 'colors'; protected $fillable = ['color_code', 'name', 'sort_order', 'hex_code']; }
3) Setup Request (app/Http/Requests/ColorRequest.php)
The validation rules for each file go under rules() and the friendly names for the columns go under attributes().
<?php namespace App\Http\Requests; use App\Http\Requests\Request; class ColorRequest extends \Backpack\CRUD\app\Http\Requests\CrudRequest { public function authorize() { // only allow updates if the user is logged in return \Auth::check(); } public function rules() { return [ 'name' => 'max:50', 'sort_order' => 'required|numeric|min:0|max:9999999', 'hex_code' => 'max:6', ]; } public function attributes() { return [ 'name' => 'Name', 'sort_order' => 'Sort Order', 'hex_code' => 'Hex Code', ]; } public function messages() { return [ // ]; } }
4) Setup Controller (app/Http/Controllers/Admin/ColorCrudController.php)
If you are setting up lots of controllers and copy / pasting watch out for the path to StoreRequest and UpdateRequest, it needs to match. IntelliJ hides the namespace and use section by default so I didn’t see that. I wasted some time tracking down some really strange errors with form validation labels.
The setFromDb() command is what wires up everything, but I found I had to comment that out and manually add all the fields (add/edit page) and columns (listing page) since the schema I was handed had the columns in a different order than made sense on the admin screens. For a really simple table setFromDb() is probably fine though.
<?php namespace App\Http\Controllers\Admin; use Backpack\CRUD\app\Http\Controllers\CrudController; // NOTE: change the requests to match your own file names if you need form validation use App\Http\Requests\ColorRequest as StoreRequest; use App\Http\Requests\ColorRequest as UpdateRequest; class ColorCrudController extends CrudController { public function setup() { $this->crud->setModel('App\Models\Color'); $this->crud->setRoute(config('backpack.base.route_prefix') . '/color'); $this->crud->setEntityNameStrings('Color', 'Colors'); $this->crud->setFromDb(); .....
5) Setup routes (web.php)
Route::group(['prefix' => 'admin', 'middleware' => ['admin'], 'namespace' => 'Admin'], function() { CRUD::resource('color', 'ColorCrudController'); // … add more CRUD:resource lines for your models here });
6) Add link to side bar (resources/views/vendor/backpack/base/inc/sidebar.blade.php)
<li><a href="{{ url(config('backpack.base.route_prefix', 'admin').'/color') }}"><i class="fa fa-table"></i> Colors</a></li>
What is great about Backpack Crud:
- It uses Laravel which is a well thought out PHP web framework that takes a lot of the pain out of PHP development. Laravel’s ORM Eloquent (also great) is central to Backpack Crud. Laravel also has excellent documentation and tutorials, including their Laracasts so it is really easy to get started.
- Backpack Crud understands Foreign Keys and can wire them to select2’s on the UI (even driven by ajax).
- You get a lot of control over how thing are displayed and formatted. It allows custom fields and columns.
- Has a mechanism for filtering data in the listing.
- Has a built in way to export the listing data to various formats (CSV, PDF, etc).
- Has a way to make a table read only.
- Has a user / permissions model (I did not play that part of it too much).
- A nice selection of baked in UI widgets (date picker, WYSIWYG editor, select2, etc).
- Has a built in revisions tracker for viewing a history of each record.
- Good documentation.
- Backpack Crud does come with a paid licensing model (free for non-commercial use only). It’s affordable and I like the fact that somebody is getting paid to maintain it.
Where I’d like to see Backpack Crud improve:
- When you wire up a model, you have to add routes and a menu link yourself. That was confusing at first because nothing was showing up. Would be nice if the menu would automatically include the models that have `use CrudTrait;`.
- Even though Backpack Crud understands foreign keys, you can’t edit child and parent records on the same screen. Django admin has a feature called inlines that lets you do that and it’s been available for over a decade. It is one of my favorite Django features. Yes inlines can get way out of hand if you have lots of child rows or more than a handful of relationships to manage. Still it is a major time saver and works really well.
- Getting a model wired up involves a lot of verbose code, some of which is repetitive or redundant. It requires you to declare the same field in several places (model, controller, requests, routes, and menu link). Seems like all Backpack Crud needs is a configuration wrapper around the existing functionality to make the implementation faster. That’s how Django admin works – you just give it a list of fields, link it to a modal, and it does the rest.
- A built in event log of who changed what, independent of revisions would be pretty neat. I ended up rolling my own, which wasn’t too bad because I was able to leverage Eloquent Observers.
- After we started using it we found select2 drop downs powered by a model were not sorting correctly. There is no way to specify default the sorting on a select2 (1-n relationship) drop down. In Laravel you can add a Global Scope that will add a default sort to all queries for a given model. That works (sort of), but it did cause conflicts on listing pages with ajax enabled and pagination (at least for me on MSSQL). So my solution was to convert all the select2 drop downs to select2_from_ajax and wire my own custom endpoints / routes / queries that had the right sorting. So at least there was a work around.
- The listing pages use DataTables.js which is a powerful but somewhat arcane jQuery based table plugin. For any kind of serious app you’ll probably end up having to override /backpack/crud/list.blade.php (copy it from vendor into resources/views/vendor/backpack/crud/list.blade.php) and hack your way through its setup.
- Related to DataTables.js, one gem I found was the saveState option which automatically remembers the current page number, page size, and sort order for the table – this is essential for a real life application with thousands of rows. Otherwise after adding or editing a record users are always dumped to page 1 with the default sort options (and that is really frustrating for them).
- Another DataTables.js feature I was asked to implement was a Jump To Page button where the user could type in a page number and it would take them straight to it. This tutorial was really helpful.