Crud: [3.4][Feature] Allow setting a custom view for fields/columns/filters/buttons

Created on 9 May 2018  路  16Comments  路  Source: Laravel-Backpack/CRUD

@tswonke mentioned here https://github.com/Laravel-Backpack/CRUD/issues/1351#issuecomment-387068769 that a necessary feature for package developers would be the ability to load fields/columns/filters/buttons from the package, without having to publish the files.

This is needed in packages like PageManager, for example, where if the developer publishes the new field type, there's no way to make changes to it without it being a breaking change, asking them to republish, etc (bleah...).

Solution 1

I think a good way to achieve this would be a non-breaking change. Have a view attribute in the addField / addColumn / addFilter / addButton syntax, that would allow a custom view path, instead of the default vendor.backpack.crud.fields for example.

So it could be:

$this->crud->addField([
            'name'  => 'text',
            'label' => 'Text',
            'type'  => 'custom',
            'view'   => 'package::fields.custom'
        ]);
$this->crud->addColumn([
            'name'  => 'text',
            'label' => 'Text',
            'type'  => 'custom',
            'view'   => 'package::fields.custom'
        ]);
$this->crud->addFilter([
          'type'  => 'custom',
          'view'  => 'package::filters.custom',
          'name'  => 'checkbox',
          'label' => 'Checked',
        ],
        false, // the simple filter has no values, just the "Draft" label specified above
        function () { // if the filter is active (the GET parameter "draft" exits)
            $this->crud->addClause('where', 'checkbox', '1');
        });

Actually, buttons already sort of have this functionality, since the 3rd parameter is view:

public function addButtonFromView($stack, $name, $view, $position = false)

but it prepends the default path, which sucks $view = 'vendor.backpack.crud.buttons.'.$view;. So in this case we'll probably go with addButtonFromExternalView() that doesn't do that, then deprecate one of the methods in a future version.

I think this is the broadest solution, as it:

  • would allow a granular control of views for each field;
  • would also allow developers to place their fields/columns/filters/buttons outside the default location (which I don't actually agree with, but people have requested it);
  • would allow package developers to use their own fields without publishing;

Solution 2

From the package developer's point of view, an even simpler solution would be to have a method to call in package boot, that would just make sure a certain folder is also checked for fields. So a method like CRUD::addFieldsFolder() could add it to a stack that is checked for fields. But this introduces a different problem - folder order. Obviously the fallback would be the default Backpack fields folder, so it would check package1/fields, then crud/fields. But what if there are 2-3 packages that use this CRUD::addFieldsFolder() method? What order would then be used? Most likely the order the packages got auto-loaded by Laravel, since all packages use auto-loading now. But Laravel provides no way to choose the order the packages get registered in. So we'd have no way to control that it's package1.fields, package2.fields, crud.fields. We _could_ add a method to allow package developers to control the order, but then you would allow developer1 to influence how package2 works. No bueno...

So... to end this rant... I think we're never _ever_ going to allow "_folder stacks_". I see now way out of this slippery slope. But Solution 1, I think, provides a good way to do the same thing, even if it's a little more difficult for package developers.


Again, I think we've talked about this feature before, but I can't seem to find our previous thoughts...

triage

All 16 comments

Thank you, @tabacitu!

I also assume that solution 1 could/should be the next incremental step. It would help a lot, is (like you said) a non-breaking change and can probably be implemented easily.
Currently I don't see a blocker for the work on Fields as Objects

@OliverZiegler do you have any further thoughts/suggestions on that?

I added three pull requests:

Custom Fields: https://github.com/Laravel-Backpack/CRUD/pull/1387
Custom Columns: https://github.com/Laravel-Backpack/CRUD/pull/1388
Custom Filters: https://github.com/Laravel-Backpack/CRUD/pull/1389

Seem to work and should be non-breaking. I also did a refactoring for the columns part.

Hey guys. Currently reviewing the PRs.

I'm currently kind of surprised, setting a view parameter will not set that exact view, but always select a subview in the given namespace by type.
I think this will lead to confusion as a view parameter suggests you can set the exact view...

Probably we could offer both behaviours...

I suggests using the view parameter for directly setting the provided view (if present) and add another parameter like view_namespace for setting the view namespace and getting the view by type.

This would also enable custom extensive used overwrites for the same field/column/filter with slightly different behaviour...

What do you think?

Thank you, Oli!

I undestand your concerns and I will rename view to view_namespace. That should avoid missunderstandings.

An additional view-option for specifying the full path might be interesting and can be implemented afterwards. But in this case I hope, that we can use my (soon updated) pull requests as soon as possible :)

Ok, I've renamed it and Oli has already done a review.
Do you like to merge the 3 pull requests, @tabacitu? 馃榿

Hi guys,

I totally agree with the changes - I'm testing and merging them right now. At first glance they look really really well. I also think the problem @OliverZiegler brought up is a good one (the naming of the view attribute).

Cheers!

@tabacitu I will prepare 3 new PRs after you merged @tswonke's PRs to apply my suggested direct view option ;)

Hehe! 馃槃

@OliverZiegler - ok, sure. That would basically overwrite the type and view_namespace, right? I don't know if such a view option would help, or just introduce confusion, since you'd still need a type defined... Interesting to see your take on this.

@tabacitu yeah, that's exactly what I intended...
typewould not be used in this approach (if view set)...

Will dig in the code and see if this is just figment or really possible (and useful)

Ok, sure. Let's open another issue for that if you find a way ;-)

PS. Merged, tested and polished all PRs. Tagged and ready to use! Thanks a lot!

Yeah! 馃帀

Thank you! 馃憤 :-)

i've added custom view $this->crud->setCreateView('admin.addArticle'); on setup function. how can i add field using $this->crud->addField([ ...]); ??? thank you

@nguyenhiepvan if you've just modified the default view in your admin.addArticle view, you should be able to add a field the exact same way: in your setupCreateOperation / setupUpdateOperation method in Backpack v4. Or as a last resort in your setup() method.

@nguyenhiepvan if you've just modified the default view in your admin.addArticle view, you should be able to add a field the exact same way: in your setupCreateOperation / setupUpdateOperation method in Backpack v4. Or as a last resort in your setup() method.

Thank you for replying, here is my admin.addArticle view:

@extends(backpack_view('blank'))
@section('after_scripts')

@endsection
@section('content')

@endsection

is that true for default view? sory for that question because i'm newbie, thank you

There you go - that's why you can't add fields, your custom blade file doesn't do anything, your content section is empty.

If you want to mimic the create.blade.php file, I suggest you start from that one, and modify it to your liking. You'll notice the CONTENT section of that file includes this:

<div class="row">
    <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() !!}

              <!-- 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->fields(), 'action' => 'create' ])
              @else
                @include('crud::form_content', [ 'fields' => $crud->fields(), 'action' => 'create' ])
              @endif

              @include('crud::inc.form_save_buttons')
          </form>
    </div>
</div>

which does the actual outputting of the fields. Without this part, the view has no idea you want to show fields, so it doesn't do anything.

Hope it helps.
Cheers!

PS. This discussion is not appropriate for Github - we keep Github for bugs only. Nor is it in connection to the thread here - it's not a bug related to it, it's a question on how to do something custom. Please post questions like this on StackOverflow, using the backpack-for-laravel tag. If you find a bug, please open a new Github thread, rather than reply to an existing one with the feature. Thank you for understanding! The less time we spend cleaning up Github, the more time we can spend fixing bugs and building features.

Was this page helpful?
0 / 5 - 0 ratings