EasyAdminBundle icon indicating copy to clipboard operation
EasyAdminBundle copied to clipboard

[FEATURE] Column chooser

Open ksn135 opened this issue 3 years ago • 12 comments

Sometimes you have too many columns/properties in entity, but some of them required by one user, some other by another one. Let's assume we have a list of employees with many fields. By default you need to scroll page left and right to see them all. first_AdobeExpress Just add the following lines to your existing code and see the results.

use EasyCorp\Bundle\EasyAdminBundle\Provider\SessionSelectedColumnStorageProvider;

class EmployeeCrudController extends AbstractCrudController
{
    public function __construct(
        private SessionSelectedColumnStorageProvider $sessionSelectedColumnStorageProvider
    ) {}

    public function configureCrud(Crud $crud): Crud
    {
        return $crud
            ->setEntityLabelInSingular('admin_label.employee.singular') 
            ->setEntityLabelInPlural('admin_label.employee.plural') 
            ->setupColumnChooser($this->sessionSelectedColumnStorageProvider) // <= magic is here
        ;
    }

Now you can see new global action button on index page named "Column chooser". It allows you choose which columns to hide/show and reorder them as you wish: demo Add video:

https://user-images.githubusercontent.com/230304/204554873-ca81ac84-4360-4c0d-b14f-6f60c7a66d58.mov

Selected columns saved in user session: Screenshot 2022-11-29 at 15 40 37 Another suggested option is to save selected columns in your user object in database. Your user entity MUST implement interface EasyCorp\Bundle\EasyAdminBundle\Interfaces\UserParametersStorageInterface

use EasyCorp\Bundle\EasyAdminBundle\Provider\UserSelectedColumnStorageProvider;

class EmployeeCrudController extends AbstractCrudController
{

    public function __construct(
        private UserSelectedColumnStorageProvider $userSelectedColumnStorageProvider
    ) {}

    public function configureCrud(Crud $crud): Crud
    {
        return $crud
            ->setEntityLabelInSingular('admin_label.employee.singular') 
            ->setEntityLabelInPlural('admin_label.employee.plural') 
            ->setupColumnChooser($this->userSelectedColumnStorageProvider) // <= magic is here
        ;
    }

In my case I store parameters in related table. But your implementation may be slightly different. Screenshot 2022-11-29 at 16 19 58

By default column chooseruse all fields from index and detail pages. Sometimes you want to setup it more precisely. You may globally setup in your dashboard controller and then make customisation on controller basis.

// DashboadController.php
use EasyCorp\Bundle\EasyAdminBundle\Provider\UserSelectedColumnStorageProvider;

class DashboardController extends AbstractDashboardController
{
   public function __construct(
        private TranslatorInterface $translator,
        private UserSelectedColumnStorageProvider $userSelectedColumnStorageProvider,
    ) {}

    public function configureCrud(): Crud
    {
        return Crud::new()
            ->setColumnChooserSelectedColumnStorageProvider($this->userSelectedColumnStorageProvider)
            ->setPaginatorPageSize(10);
    }
}

// EmployeeCrudController.php
class EmployeeCrudController extends AbstractCrudController
{
    public function configureCrud(Crud $crud): Crud
    {
        return $crud
            ->setEntityLabelInSingular('admin_label.employee.singular') 
            ->setEntityLabelInPlural('admin_label.employee.plural') 
            ->enableColumnChooser() // enable it
            ->setColumnChooserColumns(
                ['imageName', 'familyName', 'givenName', 'phone', 'email'], // default 
                [], // get all avaiable fields from index and detail page or specify only allowed
                ['deletedAt', 'virtualField'] // or may just exclude some fields
            )
        ;
    }
Screenshot 2022-11-29 at 16 43 13

ksn135 avatar Nov 29 '22 13:11 ksn135

nice feature <3 . great job mann

ahmedyakoubi avatar Nov 29 '22 21:11 ahmedyakoubi

@javiereguiluz please review this PR and tell me what you think about it. Thanks in advance.

ksn135 avatar Nov 30 '22 20:11 ksn135

yarn build add unnecessary slash before path in app.css and in manifest.json. I don't know why. Probably I should use another build tool (or special command line option) for building assets.

ksn135 avatar Dec 01 '22 07:12 ksn135

Hi!

Super nice feature ! thanks for the contribution i hope it can be implemented :D

I noticed a change in the sidebar, a padding of 40px is added on the ul.menu container image

The active fields don't show up in the modal when they are well configured and even after reset. image

Similar case for the default fields, if I modify it and reload my page it keeps the old fields defined I have to reset to have the new ones

I could not yet see the code for the moment, I do not know if it is possible to make a control of the default & active fields, because if not sent in the configureFields function then the modal returns bad information on fields possibly impossible to display

If we select no columns, the index columns is not defined so we have an error image image

Reduce the number of functions implemented in ConfigCrud and set up a DTO for the configuration of the complete module ? If no config we do not display the column chooser

Good work, I try to contribute if I find some time :)

TheoD02 avatar Dec 02 '22 11:12 TheoD02

Hi @TheoD02 !

I noticed a change in the sidebar, a padding of 40px is added on the ul.menu container

Yes, it's strange. As I mention before something wrong on my side with assets building tool (MacBook Pro, yarn 1.22.19). Probably padding issue has the same source.

The active fields don't show up in the modal when they are well configured and even after reset. image Similar case for the default fields, if I modify it and reload my page it keeps the old fields defined I have to reset to have the new ones

What storage class are you using? Session? I'll try investigate it later on this weekend.

I could not yet see the code for the moment, I do not know if it is possible to make a control of the default & active fields, because if not sent in the configureFields function then the modal returns bad information on fields possibly impossible to display

AFAIK with empty configureFields it will not work for now.

If we select no columns, the index columns is not defined so we have an error

Ought, my bad. Fix it. Thanks!

Reduce the number of functions implemented in ConfigCrud and set up a DTO for the configuration of the complete module ? If no config we do not display the column chooser

Good idea. I thought about it. For example, make it configurable in configureFields:

class EmployeeCrudController extends AbstractCrudController
{
    public function configureFields(string $pageName): iterable
    {
    public function configureFields(string $pageName): iterable
    {
        yield FormField::addTab('admin_label.common.panel.main');

        yield FormField::addPanel('admin_label.employee.panel.name')->setIcon('fa fa-user')->addCssClass('danger');

        yield TextField::new('imageName', 'admin_label.employee.field.imageName'); // ->setFormType(VichImageType::class);;
        // $this->getParameter('app.dirs.profile');

        yield TextField::new('familyName', 'admin_label.employee.field.familyName')->setColumns(4)
            ->addToDefaultColumns(); // <= add it to default columns in list
        yield TextField::new('givenName', 'admin_label.employee.field.givenName')->setColumns(4)
            ->addToDefaultColumns(); // <= add it to default columns in list
        yield TextField::new('patronymic', 'admin_label.employee.field.patronymic')->setColumns(4);

        yield DateField::new('birthday', 'admin_label.employee.field.birthday');

        yield FormField::addPanel('admin_label.employee.panel.contacts')->setIcon('fa fa-phone');

        yield EmailField::new('email', 'admin_label.employee.field.email')
            ->addToDefaultColumns(); // <= add it to default columns in list

        yield TelephoneField::new('phone', 'admin_label.employee.field.phone')
            ->addToDefaultColumns(); // <= add it to default columns in list

        yield FormField::addPanel('admin_label.employee.panel.work')->setIcon('fa fa-briefcase');

        yield AssociationField::new('department', 'admin_label.employee.field.department');
        yield TextField::new('position', 'admin_label.employee.field.position');

        if ($this->security->isGranted('ROLE_SUPER_ADMIN')) {
            yield FormField::addTab('admin_label.employee.panel.settings');

            yield FormField::addPanel('admin_label.employee.panel.auth')->setIcon('fa fa-key')->addCssClass('required');

            yield BooleanField::new('active', 'admin_label.employee.field.active');
            yield TextField::new('username', 'admin_label.employee.field.username')->hideOnIndex();
            yield TextField::new('plainPassword', 'admin_label.employee.field.plainPassword')->onlyOnForms()
               ->excludeFromColumnChooser() // never show this field in columnChooser

            yield FormField::addPanel('admin_label.employee.panel.access')->setIcon('fa fa-user-lock');

            yield ArrayField::new('roles', 'admin_label.employee.field.roles')->onlyOnForms();
        }

        yield FormField::addTab('admin_label.common.panel.additional');

        yield IdField::new('id', 'admin_label.common.field.id')->onlyOnDetail();

        yield DateTimeField::new('createdAt', 'admin_label.common.field.createdAt')->onlyOnDetail();
        yield DateTimeField::new('updatedAt', 'admin_label.common.field.updatedAt')->onlyOnDetail();
        yield AssociationField::new('createdBy', 'admin_label.common.field.createdBy')->onlyOnDetail();
        yield AssociationField::new('updatedBy', 'admin_label.common.field.updatedBy')->onlyOnDetail();

        yield DateTimeField::new('deletedAt', 'admin_label.common.field.deletedAt')->onlyOnDetail();

        yield TextField::new('virtualField')->onlyOnDetail();
    }
}

But I'd like to hear comments from @javiereguiluz first.

ksn135 avatar Dec 02 '22 13:12 ksn135

As I https://github.com/EasyCorp/EasyAdminBundle/pull/5495#issuecomment-1333308386 something wrong on my side with assets building tool (MacBook Pro, yarn 1.22.19). Probably padding issue has the same source.

I rebuild from my side (Docker on WSL2) i didn't have any issue during build, but just this padding added.

What storage class are you using? Session?

Currently yes, i don't have any User entity i don't have test with entity Additional information, when i don't define $availableColumns after reset or save & reload all fields appear in modal

Good idea. I thought about it. For example, make it configurable in configureFields:

Exactly ! I think about the same idea with EasyCorp\Bundle\EasyAdminBundle\Field\FieldTrait we can add on each EA included Field and for user custom Field too ! That would be the best implementation we wait for @javiereguiluz response about that 😄

Default : All returned field would be available in modal for selection expect excluded. If all fields don't call addToDefaultColumns() => all field to set default

If at least one call addToDefaultColumns() set only thoses who have called this method


And I just thought of a feature, it would be cool to be able to propose a preset for example in the case of a company with several services that use the data differently, only possible via DB or then via a config file or function like configureCrud for configureColumnChooserPresets([])

Config 1: id name phone

Config 2 : id email address updatedAt

TheoD02 avatar Dec 02 '22 15:12 TheoD02

As I #5495 (comment) something wrong on my side with assets building tool (MacBook Pro, yarn 1.22.19). Probably padding issue has the same source.

I rebuild from my side (Docker on WSL2) i didn't have any issue during build

What tool are you using to build assets? Yarn? What is the version number?

but just this padding added.

I did NOT change any stylesheets. May be it comes from sortablejs?

What storage class are you using? Session? Currently yes, i don't have any User entity i don't have test with entity Additional information, when i don't define $availableColumns after reset or save & reload all fields appear in modal

I'll try to reproduce and fix it.

Good idea. I thought about it. For example, make it configurable in configureFields: Exactly ! I think about the same idea with EasyCorp\Bundle\EasyAdminBundle\Field\FieldTrait we can add on each EA included Field and for user custom Field too ! That would be the best implementation we wait for @javiereguiluz response about that 😄

Yep! Still waiting...

Default : All returned field would be available in modal for selection expect excluded. If all fields don't call addToDefaultColumns() => all field to set default If at least one call addToDefaultColumns() set only thoses who have called this method

it's looks great to me.

And I just thought of a feature, it would be cool to be able to propose a preset for example in the case of a company with several services that use the data differently, only possible via DB or then via a config file or function like configureCrud for configureColumnChooserPresets([])

Config 1: id name phone

Config 2 : id email address updatedAt

I understand your point of view. I think it's better to do it in the configureCrud function

ksn135 avatar Dec 03 '22 13:12 ksn135

What tool are you using to build assets? Yarn? What is the version number?

Using version 1.22.15 (using http://devilbox.org/ stack with docker)

I did NOT change any stylesheets. May be it comes from sortablejs?

I tested without import "sortablejs" but have the same result, in changed files in didn't see anything about styles too.

I think it's better to do it in the configureCrud function

Yes, have time to reflect about what is the best for this implementation

TheoD02 avatar Dec 05 '22 16:12 TheoD02

Hi @TheoD02 !

if I not sent in the configureFields function then the modal returns bad information on fields possibly impossible to display

Please take a look. I hope that finally fixed it.

ksn135 avatar Dec 08 '22 13:12 ksn135

Hello back from long time !

I tested the current state of PR.

Something confuse me, will explain :

I have a basic "post" entity that have this following fields :

  • id
  • title
  • description
  • content
  • createdAt
  • updatedAt

I made a simple Crud for this entity, and configured column chooser with the following :

return parent::configureCrud($crud)
           ->enableColumnChooser()
           ->setColumnChooserSelectedColumnStorageProvider($this->sessionSelectedColumnStorageProvider)
           ->setColumnChooserColumns(
               ['id', 'title'],
               ['content', 'description'],
               ['updatedAt']
           )
       ;

With this configuration, I get the following configuration in admin entity crud page image

I only have id and title fields in modal available to choose. But id, title, description and content should be available to choose that is rights ? Because id/title is on default fields and content/description is on availableColumns. Tested to add id/title in availableColumns too, but didn't change anything.

With this configuration, my expect was to have default list with id/title only showed by default and availability to add/remove field of the availableColumns list.

I can't modify the sort of fields, disable one field of the list didn't save anything that i have changed in session after reload.

-- EDIT After some code review, seems to be caused in the "getCurrentColumns" function it return only id and title instead of id, title, description, content

Replacing :

return array_flip(array_merge(
            array_combine($this->getSelectedColumns(), $this->getSelectedColumns()),
            $this->indexAvailableColumnsWithLabels,
));

With :

return array_flip(array_merge(
            array_combine($this->getSelectedColumns(), $this->getSelectedColumns()),
            $this->indexAvailableColumnsWithLabels,
            array_combine($this->getAvailableColumns(), $this->getAvailableColumns()),
));

after this change all seem fine for now, need more testing but fix the above problem 😄

TheoD02 avatar Jan 17 '23 10:01 TheoD02

Hello @TheoD02 !

after this change all seem fine for now, need more testing but fix the above problem 😄

O! Great news!

PS: Just in time! I just started building a test project from scratch https://github.com/ksn135/ea-cc-test Hope it's no longer needed! )

PS2: I didn't see your last edit in the GitHub's letter Just now I saw that you already found a solution!

ksn135 avatar Jan 17 '23 17:01 ksn135

renamed:    src/Interfaces/SelectedColumnStorageProviderInterface.php -> src/Contracts/ColumnStorage/SelectedColumnStorageProviderInterface.php
renamed:    src/Interfaces/UserParametersStorageInterface.php -> src/Contracts/ColumnStorage/UserParametersStorageInterface.php

ksn135 avatar Jan 17 '23 19:01 ksn135

Hi @TheoD02!

Finally everything is working as expected. Please check it out.

Don't forget to enable session for anonymous users in security.yaml.

Screenshot 2023-01-20 at 13 01 49

PS: As I mentioned before, I've also created a test project https://github.com/ksn135/ea-cc-test

ksn135 avatar Jan 20 '23 10:01 ksn135

Hi @javiereguiluz! Please check this PR. I hope it's finally done.

ksn135 avatar Jan 20 '23 18:01 ksn135

Hi, Checked the project you provided.

Seem to be fine now 😄

I found a case that just for user experience : When no one case is checked, prevent possibility to click on "Save and reload" button and maybe show a message for informate the user at least 1 column is mandatory

Another one idea come in my head, is to add a bridge for manage column for each entity directly from admin. That will prevent for example make a change in a sprint, and wait X weeks before having a change in production.

I imagine that with optional extension package to install to let user the choice to use this if wanted and not overload EA with table.

Adding one function to choose between configuration in code, or through the package from admin (DashboardController, possible override in each Crud ? Is really necessary ?)

TheoD02 avatar Jan 23 '23 08:01 TheoD02

Hi!

Seem to be fine now 😄

Super!

I found a case that just for user experience : When no one case is checked, prevent possibility to click on "Save and reload" button and maybe show a message for informate the user at least 1 column is mandatory

Ok, I'll make it later.

Another one idea come in my head, is to add a bridge for manage column for each entity directly from admin.

Well, I don't understand you very well.

I have already implemented the ability to store user column settings in the database in the table of user parameters, not in the attributes of the session via UserSelectedColumnStorageProvider Screenshot 2023-01-24 at 11 01 04 That's exactly how it works for me. Screenshot 2023-01-24 at 10 57 47

ksn135 avatar Jan 24 '23 08:01 ksn135

Hi, I'm ready to help. Apparently we don't fully understand each other. As I wrote earlier, the current version allows you to store user settings not only in the session, but also in the database. If we're talking about the default settings, they can probably also be stored and easily retrieved from the database. For example, you can take default columns from the database and pass them as a parameter to setColumnChooserColumns function.

ksn135 avatar Mar 01 '23 22:03 ksn135