Register Custom Doctrine Column Type Mappings for Laravel Backpack CRUD
Iβve been using Laravel Backpack CRUD to build the backend for a personal application, and I must say - great tool! Generation of the CRUD part of the application is only an artisan command away! Reminds me of the Yii Auto generator.
However, I run into an issue with one of my database columns that has type JSON
. After running the backpack:crud
command, the UI for it wasnβt loading. Instead the error below was showing:
2017-01-02 09:28:12 [ERROR] (local): Doctrine\DBAL\DBALException: Unknown database type json requested, Doctrine\DBAL\Platforms\MySQL57Platform may not support it. in /path/to/laravel/app/vendor/doctrine/dbal/lib/Doctrine/DBAL/Platforms/AbstractPlatform.php:423
$
Upon doing some research (read extensive Googling/Github Issue checking), it turns out itβs a DBAL-related issue, affecting specific column types in MySQL (including enum
& json
-like columns).
For more details on why enum
, in particular, has some issues: http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/cookbook/mysql-enums.html
I came across a suggestion for the fix in the Laravel Backpack CRUD here: Register Custom Doctrine Column Type Mappings - and Iβve detailed how I implemented the fix in my application.
My fix involves:
- Implementing a modified version of the CrudController, which;
- Implements a modified CrudPanel class that includes addition of the custom column types mentioned in the fix
First, create a directory called CRUD
under the app directory of your Laravel application (you can call it anything you please, be sure to update the code below to match your naming), then create a new PHP file there which will have the modified CrudPanel class:
$ cd /path/to/laravel/app
$ mkdir app/CRUD
$ touch app/CRUD/CustomCrudPanel.php
# your path should look something like:
$ tree app/
app/
βββ Console
βΒ Β βββ Commands
βΒ Β βΒ Β βββ Inspire.php
βΒ Β βββ Kernel.php
βββ CRUD
βΒ Β βββ CustomCrudPanel.php
βββ Events
βΒ Β βββ Event.php
βββ Exceptions
βΒ Β βββ Handler.php
βββ Http
βΒ Β βββ Controllers
...
Next, create a custom CrudController file which will have our extended & modified CrudController class. I put mine in app/Http/Controllers/Admin/
- where the other Backpack CRUD controllers are.
$ cd /path/to/laravel/app
$ touch app/Http/Controllers/Admin/CustomCrudController.php
Put the code below in CustomCrudPanel.php
:
<?php
// update this to match your app's namespace
namespace MyApp\CRUD;
use Backpack\CRUD\CrudPanel;
use Backpack\CRUD\PanelTraits\AutoSet;
class CustomCrudPanel extends CrudPanel
{
use AutoSet {
// we're overriding the original method from the AutoSet trait
setFromDb as parentSetFromDb;
}
public function setFromDb()
{
// register custom column types
$this->addCustomDoctrineColumnTypes();
// call the parent method so that all attributes are initialized properly
$this->parentSetFromDb();
}
// this is the fix suggested from the Github Issue (https://goo.gl/wN7dEd) Thank you @zschuessler
public function addCustomDoctrineColumnTypes()
{
$dbPlatform = \Schema::getConnection()->getDoctrineSchemaManager()->getDatabasePlatform();
$dbPlatform->registerDoctrineTypeMapping('enum', 'string');
$dbPlatform->registerDoctrineTypeMapping('json', 'json_array');
}
}
Put the code below in CustomCrudController.php
:
<?php
// update this to match your app's namespace
namespace MyApp\Http\Controllers\Admin;
use Backpack\CRUD\app\Http\Controllers\CrudController;
use MyApp\CRUD\CustomCrudPanel as CrudPanel;
class CustomCrudController extends CrudController
{
public function __construct()
{
if (!$this->crud) {
$this->crud = new CrudPanel();
// call the setup function inside this closure to also have the request there
// this way, developers can use things stored in session (auth variables, etc)
$this->middleware(function ($request, $next) {
$this->request = $request;
$this->crud->request = $request;
$this->setup();
return $next($request);
});
}
}
}
Finally, in your auto-generated CrudController
class for the model with an enum
or json
column, update it to extend the CustomCrudController class instead.
E.g. if the CrudController
class with the json
column is called PlaylistCrudController
, it should look something like:
<?php
// update this to match your app's namespace
namespace MyApp\Http\Controllers\Admin;
// use the CustomCrudController class instead of Backpack\CRUD\app\Http\Controllers\CrudController
use MyApp\Http\Controllers\Admin\CustomCrudController as CrudController;
use MyApp\Http\Requests\PlaylistRequest as StoreRequest;
use MyApp\Http\Requests\PlaylistRequest as UpdateRequest;
class PlaylistCrudController extends CrudController
{
public function setUp()
{
$this->crud->setModel('MyApp\Models\Playlist');
$this->crud->setRoute('admin/playlist');
$this->crud->setEntityNameStrings('playlist', 'playlist');
$this->crud->setFromDb();
}
public function store(StoreRequest $request)
{
// your additional operations before save here
$redirect_location = parent::storeCrud();
return $redirect_location;
}
public function update(UpdateRequest $request)
{
// your additional operations before save here
$redirect_location = parent::updateCrud();
return $redirect_location;
}
}
If all goes well, you should be able to view the CRUD for our troublesome model.
Credits for the suggested fix go to @zschuessler from this Github Issue on the Laravel Backpack CRUD.
I also ended up learning a lot about PHP Traits that I didnβt know before.
Thank you Laravel Backpack CRUD project for such an amazing tool!
If you have any comments/suggestions/improvements, feel free to reach out in the comments.
Happy Coding! (And all the best this year?!)