Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: how to apply default filters (from url parameters)? #2383

Closed
justinmoh opened this issue Jan 19, 2020 · 5 comments
Closed

Question: how to apply default filters (from url parameters)? #2383

justinmoh opened this issue Jan 19, 2020 · 5 comments
Labels

Comments

@justinmoh
Copy link

Something that have been troubled me since the day I started using Backpack.

Question 1

How to apply filter from url parameters? Visiting backpack_url('booking').'?only_pending=1 will just redirect to itself and ditch the querystring.

In a CRUD controller, we have a list operation, and we can define multiple filters that make sense for this Entity. Then in an example scenario:

  • Admin-A wants to see only Pending booking list by default
  • Admin-B wants to see only Approved booking list by default
  • Admin-C wants to see only Completed booking list by default
  • etc

From the developer point of view, they're actually just a matter of applying filter on the underlaying entity CRUD controller list operation -- but I don't know how to implement this.

Question 2

The next issue closely related. Backpack doesn't expose the API to directly amend the base model query. All query must be added by CRUD::addClause(), which by backpack's own understanding, it's a filter.

So if Admin-A is a user that only allowed to see Pending list and nothing else, and developer use CRUD::addClause('onlyPending') on setupListOperation(), the user will indeed seeing something like showing x from 12,345 filtered result.

Then Admin-A will then ask the developer how he can see the 12,345 items. But in fact he only entitled to see that x items.

This is arguably a "visual" issue, but I think it's more about framework architecture.

So what do I do?

Anyway, for the situation that I've been facing, I create a dedicated model for each List, and in each of them, I only utilise the list operation. E.g.

class StaffMyPendingBooking extends Booking
class AdminMyApprovedBooking extends Booking
class ManagerMyCompletedBooking extends Booking
...

// then register custom Route::crud() for each of them

I'm asking if there's a better way from experts like you guys.

@tabacitu
Copy link
Member

Hi @justinmoh ,

Question 1

Accessing https://demo.backpackforlaravel.com/admin/monster?checkbox=true in the demo project goes to the Monsters CRUD, and activates the Simple filter for me. That's what I understand you needed, right? I haven't used this a lot, but that's how I do it. Worked for me.

If it "redirects to itself and ditches the query string" I'm wondering what you're using in config/backpack/crud.php for the list operation. Here's mine:

<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Backpack\CRUD preferences
    |--------------------------------------------------------------------------
    */

    // --------------------------
    // Default operation settings
    // --------------------------
    'operations' => [

        /*
         * List Operation
         */
        'list' => [
            // Define the size/looks of the content div for all CRUDs
            // To override per view use $this->crud->setListContentClass('class-string')
            'contentClass' => 'col-md-12',

            // enable the datatables-responsive plugin, which hides columns if they don't fit?
            // if not, a horizontal scrollbar will be shown instead
            'responsiveTable' => true,

            // stores pagination and filters in localStorage for two hours
            // whenever the user tries to see that page, backpack loads the previous pagination and filtration
            'persistentTable' => true,

            // How many items should be shown by default by the Datatable?
            // This value can be overwritten on a specific CRUD by calling
            // $this->crud->setDefaultPageLength(50);
            'defaultPageLength' => 10,

            // A 1D array of options which will be used for both the displayed option and the value, or
            // A 2D array in which the first array is used to define the value options and the second array the displayed options
            // If a 2D array is used, strings in the right hand array will be automatically run through trans()
            'pageLengthMenu' => [[10, 25, 50, 100, -1], [10, 25, 50, 100, 'backpack::crud.all']],

            // How important is it for the action buttons to be visible?
            // - 0 - most important
            // - 1 - as important as bulk buttons
            // - 2-3 - more important than the rest of the columns
            // - 4 - less important than most columns
            'actionsColumnPriority' => 1,
        ],

// ...

Question 2

You don't have to use filters. For your use case, where the admins shouldn't even be able to see things that are note pending/approved/completed, you should probably modify the query in setup(), so they only get to see what they're supposed to. The query is exposed and modifiable in Backpack.

In you setup() method you can do something like:

   switch ($admin_type) {
       case 'A':
           $this->crud->query = $this->crud->query->where('status', 'pending');
           break;
       case 'B':
           $this->crud->query = $this->crud->query->where('status', 'approved');
           break;
       case 'C':
           $this->crud->query = $this->crud->query->where('status', 'completed');
           break;
   }

This would also solve your problem with the total number of items. $this->crud->query is then used by Backpack everywhere else. Filters just add stuff to it. The operations just add stuff on top of it. For example, the List operation just does a $this->crud->query->get(). So you can modify the query as you see fit, then that modified query will be used by all operations.


You solution with multiple models is cool too. Arguably, it's even cleaner :-) And you can use the same filtered models everywhere inside the app to make sure they only get to see their stuff. Which is cool. But personally I prefer one model per database table. I would do it like I detailed for Question 2 above.

Hope it helps.
Cheers!

@tabacitu
Copy link
Member

(closing the issue so it doesn't show up in our list of things to-do; feel free the continue the conversation or for others to pitch in with their opinion)

@edrisaturay
Copy link

Managed to get something similar working and any improvement is much appreciated..

i needed to make a column that link to the child model and and show only the linked items in the table therefore i wrote this in a trait

protected function redirect_filter_column($_name, $_attribute, $_query){ $this->crud->addColumn([ 'name' => $_name, 'label' => ucwords(Str::replace('_', ' ', $_name)), 'type' => 'select', 'attribute' => $_attribute, 'entity' => $_name, 'wrapper' => [ 'element' => 'a', 'href' => function ($crud, $column, $entry, $related_key) use ($_query){ return backpack_url( $column['entity'] . '?' . $_query . '=' .$related_key . '&id' . '=' . $related_key ); }, ] ]);

In the crud controller for the related (belongs to) model i used this to filter
`
$query = $this->crud->query;

    if (request()->get('parent_id')) {
        $query->orWhere('parent_id', request()->get('parent_id'));
    }
    if (request()->get('id')){
        $query->orWhere('id', request()->get('id'));
    }

    $this->crud->query = $query;

`

it worked for me

@haniramadhan-kkp
Copy link

haniramadhan-kkp commented Sep 15, 2023

Managed to get something similar working and any improvement is much appreciated..

i needed to make a column that link to the child model and and show only the linked items in the table therefore i wrote this in a trait

protected function redirect_filter_column($_name, $_attribute, $_query){ $this->crud->addColumn([ 'name' => $_name, 'label' => ucwords(Str::replace('_', ' ', $_name)), 'type' => 'select', 'attribute' => $_attribute, 'entity' => $_name, 'wrapper' => [ 'element' => 'a', 'href' => function ($crud, $column, $entry, $related_key) use ($_query){ return backpack_url( $column['entity'] . '?' . $_query . '=' .$related_key . '&id' . '=' . $related_key ); }, ] ]);

In the crud controller for the related (belongs to) model i used this to filter ` $query = $this->crud->query;

    if (request()->get('parent_id')) {
        $query->orWhere('parent_id', request()->get('parent_id'));
    }
    if (request()->get('id')){
        $query->orWhere('id', request()->get('id'));
    }

    $this->crud->query = $query;

`

it worked for me

Thank you, I really really need this. I will try to implement this, but if you don't mind, can you share the detail for me? How is the trait, how to implement the trait in the entityController?

After trying searching more about this topic, I think this is the answer for me.

https://backpackforlaravel.com/articles/tutorials/nested-resources-in-backpack-crud

@pxpm
Copy link
Contributor

pxpm commented Sep 19, 2023

@haniramadhan-kkp just an heads up that you have a nested crud example on demo if you want some code example on how to implemente it: Laravel-Backpack/demo#568

Cheers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

5 participants