Nested resources in Backpack CRUD

Posted in Tutorials by Cristian Tabacitu - 29th of November 2016

Sometimes you'll have one resource that depends on another one. For example, you might have:

  • a Post model;
  • a User model;

The admins could see all posts, of course. But if you might want to see only one user's posts. Sometimes, you don't want to be using a filter. You just want to go from a User to that User's posts. In this case, you can create a nested resource. Basically you'd be creating a third admin panel, UserPostCrudController, which is exactly like your PostCrudController, but it adds a constraint to only show one user's posts.

So you'd have the following admin panels (and routes):

  • admin/post/ - the admin can see all posts;
  • admin/user/ - the admin can see all users;
  • admin/user/{user_id}/post/ - the admin (or someone else) can see a user's posts;

Step 1. Add a button

You'll need to show a button next to the Edit/Delete buttons, to take the admin to the User's posts. You can do that by creating a custom button.

1.1. Create a new blade file in your /resources/views/vendor/backpack/crud/buttons/, named user_posts.blade.php, that looks something like this: <a href="{{ Request::url().'/'.$entry->getKey() }}/post" class="btn btn-xs btn-default"><i class="fa fa-eye"></i> See Posts</a>.

1.2. Now tell your UserCrudController to show that new button we've created: $this->crud->addButtonFromView('line', 'user_posts', 'user_posts', 'beginning');

Step 2. Create your new controller

In this new controller, UserPostCrudController, we could just extend the PostCrudController and specify what's different about it:

<?php namespace App\Http\Controllers\Admin;

use Backpack\CRUD\app\Http\Controllers\CrudController;
use App\Http\Controllers\Admin\PostCrudController;

// VALIDATION: change the requests to match your own file names if you need form validation
use App\Http\Requests\PostRequest as StoreRequest;
use App\Http\Requests\PostRequest as UpdateRequest;

class UserPostCrudController extends PostCrudController {

    public function setup() {
        parent::setup();

        // get the user_id parameter
        $user_id = \Route::current()->parameter('user_id');

        // set a different route for the admin panel buttons
        $this->crud->setRoute("admin/user/".$user_id."/post");

        // show only that user's posts
        $this->crud->addClause('where', 'user_id', '==', $user_id); 
    }

    public function store(StoreRequest $request)
    {
        return parent::storeCrud();
    }

    public function update(UpdateRequest $request)
    {
        return parent::updateCrud();
    }
}

Step 3. Add the route

Your routes file (routes/web.php) should include a third route, that points to this new UserPostCrudController we've created. It might look something like this:

// Admin Interface Routes
Route::group(['prefix' => 'admin', 'middleware' => ['web', 'auth'], 'namespace' => 'Admin'], function()
{
    CRUD::resource('post', 'PostCrudController');
    CRUD::resource('user', 'UserCrudController');

    // !!! DIFFERENT ADMIN PANEL FOR USER POSTS
    Route::group(['prefix' => 'user/{user_id}'], function()
    {
        CRUD::resource('post', 'UserPostCrudController');
    });
});

That's it. Your admins should now be able to see Users, Posts and User's posts.

Hope it helps. Please email me if you have any suggestions.

Cheers!

PS. If you need any advanced feature to work (details_row, revisions, preview) you may notice it takes the parent entity's ID (user), when it should actually use the child's (post). In order to fix this, you can overwrite any controller method that feature is using and just call the parent method with the right ID. So for the "preview" feature (which is for developers only), you would do something this:

    public function show($id)
    {
        return parent::show($this->request->post);
    }

Here's all of them at this date:

    public function show($id)
    {
        return parent::show($this->request->post);
    }

    public function listRevisions($id)
    {
        return parent::listRevisions($this->request->post);
    }

    public function restoreRevision($id)
    {
        return parent::restoreRevision($this->request->post);
    }

    public function showDetailsRow($id)
    {
        return parent::showDetailsRow($this->request->post);
    }