Backpack for Laravel

Backpack for Laravel

A collection of Laravel packages to build a custom admin panel.

Documentation    Support

Advanced Features

In addition the usual CRUD functionality, Backpack also allows you to do a few more complicated things:

Reordering and nesting items

If your model has the proper fields (parent_id, lft, rgt, depth), you can use the built-in reorder functionality. All you need to do is enable it in the EntityCrudController's constructor:

$this->crud->enableReorder('label_name', MAX_TREE_LEVEL);
$this->crud->allowAccess('reorder');

*Note: MAX_TREE_LEVEL is the maximum number of nesting elements from parents. For infinit levels, you should set it to 0

For the enableReorder function, the parameters are the db column you need to show on the elements and the number of levels you will allow nesting to (specify 1 for no nesting, just reordering).

This will be your result:

Media Library (file manager)

The file manager used is elFinder. It’s integrated in Backpack into:

  • TinyMCE (as CRUD “tinymce” fieldtype)
  • CKEditor (as CRUD “ckeditor” fieldtype)
  • CRUD “browse” fieldtype
  • standalone, at the your-project/admin/elfinder route (for the integration, barryvdh's laravel-elfinder package is used)

Details Row

The details row functionality allows you to present more information in the table view of a CRUD. When enabled, a "+" button will show up next to every row, which on click will expand a "details row" below it, showing additional information.

Behind the scenes, on click an AJAX request is sent to the entity/{id}/details route, which calls the showDetailsRow() method on your EntityCrudController. Everything returned by that method is then shown in the details row. You'll want to overwrite that method to show anything you'd like in the details row.

Usage (everything in your EntityCrudController):

  1. Enable the functionality: $this->crud->enableDetailsRow();
  2. Allow access to all users: $this->crud->allowAccess('details_row');; Wrap an "if" statement around this if you don't want everybody to be able to see it.
  3. Overwrite the showDetailsRow($id) method;

Alternative for the 3rd step: overwrite views/backpack/crud/details_row.blade.php which is called by the default showDetailsRow($id) functionality;

Revisions

Revisions allows you to store all entry's changes in a table and revert to a specific value, if you'd like. It can work as a backup system and an accountability system for the admins.

When enabled, it will show another button in the table view, between Edit and Delete. On click, that button opens another page which will allow an admin to see all changes and who made them. The revisions page will look like this:

In order to enable this functionality for a crud panel, you need to:

  1. Create the revisions table:
cp vendor/venturecraft/revisionable/src/migrations/2013_04_09_062329_create_revisions_table.php database/migrations/ && php artisan migrate
  1. Use venturecraft/revisionable on your model. If you are using another bootable trait be sure to override the boot method in your model.
namespace MyApp\Models;

class Article extends Eloquent {
    use \Backpack\CRUD\CrudTrait, \Venturecraft\Revisionable\RevisionableTrait;

    // If you are using another bootable trait the be sure to override the boot method in your model
    public static function boot()
    {
        parent::boot();
    }
}
  1. In your EntityCrudController, enable access to the revisions:
$this->crud->allowAccess('revisions');
  1. [optional] If you want the table view to load faster (and why wouldn't you?), you should eager-load the revisions, so there won't be any extra DB queries for the revisions:
    $this->crud->with('revisionHistory');
    

For complex usage, head on over to VentureCraft/revisionable to see the full documentation and extra configuration options.

Export buttons

Exporting the DataTable to PDF, CSV, XLS is as easy as typing $this->crud->enableExportButtons(); in your constructor.

Extras (fake fields, stored as JSON in the database)

Step 1. You can define any number of fields as fake. Instead of being stored in the database table in their own column, they will be compacted and stored as a JSON array in the “extras” column in the database table. Optionally, you can choose what that column is called.

Basic definition example:

<?php

[
    'name' => 'name', // JSON variable name
    'label' => "Tag Name", // human-readable label for the input

    'fake' => true, // show the field, but don’t store it in the database column above
    'store_in' => 'extras' // [optional] the database column name where you want the fake fields to be stored as a JSON array 
],

Example usage:

<?php

[
    'name' => 'meta_title',
    'label' => "Meta Title", 
    'fake' => true, 
    'store_in' => 'metas' // [optional]
],
[
    'name' => 'meta_description',
    'label' => "Meta Description", 
    'fake' => true, 
    'store_in' => 'metas' // [optional]
],
[
    'name' => 'meta_keywords',
    'label' => "Meta Keywords", 
    'fake' => true, 
    'store_in' => 'metas' // [optional]
],

In this example, these 3 fields will show up in the create & update forms, the CRUD will process as usual, but in the database these values won’t be stored in the “meta_title”, “meta_description” and “meta_keywords” columns. They will be stored in the “metas” column as a JSON array:

{"meta_title":"title","meta_description":"desc","meta_keywords":"keywords"}

If “store_in” wasn’t defined, they would have been stored in the “extras” column.

Step 2. (optional) If you’ve chosen anything else than “extras” to store the fake fields in, define the fakeColumns property on the model. It needs to be an array with all of the columns where you store fake fields. This will allow you to use Entity::find(1)->withFakes();

Note

Make sure “extras” or any other database column where you want to store fake fields is defined as “fillable” in the model. Otherwise your extra values will be ignored by the CRUD. Also, if you’re using any other column name for fake fields other than ‘extras’, you need to cast it as array in your model, so that your JSON results are always turned to PHP arrays.

Example model:

<?php

class Tag extends Model {

	use CrudTrait;

	protected $table = 'tags';
	protected $fakeColumns = ['extras', 'metas'];
	protected $fillable = ['name', 'extras', 'metas'];
  protected $casts = [
        'metas' => 'array',
       ];

    public function articles()
    {
        return $this->hasMany('App\Models\Article', 'article_tag');
    }

}

Column and field order

You can use the beforeColumn('name'), afterColumn('name'), beforeField('name'), afterField('name') methods to change the order of the fields/columns after the fact. Otherwise they will be shown in the order you write them.

Example usage:

$this->crud->addColumn([
         'name' => 'name', // The db column name
         'label' => "Tag Name", // Table column heading
         'type' => 'text'
         ]);
$this->crud->addColumn([
         'name' => 'type', // The db column name
         'label' => "Tag Type", // Table column heading
         'type' => 'text'
         ]);
$this->crud->addColumn([
         'name' => 'id', // The db column name
         'label' => "Tag ID", // Table column heading
         'type' => 'text'
         ])->beforeColumn('name'); // ======> because of this, it will show up first

Translatable models and multi-language CRUDs

You can let your admins edit multi-lingual entries. Only translations stored the spatie/laravel-translatable way are supported right now, but more options will be coming soon.

In order to make one of your Models translatable, you need to:

  1. Be running MySQL 5.7+ (or a PosgreSQL with JSON column support);
  2. Install spatie/laravel-translatable;
  3. Make all the columns you will want to be translatable either JSON or TEXT;
  4. Use Backpack's HasTranslations trait on your model (instead of using spatie's HasTranslations) and define what fields are translatable, inside the $translatable property. For example:
<?php

namespace App\Models;

use Backpack\CRUD\CrudTrait;
use Illuminate\Database\Eloquent\Model;
use Backpack\CRUD\ModelTraits\SpatieTranslatable\HasTranslations;

class Product extends Model
{
    use CrudTrait;
    use HasTranslations;

     /*
    |--------------------------------------------------------------------------
    | GLOBAL VARIABLES
    |--------------------------------------------------------------------------
    */

    protected $table = 'products';
    protected $primaryKey = 'id';
    protected $fillable = ['name', 'category_id', 'options', 'price', 'tags'];
    protected $translatable = ['name', 'options'];

Additionally, if you have slugs, you'll need to use backpack's classes instead of the ones provided by cviebrock/eloquent-sluggable:

<?php

namespace Backpack\NewsCRUD\app\Models;

use Illuminate\Database\Eloquent\Model;
use Backpack\CRUD\CrudTrait;
use Backpack\CRUD\ModelTraits\SpatieTranslatable\Sluggable;
use Backpack\CRUD\ModelTraits\SpatieTranslatable\SluggableScopeHelpers;
use Backpack\CRUD\ModelTraits\SpatieTranslatable\HasTranslations;

class Category extends Model
{
    use CrudTrait;
    use Sluggable, SluggableScopeHelpers;
    use HasTranslations;

    /*
    |--------------------------------------------------------------------------
    | GLOBAL VARIABLES
    |--------------------------------------------------------------------------
    */

    protected $table = 'categories';
    protected $primaryKey = 'id';
    // public $timestamps = false;
    // protected $guarded = ['id'];
    protected $fillable = ['name', 'parent_id'];
    // protected $hidden = [];
    // protected $dates = [];
    protected $translatable = ['name', 'slug'];

    /**
     * Return the sluggable configuration array for this model.
     *
     * @return array
     */
    public function sluggable()
    {
        return [
            'slug' => [
                'source' => 'slug_or_name',
            ],
        ];
    }
}

Tabbed Forms

You can now split your create/edit inputs into multiple tabs.

In order to use this feature, you just need to specify the tab name for each of your fields. Example:

        $this->crud->addField([ // select_from_array
            'name' => 'select_from_array',
            'label' => "Select from array",
            'type' => 'select_from_array',
            'options' => ['one' => 'One', 'two' => 'Two', 'three' => 'Three'],
            'allows_null' => false,
            'allows_multiple' => true,
            'tab' => 'Tab name here'
        ]);

If you forget to specify a tab name for a field, Backpack will place it in the last tab.

Inline Errors

In addition to the top errors (grouped), Backpack forms (create & edit) can now also show highlight each input that has errored. The feature comes enabled by default in Backpack\CRUD 3.2. You just can disable it in your crud.php config file. This should improve your admin's UX a lot when making mistakes.

Customize Views for each CRUD Panel

Previously, if you wanted to create a custom create.blade.php view for just one CRUD Panel, you needed to overwrite the create() method. The same for edit, list, revisions, reorder, etc.

Now you can specify custom views in your setup() method:

$this->crud->setShowView('your-view');
$this->crud->setEditView('your-view');
$this->crud->setCreateView('your-view');
$this->crud->setListView('your-view');
$this->crud->setReorderView('your-view');
$this->crud->setRevisionsView('your-view');
$this->crud->setRevisionsTimelineView('your-view');
$this->crud->setDetailsRowView('your-view');

Custom CSS and JS for CRUD operations

Starting with Backpack\CRUD 3.2, there's a place where you can place your custom operations javascript and styles. You'll find a CSS and JS file in your public/vendor/backpack/crud folder for each Backpack operation. You can place any custom operation code there, without bloating general files css, js or blade files.

Extra CRUD Routes

Starting with Backpack\CRUD 3.2, you can use the with() method on CRUD::resource to better organize your routes. Something like this:

CRUD::resource(‘teams’, ‘Admin\TeamCrudController’)->with(function(){
    // add extra routes to this resource
    Route::get('teams/ajax-name-options', 'Admin\TeamCrudController@nameOptions');
    Route::get('teams/ajax-category-options', 'Admin\TeamCrudController@categoryOptions');
});

Advanced Features