gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
The methods configureAssets()
, configureCrud()
, configureActions()
and configureFilters()
all live here inside of AbstractCrudController
. And each gives us a way to control different parts of the CRUD section.
But, these methods also live inside of AbstractDashboardController
. Here's configureAssets()
, and then further down, we see the methods for, CRUD, actions and filters.
But... that doesn't make sense! The dashboard controller just renders... the dashboard page. And that page doesn't have any actions or any CRUD to configure. What's going on here?
Click "Questions" and look at the URL. It starts with /admin
and then has a bunch of query parameters. It turns out that everything in EasyAdmin is handled by a single giant route. It all runs through the DashboardController
route - the /admin
route that's above index()
:
... lines 1 - 15 | |
class DashboardController extends AbstractDashboardController | |
{ | |
... line 18 | |
'/admin', name: 'admin') ( | |
public function index(): Response | |
{ | |
... line 22 | |
} | |
... lines 24 - 38 | |
} |
So when we go to QuestionCrudController
, it's actually matching this route here with extra query parameters to say which CRUD controller and which action to run. You can see crudController
and crudAction
hiding in the URL. Yup, we're rendering QuestionCrudController
, but in the context of DashboardController
.
And when we go to this page, in order to get the CRUD config, EasyAdmin first calls configureCrud()
on our dashboard controller. Then it calls configureCrud()
on the specific CRUD controller, in this case, QuestionCrudController
. This is incredibly powerful. It means that we can configure things inside of our dashboard - and have those apply to every section in our admin - or configure things inside of one specific CRUD controller to only change the behavior for that one section.
We can prove it! Go back to AbstractDashboardController
. Look at configureCrud()
. Every CRUD section has 4 pages. Hold Cmd
or Ctrl
and click to open this Crud
class. Check out the constants on top. Every CRUD section has an index page - that's this - an edit page, a new page, and also a detail page. Each page can then have links and buttons to a set of actions. For example, on the index page, right now, we have an action for editing, an action for deleting... and also an action on top to add a new question. And, of course, this is all something we can control.
You can see how this is configured down in configureActions()
. Because we're inside of the dashboard controller class, this is the default action configuration that applies to every CRUD section. You can see that, for the index page, it adds NEW
, EDIT
and DELETE
actions. For the detail page, there's EDIT
, INDEX
, and DELETE
. And if you're on the edit page, you have the actions SAVE_AND_RETURN
and SAVE_AND_CONTINUE
.
If you look closely, you'll notice that while we do have a detail page, nobody links to it! We don't see an action called DETAIL
on any of these pages. So the page exists, but it's not really used out-of-the-box. Let's change that!
Go back to DashboardController
. It doesn't matter where, but I'll go down to the bottom, go to "Code"->"Generate..." - or Cmd
+ N
on a Mac - click "Override Methods" and select configureActions()
:
... lines 1 - 9 | |
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions; | |
... lines 11 - 18 | |
class DashboardController extends AbstractDashboardController | |
{ | |
... lines 21 - 42 | |
public function configureActions(): Actions | |
{ | |
return parent::configureActions() | |
... line 46 | |
} | |
} |
We do want to call the parent
method so that it can create the Actions
object and set up all of those default actions for us. Let's add a link to the "detail" page from the "index" page. In EasyAdmin language, this means we want to add a detail action to the index page. Do that by saying ->add()
, passing the page name - Crud::PAGE_INDEX
- and then the action: Action::DETAIL
:
... lines 1 - 8 | |
use EasyCorp\Bundle\EasyAdminBundle\Config\Action; | |
... line 10 | |
use EasyCorp\Bundle\EasyAdminBundle\Config\Crud; | |
... lines 12 - 18 | |
class DashboardController extends AbstractDashboardController | |
{ | |
... lines 21 - 42 | |
public function configureActions(): Actions | |
{ | |
return parent::configureActions() | |
->add(Crud::PAGE_INDEX, Action::DETAIL); | |
} | |
} |
Thanks to this, when we refresh the index page of QuestionCrudController
... we have a "Show" link that goes to the DETAIL
action! And you'll see this on every section of our admin! Yup, we just modified every CRUD controller in the system!
But, since the Topic
entity is so simple, let's disable the DETAIL
action for just this section. To do that, open up TopicCrudController
, and, just like before, go to "Code"->"Generate..." - or Cmd
+ N
on a Mac - hit "Override Methods" and select configureActions()
:
... lines 1 - 6 | |
use EasyCorp\Bundle\EasyAdminBundle\Config\Actions; | |
... lines 8 - 9 | |
class TopicCrudController extends AbstractCrudController | |
{ | |
... lines 12 - 16 | |
public function configureActions(Actions $actions): Actions | |
{ | |
return parent::configureActions($actions) | |
... line 20 | |
} | |
... lines 22 - 32 | |
} |
By the time this method is called, it will pass us the Actions
object that was already set up by our dashboard. So it will already have the detail action enabled for the index page. But now, we can change that by saying ->disable(Action::DETAIL)
:
... lines 1 - 5 | |
use EasyCorp\Bundle\EasyAdminBundle\Config\Action; | |
... lines 7 - 9 | |
class TopicCrudController extends AbstractCrudController | |
{ | |
... lines 12 - 16 | |
public function configureActions(Actions $actions): Actions | |
{ | |
return parent::configureActions($actions) | |
->disable(Action::DETAIL); | |
} | |
... lines 22 - 32 | |
} |
We'll talk more about the actions configuration later. But these are the main things that you can do inside of them: add a new action to a page, or completely disable an action. Now, when we refresh, our DETAIL
action is gone! But if we go to any other section, it's still there.
The big takeaway is that everything is processed through our DashboardController
, which means that we can configure things on a dashboard-level, which will apply to all of our CRUDs, or we can configure things for one specific CRUD.
The fact that all of the CRUD controllers go through this /admin
URL has one other effect related to security. It means that all of our controllers are already secure. That's thanks to our access_control
.
Remember, back in config/packages/security.yaml
, we added an access_control
that said if the URL starts with /admin
, require ROLE_ADMIN
:
security: | |
... lines 2 - 38 | |
access_control: | |
- { path: ^/admin, roles: ROLE_ADMIN } | |
... lines 41 - 55 |
This means that without doing anything else, every CRUD controller and action in our admin already requires ROLE_ADMIN
. We'll talk more later about how to secure different admin controllers with different roles... but at the very least, you need to have ROLE_ADMIN
to get anywhere, which is awesome.
But one important point: adding this access_control
was necessary. Why? The index()
action in our dashboard is what holds the one route. When we go to a CRUD controller, like this, it does match this route.... but EasyAdmin does something crazy. Instead of allowing Symfony to call this controller, it sees this crudController
query parameter and magically switches the controller to be the real controller. In this case, it changes it to QuestionCrudController::index()
.
You can see this down on the web debug toolbar. If you hover over "@admin", this tells you that the matched route name was admin
. So, yes, the route is matching the main dashboard route. But the controller is QuestionCrudController::index()
.
This means that the method in your CRUD controller is what Symfony ultimately executes. In this case, it's the index()
method in this AbstractCrudController
... down here. This is the real controller for the page.
Why does that matter? First, it's nice to know that, even with all the EasyAdmin coolness and magic, at the end of the day, the actions in our controller are real actions that are called like any normal action. And second, this is important for security. Because if we had only put the IsGranted
above index()
and not added the access_control
, that would not have been enough. Why? Because this isGranted
attribute is only enforced when you execute this action. So, when we go to the dashboard page.
Anyways, if some of this is still a bit fuzzy, no worries! This was a blast of EasyAdmin theory that'll help us understand things better as we dig and experiment.
Next, before we go deeper into our CRUD controllers, let's mess around a bit more with our dashboard by adding some custom links to our admin menu and user menu.
Hey Dejan94,
We're sorry to hear you could not find the information you were looking for. Actually, we covered the topics you mentioned in this course. Did you start watching this course from the beginning? Did you watch the course till the end? In every new chapter, we add cover more and more topics. Because the bundle is so rich in features it's impossible to cover all possible features related to the CRUD controllers in a single video, that's why we split them into sub-topics and covered them in many videos. Please, watch this tutorial till the end to see the full picture of the bundle and its features.
Cheers!
Hey Dejan94,
Awesome, glad to hear it! But in case we really missing something in this course - feel free to share :)
Cheers!
Hi,
I had to put this line : "->add(Crud::PAGE_INDEX, Action::DETAIL)"
into the AbstractDashboardController because if I put it like you say (@4:18), I get this error from my IDE:
"Return type declaration must be compatible with AbstractDashboardController->configureActions() : \EasyCorp\Bundle\EasyAdminBundle\Config\Actions".
Why this ?
Cordialement, Richard.
I found the issue I made thanks to GIT : I wrote previously instead of page_index :
->add(Crud::**PAGE_DETAIL**,Action::DETAIL);
That' why I had the issue and sorry for that, it's just a matter of age, unfortunatly.
Merci et bonne journée.
// 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.0.1
"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
}
}
This tutorial is not good, how to setup links for menu items, how to setupd crud controllers ?