4

What I did

Back when I was using Backpack 3.6, I've created a trait called RelatedCrud and set up everything in order to have more than one CRUD inside the same page.

To better explain myself with an example, I had the CRUD page "Note", which handled the notes inside a "Ticket". Ticket and Note are two separate CRUD entities. By modifying the views and by creating a trait, I've managed to put the "Show" portion of one particular Ticket inside the "Create" page of a Note. The Create page of Note basically had both the Show of a particular ticket and the form that I could use to create a new Note.

It used to work back in 3.6 but it doesn't now that I've moved to version 4.0. Is it because the CrudPanel is now a singleton? What could I do in order to make this work?

Here's what I did:

NoteTicketCrudController:

<?php

namespace App\Http\Controllers\Admin;

use Route;

class NoteTicketCrudController extends TicketCrudController
{
    public function setup()
    {
        parent::setup();
        $this->crud->removeButton('reply_button');
        $this->crud->removeButton('note_button');
        $this->crud->denyAccess('delete');
        $this->crud->denyAccess('create');
        $this->crud->denyAccess('update');
        // get the user_id parameter
        $noteId = Route::current()->parameter('note') ?: Route::current()->parameter('note_id');
        // set a different route for the admin panel buttons
        $this->crud->setRoute("admin/notes/".$noteId."/tickets");
        // show only that user's posts
        $this->crud->addClause('whereHas', 'notes', function ($query) use($noteId) {
            $query->where('id', $noteId);
        });

        $this->crud->setColumnDetails('subject', ['limit' => '1000']);

        $this->crud->addColumn([
                'name'  => 'description',
                'label' => 'Messaggio',
                'type'  => 'text',
                'limit' => 1000
        ])->afterColumn('subject');

        $this->crud->addColumn([
                // run a function on the CRUD model and show its return value
                'name' => "history_info",
                'label' => "",
                'type' => 'closure',
                'function' => function ($entry) {
                    return $entry->getHistoryView();
                }
        ]);
    }
}

RelatedCrud:

<?php

namespace App\Traits;

use Backpack\CRUD\app\Http\Controllers\CrudController;

trait RelatedCrud
{
    public $relatedCrud;

    public function setupRelated(CrudController $relatedController)
    { 

        $relatedController->crud = app()->make('crud'); // 4.0
        $relatedController->request = request();
        $relatedController->setupDefaults(); // 4.0
        $relatedController->setup(); 
        $relatedController->setupConfigurationForCurrentOperation(); // 4.0
        $this->relatedCrud = $relatedController->crud;
    }
}

NoteCrudController:

class NoteCrudController extends CrudController
{
    use \Backpack\CRUD\app\Http\Controllers\Operations\ListOperation;
    use \Backpack\CRUD\app\Http\Controllers\Operations\ShowOperation   { show as traitShow; }
    use \Backpack\CRUD\app\Http\Controllers\Operations\CreateOperation { store as traitStore; create as traitCreate; }
    use \Backpack\CRUD\app\Http\Controllers\Operations\UpdateOperation { edit as traitEdit; update as traitUpdate; }
    use \Backpack\CRUD\app\Http\Controllers\Operations\DeleteOperation { destroy as traitDestroy; }
    use AccessManager;
    use RelatedCrud;

    public $ticket;

    public function setup()
    {
        /*
        |--------------------------------------------------------------------------
        | CrudPanel Basic Information
        |--------------------------------------------------------------------------
        */
        $this->crud->setModel('App\Note');
        $this->crud->setRoute(config('backpack.base.route_prefix') . '/notes');
        $this->crud->setEntityNameStrings('note', 'note');

        //imposta il controller usato per mostrare la tabella dei ticket nel metodo show
        $this->setupRelated(new NoteTicketCrudController()); // LOADS SECOND CRUD

        /*
        |--------------------------------------------------------------------------
        | CrudPanel Configuration
        |--------------------------------------------------------------------------
        */

        $this->setPermissions();

        $this->ticket = $this->crud->request->ticket ? Ticket::find($this->crud->request->ticket) : null; // GETS TICKET ID BASED ON GET REQUEST

[...]

public function create()
    {
        if(!$this->ticket) {
            throw new AccessDeniedException(trans('backpack::crud.unauthorized_access', ['access' => 'create']));
        }
        $this->hasAccessToEntryOrFail('create', null);

        $this->crud->hasAccessOrFail('create');
        // $this->crud->setOperation('create');

        // prepare the fields you need to show
        $this->data['crud'] = $this->crud;
        if($this->ticket) {      // GETS TICKET AND SAVES ITS DATA IN RELATEDCRUD FIELD
            $this->data['relatedCrud'] = $this->relatedCrud;
            $this->data['entry'] = $this->data['relatedCrud']->getEntry($this->ticket->id);
        }
        $this->data['saveAction'] = $this->crud->getSaveAction();
         // $this->data['fields'] = $this->crud->getCreateFields();
        $this->data['title'] = $this->crud->getTitle() ?? trans('backpack::crud.add').' '.$this->crud->entity_name;

        // load the view from /resources/views/vendor/backpack/crud/ if it exists, otherwise load the one in the package
        return view($this->crud->getCreateView(), $this->data);
    }

Routes\backpack\custom.php:

Route::group([
    'prefix'     => config('backpack.base.route_prefix', 'admin'),
    'middleware' =>  ['web', config('backpack.base.middleware_key', 'admin')],
    'namespace'  => 'App\Http\Controllers\Admin',
], function () { // custom admin routes
    Route::get('/dashboard', 'DashboardController@dashboard')->name('backpack.dashboard');
    Route::get('/', 'DashboardController@redirect')->name('backpack');
    Route::crud('tickets', 'TicketCrudController');
    Route::crud('notes', 'NoteCrudController');
    Route::group(['prefix' => 'notes/{note_id}'], function() {
        Route::crud('tickets', 'NoteTicketCrudController');
    });
});

Create.blade.php:

@extends(backpack_view('layouts.top_left'))

@php
  $defaultBreadcrumbs = [
    trans('backpack::crud.admin') => url(config('backpack.base.route_prefix'), 'dashboard'),
    $crud->entity_name_plural => url($crud->route),
    trans('backpack::crud.preview') => false,
  ];

  // if breadcrumbs aren't defined in the CrudController, use the default breadcrumbs
  $breadcrumbs = $breadcrumbs ?? $defaultBreadcrumbs;
@endphp

@section('header')
    <section class="container-fluid">
      <h2>
        <span class="text-capitalize">{!! $crud->getHeading() ?? $crud->entity_name_plural !!}</span>
        <small>{!! $crud->getSubheading() ?? trans('backpack::crud.add').' '.$crud->entity_name !!}.</small>

        @if ($crud->hasAccess('list'))
          <small><a href="{{ url($crud->route) }}" class="hidden-print font-sm"><i class="fa fa-angle-double-left"></i> {{ trans('backpack::crud.back_to_all') }} <span>{{ $crud->entity_name_plural }}</span></a></small>
        @endif
      </h2>
    </section>
@endsection

@section('content')
@if(!empty($relatedCrud))
<div class="row">
    <div class="{{ $relatedCrud->getShowContentClass() }}">
    <!-- Default box -->
        <div class="">
            @if ($relatedCrud->model->translationEnabled())
            <div class="row">
                <div class="col-md-12 mb-2">
                    <!-- Change translation button group -->
                    <div class="btn-group float-right">
                    <button type="button" class="btn btn-sm btn-primary dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                        {{trans('backpack::crud.language')}}: {{ $relatedCrud->model->getAvailableLocales()[$relatedCrud->request->input('locale')?$relatedCrud->request->input('locale'):App::getLocale()] }} &nbsp; <span class="caret"></span>
                    </button>
                    <ul class="dropdown-menu">
                        @foreach ($relatedCrud->model->getAvailableLocales() as $key => $locale)
                            <a class="dropdown-item" href="{{ url($relatedCrud->route.'/'.$entry->getKey()) }}?locale={{ $key }}">{{ $locale }}</a>
                        @endforeach
                    </ul>
                    </div>
                </div>
            </div>
            @else
            @endif
            <div class="card no-padding no-border">
                <table class="table table-striped mb-0">
                    <tbody>
                    <?php $i = 0 ?>
                    @foreach ($relatedCrud->columns() as $column)
                    @if($column['name'] != 'history_info')
                    @if($column['name'] != 'priority' && $column['name'] != 'description')
                        @if($i % 2 == 0)
                        <tr>
                        @endif
                            <td style="width:15%;">
                                <strong>{!! $column['label'] !!}</strong>
                            </td>
                            <td style="width:35%;">
                                @if (!isset($column['type']))
                                    @include('crud::columns.text')
                                @else
                                    @if(view()->exists('vendor.backpack.crud.columns.'.$column['type']))
                                        @include('vendor.backpack.crud.columns.'.$column['type'])
                                    @else
                                        @if(view()->exists('crud::columns.'.$column['type']))
                                            @include('crud::columns.'.$column['type'])
                                        @else
                                            @include('crud::columns.text')
                                        @endif
                                    @endif
                                @endif
                            </td>
                        @if($i % 2 == 1)
                        </tr>
                        @endif
                        <?php $i++; ?>
                        @endif
                    @endif
                    @endforeach
                    @if (count($relatedCrud->columns()) % 2 == 1)
                    </tr>
                    @endif
                </tr>
                    </tbody>
                </table>
                <table class="table table-striped mb-0">
                        <tbody>
                @foreach($relatedCrud->columns() as $column)

            @if($column['name'] == 'history_info' || $column['name'] == 'description')

                    <tr>
                    <td style="width:100%;">
                            @if($column['name'] == 'description')

                                    <strong>{!! $column['label'] !!}</strong><br>

                            @endif
                        @if (!isset($column['type']))
                            @include('crud::columns.text')
                        @else
                            @if(view()->exists('vendor.backpack.crud.columns.'.$column['type']))
                                @include('vendor.backpack.crud.columns.'.$column['type'])
                            @else
                                @if(view()->exists('crud::columns.'.$column['type']))
                                    @include('crud::columns.'.$column['type'])
                                @else
                                    @include('crud::columns.text')
                                @endif
                            @endif
                        @endif
                    </td>
                    </tr>
                    @endif
                @endforeach
                        </tbody>
                </table>
            </div><!-- /.box-body -->
        </div><!-- /.box -->

    </div>
</div>
@endif

<div class="row m-t-20">
    <div class="{{ $crud->getCreateContentClass() }}">
        <!-- Default box -->

        @include('crud::inc.grouped_errors')

          <form method="post"
                action="{{ url($crud->route) }}"
                @if ($crud->hasUploadFields('create'))
                enctype="multipart/form-data"
                @endif
                >
          {!! csrf_field() !!}

            <div class="row display-flex-wrap">
              <!-- load the view from the application if it exists, otherwise load the one in the package -->
              @if(view()->exists('vendor.backpack.crud.form_content'))
                @include('vendor.backpack.crud.form_content', [ 'fields' => $crud->getFields('create'), 'action' => 'create' ])
              @else
                @include('crud::form_content', [ 'fields' => $crud->getFields('create'), 'action' => 'create' ])
              @endif
            </div><!-- /.box-body -->
                @include('crud::inc.form_save_buttons')
          </form>
    </div>
</div>

@endsection

What I expected to happen

I expected to see the "Show" part of one particular ticket inside the NoteCrud's "Create" page.

What happened

The Ticket data overwrite the Note data inside the CRUD page (because the CrudPanel object is now treated as a singleton?) and the create page returns an error because it tries to access Note's fields and methods through Ticket.

What I've already tried to fix it

I've put the new commands located inside the CrudController's __construct() into the SetupRelated method which is inside my custom RelatedCrud trait.

Backpack, Laravel, PHP, DB version

When I run php artisan backpack:version the output is:

laravel/framework                     v5.8.35            The Laravel Framework.
backpack/crud                         4.0.5              Quickly build an admin interfaces using Laravel 6, CoreUI, Boostrap 4 and jQuery.
backpack/generators                   2.0.4              Generate files for laravel projects
backpack/permissionmanager            5.0.0              Users and permissions management interface for Laravel 5 using Backpack CRUD.
Pat
  • 41
  • 1
  • 4
  • Check out [this answer](https://stackoverflow.com/a/57022215/1376624) on a similar question. An approach like this would work even with any singleton type changes in v4 – Wesley Smith Oct 06 '19 at 04:53
  • 1
    Yeah, the iFrame solution is actually a pretty good one. I've used it for this specific issue and everything is fine now. Needs a bit of tuning but I'd say the problem has been solved! Thank you, Delighted! – Pat Oct 08 '19 at 07:08

0 Answers0