Chapters
-
Course Code
Subscribe to download the code!
Subscribe to download the code!
-
This Video
Subscribe to download the video!
Subscribe to download the video!
-
Subtitles
Subscribe to download the subtitles!
Subscribe to download the subtitles!
-
Course Script
Subscribe to download the script!
Subscribe to download the script!
configureCrud()
Scroll down to the script below, click on any sentence (including terminal blocks) to jump to that spot in the video!
Keep on Learning!
If you liked what you've learned so far, dive in! Subscribe to get access to this tutorial plus video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeIf you look at the index page of any of the crud sections, it doesn't sort by default. It just... lists things in whatever order they come out of the database. It would nice to change that: to sort by a specific column whenever we go to a section.
So far, we've talked about configuring assets, fields and actions. But "how a crud section sorts"... doesn't fall into any of these categories. Nope, for the first time, we need to configure something on the "crud section" as a whole.
Go to one of your crud controllers and open its base class. We know that there are a number of methods that we can override to control things... and we've already done that for many of these. But we have not yet used configureCrud()
. As its name suggests, this is all about controlling settings on the entire crud section.
And just like with most of the other methods, we can override this in our dashboard controller to make changes to all crud sections, or override it in one specific crud controller.
Sorting All Crud Sections
Let's see if we can set the default sorting across our entire admin to sort by id descending. To do that, open DashboardController
and, anywhere inside, go to Code -> Generate - or command+N on a Mac - select override methods and choose configureCrud()
.
Show Lines
|
// ... lines 1 - 21 |
class DashboardController extends AbstractDashboardController | |
{ | |
Show Lines
|
// ... lines 24 - 58 |
public function configureCrud(): Crud | |
{ | |
Show Lines
|
// ... lines 61 - 64 |
} | |
Show Lines
|
// ... lines 66 - 77 |
} |
The Crud
object has a bunch of methods on it... including one called setDefaultSort()
. That sounds handy! Pass that the array of the fields we want to sort by. So, id
set to DESC
.
Show Lines
|
// ... lines 1 - 58 |
public function configureCrud(): Crud | |
{ | |
return parent::configureCrud() | |
->setDefaultSort([ | |
'id' => 'DESC', | |
]); | |
} | |
Show Lines
|
// ... lines 66 - 79 |
Back over at the browser, when we click on "Questions"... beautiful! By default, it now sorts by id. Really, all sections now sort by id.
Sorting Differently in One Section
And what if we want to sort Questions in a different way than the default? I bet you already know how we would do that. So let's make things more interesting. Every Question
is owned by a User
. What if we wanted to show the questions whose users are enabled first? Can we do that?
First, since we want to only apply this to the questions section, we need to make this change inside of QuestionCrudController
. Go to the bottom and... same thing: override configureCrud()
... and call the exact same method as before: setDefaultSort()
. Now we can say, askedBy
- that's the property on Question
that is a relation to User
- askedBy.enabled
. Set this to DESC
.
And then, after sorting by enabled first, sort the rest by createdAt
DESC
.
Show Lines
|
// ... lines 1 - 14 |
class QuestionCrudController extends AbstractCrudController | |
{ | |
Show Lines
|
// ... lines 17 - 21 |
public function configureCrud(Crud $crud): Crud | |
{ | |
return parent::configureCrud($crud) | |
->setDefaultSort([ | |
'askedBy.enabled' => 'DESC', | |
'createdAt' => 'DESC', | |
]); | |
} | |
Show Lines
|
// ... lines 30 - 65 |
} |
Let's check it! Click "Questions". Because we're sorting across multiple fields, you don't see any header highlighted as the currently-sorted column. But... it looks right, at least based on the "Created At" dates.
To know for sure, click the database link on the web debug toolbar. Then... search this page for "ORDER BY". There it is! Click "View formatted query". And...yes! It's ordering by user.enabled
and then createdAt
. Pretty cool.
Disabling Sorting on a Field
So we now have default sorting... though the user can, of course, still click any header to sort by whichever column they want. But sometimes, sorting by a specific field doesn't make sense! You can see that it's already disabled for "answers".
And if we go over to... let's see... the Users section, there's also no sort for the avatar field, which also seems reasonable.
If you want to control this a bit further, you can. Like, let's pretend that we don't want people sorting by the name of the question. This is something we can configure on the field itself.
In QuestionCrudController
, for the name field, call setSortable(false)
.
Show Lines
|
// ... lines 1 - 30 |
public function configureFields(string $pageName): iterable | |
{ | |
Show Lines
|
// ... lines 33 - 41 |
yield Field::new('name') | |
->setSortable(false); | |
Show Lines
|
// ... lines 44 - 65 |
} | |
Show Lines
|
// ... lines 67 - 68 |
And... just like that, the arrow is gone.
Inlining Controls for an Admin Section
Before we move on - because there isn't a ton that we need to control on the CRUD-level, let me show you one more thing. Head to the Topics section. This entity is really simple... so we have plenty of space here to show these fields.
Normally, the "actions" on the index page are hidden under this dropdown. But, we can render them inline.
To do that, head to TopicCrudController
... go down... and override configureCrud()
. On the Crud
object, call ->showEntityActionsInlined()
.
Show Lines
|
// ... lines 1 - 10 |
class TopicCrudController extends AbstractCrudController | |
{ | |
Show Lines
|
// ... lines 13 - 17 |
public function configureCrud(Crud $crud): Crud | |
{ | |
return parent::configureCrud($crud) | |
->showEntityActionsInlined(); | |
} | |
Show Lines
|
// ... lines 23 - 39 |
} |
That's it. Now... yea! That looks better.
Next: I want to talk about using a what-you-see-is-what-you-get editor. There's actually a simple one built into Easy Admin. But we're going to go further and install our own. Doing that will require some custom JavaScript.
25 Comments
Hey Ruslan,
Hm, it sounds like you should override the createIndexQueryBuilder()
method from the EA controller and make sure you always have your default sorting. Actually, I think you should drop that setDefaultSort()
at all in favor this solution, as setDefaultSort()
is meant to be changed, i.e. it's just a default sort. What you need - is a bit different, you always have to have that sorting :)
Cheers!
Hello
I am trying to localize the EasyAdmin, and it is going well so far. However, I have one big problem that I can not resolve. To change the title of the index page in EasyAdmin to be as I want it, I use:
public function configureCrud(Crud $crud): Crud
{
return $crud->setEntityLabelInPlural('Χρήστες') // Χρήστες = Users
->showEntityActionsInlined(true);
}
My big problem is: I want now to customize the label on the add new button on the index page. Currently the label is: "Δημιουργία Χρήστης" which is grammatically wrong. It should either say "Δημιουργία χρήστη" or "Δημιουργία νέου χρήστη" but the best is if I can write what ever I need, for example, to use Ryan's favorite terminology "Δημιουργήστε νέο δεινόσαυρο" which means "Create new dinosaur"
So, how can I create any label that I want for the create new button? Which method should I override?
Cheers
Hey @t5810
I believe you can do something like this:
public function configureActions(Actions $actions): Actions
{
return $actions
// ...
->update(Crud::PAGE_INDEX, Action::NEW, function (Action $action) {
return $action->setLabel('Χρήστες');
})
;
}
I got the example from the docs but didn't test it, I hope it helps!
https://symfony.com/bundles/EasyAdminBundle/current/actions.html#updating-an-action
Hi MolloKhan
It works like a charm. Thank you!
Regards
Hi, I have a complex (for me) problem:
In my project I have an entity with latitude, longitude and address properties. I use leaflet to enable completing these field by clickin on a map.
So I use
public function configureCrud(...)...
{
return $crud
->overrideTemplate('...mapZone.html.twig');
}```
since each field depends on the map.
First I don't know if it is a good approach for my problem.
How to pass the data in my mapZone.html.twig ? How can I save my input from the twig ?
Should I use a form ? In that case, where can I create it to render it ? How to validate the form when submitted ?
Should I use a standard controller to use a form ? If so, how to connect it with the crudcontroller ?
Hey mofogasy!
I think what you basically want to do is:
A) Create a Stimulus controller that will handle initializing leaflet and triggering the functionality. This would contain the code where, in click, you find the 3 fields (latitude, longitude and address) and fill in their values.
B) For those 3 fields, I think there is no reason for the user to see them, so I would make them each a HiddenField. To make your life easier in step (A) (specifically, finding the 3 fields), you could make each of them a "target" for your controller, like we do here for the question
field: https://symfonycasts.com/screencast/easyadminbundle/field-stimulus#codeblock-e423030bfc
C) To initialize the Stimulus controller, hmm. I think (?) you could use https://symfony.com/bundles/EasyAdminBundle/current/crud.html#templates-and-form-options to add an attr
option to your form:
->setFormOptions([
'attr' => ['data-controller' => 'your-controller-name'],
])
I believe that will cause the data-controller attribute to be added to your form
tag, which will initialize your controller. That Stimulus controller will now initialize leaflet, which will handle setting the hidden fields on click.
D) How to handle validation? Well, first, my guess is that validation won't happen very often: if a user is clicking a map, then latitude, longitude and address should be "correct" for normal users. But, of course, we should still validate just in case. To do this, I would probably use the Callback validation on your entity - https://symfony.com/doc/current/reference/constraints/Callback.html - that allows you to write some custom code that has access to all of the fields... in case you want to look at them all at once. If there are any problems, just fail, but don't call ->atPath()
. If you don't call ->atPath()
, then the error should be attached to the "top level" of the form. So even though the fields are hidden, an error will show up at the top of the form.
Let me know if that helps!
Cheers!
Hi, I manage to progress a bit.
I found a very convinient file for my problem : <i>vendor/easycorp/easyadmin-bundle/src/Resources/views/crud/new.html.twig</i>
I pasted this file's content in mapZone.html.twig.
Now, I have the map and the appropriate form on the page.
With only javascript I can dymanically fill the inputs when I click on the map :<br />document.getElementById("ZoneTournee_points_reperes_latitude").value = event.latlng.lat<br />
Now I am moving to the next level. In fact I need to add several markers at a time. I have AdresseRepere entity which represent the locations, it's related to ZoneTournee entity which is a collection of AdresseRepere (it's an area/polygon).
My ZoneTourneeCrudController has
`
yield CollectionField::new('points_reperes')
->setEntryType(AdresseRepereType::class)
->addCssClass("numtournee");
`
but now I am a bit limited with javascript tricks: by inspecting the browser , I could manage to catch "add new item" button and trigger it when I click on the map :
<br />document.querySelector('.form-group.field-collection .field-collection-add-button').click();<br />
Since this class is generated by easyadmin, do you have a tip to make it cleaner (like adding id attribute to this "add new item" button) ?
Filling works great.
But removing is still in process.
I cannot access a specific "remove the item" button since they all have same class. I have to get the child of nextsibling of the button's parent to get a numbered item attribute which can be tough.
Do you have a trick to get esasyadmin to add index attribute (id) to the global div of a new collection field item ?
Hi, thank you for the answer.
Well, I kinda dislike stimulus because it rarely gives feedback when there's something wrong in the code. And it messes other webpack modules: for example, bootstrap dropdown stopped working because of my stimulus. But I couldn't specify the cause because no error.
I ll definitly give it a try. I 'll give my solution later if it works
Well, I could not find the a way to custom each collection item. I am stuck
thanks
Sorry I couldn't help more - your previous reply landed while I was on vacation, and this stuff is deep and complex.
don't worry,
for now i'll try with javascript DOM first. My code will be trash but I ll keep it until I find out where I can customize collectionfield
Hello, Is it possible to access the Entity when editing in configureCrud() to set a special help info?
The idea is to display a different message with the type of my Entity (image, video, text)->setHelp('edit', 'Template.Help.'.$entity->getType()
Hey Julien,
I see your point... well, that's not something that easy to do. As you see, the signature of configureCrud() does not allow to inject the current entity object, but you probably could try to do some tricks to do what you want. First of all, you can override some templates to add more custom code base on some conditions, like entity type, etc. Overriding templates might be the most correct way usually.
But if it does not fit you, we can try to get access to the AdminContextProvider service in order to get the AdminContext from it. I believe you can do it with $this->container->get(AdminContextProvider::class)->getContenxt(). With AdminContext you can get the current entity with $adminContext->getEntity()->getInstance(). But probably you even don't need the actual entity because getEntity() gives you the entityDto that contains a lot of useful information that might be enough for you, you can try to dump it first.
I hope this helps!
Cheers!
Ok thank you for your quick answer. Maybe create a custom template for edit should be better indeed. i wanted to be sure there is no other easy way^^
Hey Julien,
No easy way, but there might be some workarounds I mentioned :) Good luck with template overriding!
Cheers!
I think sorting by the number of objects in a collection field should be the default setting, but is there a simple way to implement that without making a custom filter or creating a completely separate database column on the owning entity just to store the collection count?
Hey Nick F.!
Hmm, that's actually a great question - I'm not sure. Even in SQL, this is a bit trickier, as it requires a JOIN over to the collection table, then a COUNT... on that collection. My guess (?) is that there is not a simple way to do this. I'd try 'COUNT(articles)' => 'DESC'
.... but I don't feel lucky enough to expect that to work :p.
Otherwise, you could always take manual control over the "index query builder" - e.g. https://symfonycasts.com/screencast/easyadminbundle/multiple-crud#codeblock-b52430ba65 - and then add the order by manually. You'd still need to figure out how to do it correctly with the query builder, but that should be possible. Here's some info - https://stackoverflow.com/questions/6000622/how-to-order-by-count-in-doctrine-2#answer-13652479 - specifically solution 3 on that answer is, I think, what you'd need. Use ->addSelect('COUNT(something.id) AS HIDDEN mycount')
to "add" the count into the select.
Let me know if that helps :).
Cheers!
Hello,
It is possible for specific EntityCrudController
change on Crud::PAGE_INDEX
a <b>button text</b> for Action::NEW
?
For my specific entity better named button "Create Entity" instead of "Add Entity"
Thank you
Hey Tomas,
Yes, you can... you need to rename the translation for that button. The original translation is located in "src/Resources/translations/EasyAdminBundle.en.php" of the bundle code, see: 'new' => 'Add %entity_label_singular%'
there. So, basically you need to override that translation in your code, the full key of that translation method should be "action.new".
Cheers!
Hello Victor,
thank you for your help. This probably changes button label on whole application. I want to change only on specific EntityCrudController. There is word "Create" more appropriate than word "New".
Regards!
Hey Tomas,
in that case, you can override the label directly on the configureFields()
method of your controller.
Cheers!
Hello Diego,
Thank you for help me. I thing the method configureFields is for specifying which field should be displayed and how. I have inspected source code of this method and I dont know how to do. Could you please be more specific how to rename Action Button for creating new record on a specific CRUD page?
Cheers!
Oh, sorry, I got confused. In the configureActions()
method, you can do something like this
$actions->getAsDto(Crud::PAGE_NEW)
->getAction(Crud::PAGE_INDEX, Action::NEW)
->setLabel('New label here');
Hey Diego,
It is working! Your are awesome.
Here is complete method which I have and works
`
public function configureActions(Actions $actions): Actions
{
$actions
->getAsDto(Crud::PAGE_NEW)
->getAction(Crud::PAGE_INDEX, Action::NEW)
->setLabel('__ea__action.create');
return parent::configureActions($actions)
->remove(Crud::PAGE_INDEX, Action::DELETE)
->remove(Crud::PAGE_INDEX, Action::EDIT);
}
`
Cheers!
"Houston: no signs of life"
Start the conversation!
What PHP libraries does this tutorial use?
// composer.json
{
"require": {
"php": ">=8.1.0",
"ext-ctype": "*",
"ext-iconv": "*",
"composer/package-versions-deprecated": "^1.11", // 1.11.99.4
"doctrine/doctrine-bundle": "^2.1", // 2.5.5
"doctrine/doctrine-migrations-bundle": "^3.0", // 3.2.1
"doctrine/orm": "^2.7", // 2.10.4
"easycorp/easyadmin-bundle": "^4.0", // v4.0.2
"handcraftedinthealps/goodby-csv": "^1.4", // 1.4.0
"knplabs/knp-markdown-bundle": "dev-symfony6", // dev-symfony6
"knplabs/knp-time-bundle": "^1.11", // 1.17.0
"sensio/framework-extra-bundle": "^6.0", // v6.2.5
"stof/doctrine-extensions-bundle": "^1.4", // v1.7.0
"symfony/asset": "6.0.*", // v6.0.1
"symfony/console": "6.0.*", // v6.0.2
"symfony/dotenv": "6.0.*", // v6.0.2
"symfony/flex": "^2.0.0", // v2.4.5
"symfony/framework-bundle": "6.0.*", // v6.0.2
"symfony/mime": "6.0.*", // v6.0.2
"symfony/monolog-bundle": "^3.0", // v3.7.1
"symfony/runtime": "6.0.*", // v6.0.0
"symfony/security-bundle": "6.0.*", // v6.0.2
"symfony/stopwatch": "6.0.*", // v6.0.0
"symfony/twig-bundle": "6.0.*", // v6.0.1
"symfony/ux-chartjs": "^2.0", // v2.0.1
"symfony/webpack-encore-bundle": "^1.7", // v1.13.2
"symfony/yaml": "6.0.*", // v6.0.2
"twig/extra-bundle": "^2.12|^3.0", // v3.3.7
"twig/twig": "^2.12|^3.0" // v3.3.7
},
"require-dev": {
"doctrine/doctrine-fixtures-bundle": "^3.3", // 3.4.1
"symfony/debug-bundle": "6.0.*", // v6.0.2
"symfony/maker-bundle": "^1.15", // v1.36.4
"symfony/var-dumper": "6.0.*", // v6.0.2
"symfony/web-profiler-bundle": "6.0.*", // v6.0.2
"zenstruck/foundry": "^1.1" // v1.16.0
}
}
Hello
I have Many-To-Many relation table Article - Image and ArticleMediaCrudController. In Atricle column I display article.name. Then I made the default sort for Article column:
setDefaultSort(['article.name' => 'ASC'])
and it working. But how to leave this sorting if I change sorting direction of this column to Up or Down?