[FEATURE] Column chooser
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.
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:
Add video:
https://user-images.githubusercontent.com/230304/204554873-ca81ac84-4360-4c0d-b14f-6f60c7a66d58.mov
Selected columns saved in user session:
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.

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
)
;
}
nice feature <3 . great job mann
@javiereguiluz please review this PR and tell me what you think about it. Thanks in advance.
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.
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

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

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

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 :)
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.
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.
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
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
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
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.
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

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 😄
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!
renamed: src/Interfaces/SelectedColumnStorageProviderInterface.php -> src/Contracts/ColumnStorage/SelectedColumnStorageProviderInterface.php
renamed: src/Interfaces/UserParametersStorageInterface.php -> src/Contracts/ColumnStorage/UserParametersStorageInterface.php
Hi @TheoD02!
Finally everything is working as expected. Please check it out.
Don't forget to enable session for anonymous users in security.yaml.
PS: As I mentioned before, I've also created a test project https://github.com/ksn135/ea-cc-test
Hi @javiereguiluz! Please check this PR. I hope it's finally done.
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 ?)
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
That's exactly how it works for me.

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.