Skip to content

[4.0][Proposal] Maybe move CrudPanel configuration to its own object? #1600

@tabacitu

Description

@tabacitu

Right now we're configuring the entire CrudPanel object inside the EntityCrudController::setup() method. That's basically all we're doing inside setup(), because all calls start with $this->crud.

I think this makes Backpack easy to understand. Since it happens inside the EntityCrudController, even a first-timer with medium OOP knowledge should understand what's happening.

BUT.

For big (huge) CRUDs, the setup() method becomes so bloated. Just take a look at the MonsterCrudController in the demo. It's very very rare that you'll need a CRUD this complicated, but when you do, it's a drag to organize the code inside the CrudController. There are a few options, but none of them good, in my opinion. You can further split your configuration using:

  1. comments inside the setup() method (not really an organization method imho)
  2. methods like addFieldsToCrud(), addFiltersToCrud() or something like addMetaFakeFields() that you could even isolate into a trait, that you can then reuse across multiple EntityCrudControllers;

Out of the above, I definitely like no 2 better, but even that one has got problems:

  • I'm used to my controllers having all methods public and accessible through routes.
  • When creating new methods, you really have to make sure you're not overwriting some CrudPanel or CrudTrait methods;
  • The CrudPanel configuration is in the same object as its representation;

A solution to this problem (or maybe it isn't a real problem, you tell me) could be to configure the CrudPanel separately - not in the CrudPanel at all, but in a Builder. So that your MonsterCrudController could look something like:

<?php

namespace App\Http\Controllers\Admin;

use App\Http\Requests\MonsterRequest as StoreRequest;
// VALIDATION: change the requests to match your own file names if you need form validation
use App\Http\Requests\MonsterRequest as UpdateRequest;
use Backpack\CRUD\app\Http\Controllers\CrudController;
use App\CrudPanel\MonsterCrudPanelConfiguration;

class MonsterCrudController extends CrudController
{
    public function setup()
    {
         $this->crud = $this->crud->makeFrom(MonsterCrudPanelConfiguration::class);
    }

    public function store(StoreRequest $request)
    {
        $redirect_location = parent::storeCrud($request);

        return $redirect_location;
    }

    public function update(UpdateRequest $request)
    {
        $redirect_location = parent::updateCrud($request);

        return $redirect_location;
    }

    public function show($id)
    {
        $content = parent::show($id);

        // so we want to change something only for the Show operation
        // ok, sure, we can do it here
        $this->crud->addColumn([
            'name' => 'table',
            'label' => 'Table',
            'type' => 'table',
            'columns' => [
                'name'  => 'Name',
                'desc'  => 'Description',
                'price' => 'Price',
            ]
        ]);
        $this->crud->addColumn([
            'name' => 'fake_table',
            'label' => 'Fake Table',
            'type' => 'table',
            'columns' => [
                'name'  => 'Name',
                'desc'  => 'Description',
                'price' => 'Price',
            ],
        ]);
        $this->crud->addColumn([
            'name' => 'upload_multiple',
            'label' => 'Upload multiple',
            'type' => 'upload_multiple',
            // 'disk' => 'uploads',
        ]);
        $this->crud->addColumn('text');
        $this->crud->removeColumn('date');
        $this->crud->removeColumn('extras');

        return $content;
    }
}

With a separate file containing the actual configuration (pseudo-code here):

<?php

namespace App\CrudPanels;

class MonsterCrudControllerConfiguration extends CrudPanelConfiguration 
{
	public function fields() {
		return [];
	}

	public function columns()
	{
		return [];
	}

	public function filters()
	{
		return [];
	}

	public function buttons()
	{
		return [];
	}
}

What I like about this:

  • would make it easier for someone to see all configuration options, since the methods are generated in the builder;
  • would probably make it easier to generate configurations;
  • it would separate CrudPanel configuration from the actual representation; we need to do that anyway, at some point - it's just good OOP for big objects like this one;
  • it would allow us to create CRUDs inside CRUDs (aka "matrix field type", aka “add related item in a pop-up”) - we could just have a related_crud field type that points to this configuration file;
  • it would make it easier for us to generate 100% complete CRUD panels - we just generate this configuration file;
  • it would make it easier to share configurations between admin panels, or create multiple admin panels for the same entity (just extend the configuration file and modify a few things);

What I don't like about this solution:

  1. configuration is still not split up into Operations (Create, Update, Delete, ListEntries), and I think it should be;
  2. how would you go about enabling/disabling some nitch features of an operation? say disableResponsiveTable()?

Food for thought...

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions