If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
Another chapter, another problem to solve! I need to hide the delete button on the
list page if an entity is published. So... nope! We can't just go into config.yml
and add -delete
. We need to override the list.html.twig
template and take control
of those actions manually.
Copy that file. Then, up inside our views
directory, I want to show the other
way of overriding templates: by convention. Create a new easy_admin
directory,
and paste the template there and... that's it! EasyAdminBundle will automatically
know to use our list template.
The toughest thing about overriding a template is... well... figuring out what
variables you can use! In list.html.twig
... how about in the content_header
block, add {{ dump() }}
:
... lines 1 - 38 | |
{% block content_header %} | |
{{ dump() }} | |
... lines 41 - 93 | |
{% endblock content_header %} | |
... lines 95 - 245 |
And in _id.html.twig
, do the same:
{{ dump() }}<i class="fa fa-key"></i> {{ value }} |
I want to see what the variables look like in each template.
Ok, refresh the genus list page! Awesome! This first dump is from list.html.twig
.
It has the same fields
configuration we've been looking at in the profiler, a
paginator
object and a few other things, including configuration for this specific
section.
The other dumps come from _id.html.twig
. The big difference is that we're rendering
one Genus
each time this template is called. So it has an item
variable
set to the Genus
object. That will be super handy. If some of the other keys
are tough to look at, remember, a lot of this already lives in the EasyAdminBundle
profiler area.
Ok, take out those dumps! So, how can we hide the delete button for published genuses? It's actually a bit tricky.
In list.html.twig
, if you search, there is a variable called _list_item_actions
:
... lines 1 - 94 | |
{% block main %} | |
{% set _list_item_actions = easyadmin_get_actions_for_list_item(_entity_config.name) %} | |
... lines 97 - 189 | |
{% endblock main %} | |
... lines 191 - 244 |
This contains information about the actions that should be rendered for each row.
It's used further below, in a block called item_actions
:
... lines 1 - 94 | |
{% block main %} | |
{% set _list_item_actions = easyadmin_get_actions_for_list_item(_entity_config.name) %} | |
<div class="table-responsive"> | |
<table class="table"> | |
... lines 100 - 129 | |
<tbody> | |
{% block table_body %} | |
{% for item in paginator.currentPageResults %} | |
... line 133 | |
<tr data-id="{{ _item_id }}"> | |
... lines 135 - 143 | |
{% if _list_item_actions|length > 0 %} | |
... line 145 | |
<td data-label="{{ _column_label }}" class="actions"> | |
{% block item_actions %} | |
{{ include('@EasyAdmin/default/includes/_actions.html.twig', { | |
... lines 149 - 153 | |
}, with_context = false) }} | |
{% endblock item_actions %} | |
</td> | |
{% endif %} | |
</tr> | |
... lines 159 - 164 | |
{% endfor %} | |
{% endblock table_body %} | |
</tbody> | |
</table> | |
</div> | |
... lines 170 - 189 | |
{% endblock main %} | |
... lines 191 - 244 |
The template it renders - _actions.html.twig
- generates a link at the end
of the row for each action.
Let's dump _list_item_actions
to see exactly what it looks like.
Ah, ok! It's an array with 3 keys: edit
, show
and delete
. We need to remove
that delete
key, only if the entity is published. But how?
Here's my idea: if we override the item_actions
block, we could remove the
delete
key from the _list_item_actions
array and then call the parent item_actions
block. It would use the new, smaller _list_item_actions
.
Start by deleting everything and extending the base layout: @EasyAdmin/default/list.html.twig
...
so that we don't need to duplicate everything:
{% extends '@EasyAdmin/default/list.html.twig' %} | |
... lines 2 - 8 |
Next, add block item_actions
and endblock
:
{% extends '@EasyAdmin/default/list.html.twig' %} | |
{% block item_actions %} | |
... lines 4 - 6 | |
{% endblock %} |
Twig isn't really meant for complex logic like removing keys from an array. But,
to accomplish our goal, we don't have any other choice. So, set _list_item_actions
= _list_item_actions|filter_admin_actions(item)
:
{% extends '@EasyAdmin/default/list.html.twig' %} | |
{% block item_actions %} | |
{% set _list_item_actions = _list_item_actions|filter_admin_actions(item) %} | |
... lines 5 - 6 | |
{% endblock %} |
That filter does not exist yet: we're about to create it.
Just to review, open up the original list.html.twig
. The _list_item_actions
variable
is set up here:
... lines 1 - 94 | |
{% block main %} | |
{% set _list_item_actions = easyadmin_get_actions_for_list_item(_entity_config.name) %} | |
... lines 97 - 189 | |
{% endblock main %} | |
... lines 191 - 244 |
Later, the for
loop creates an item
variable...
... lines 1 - 94 | |
{% block main %} | |
{% set _list_item_actions = easyadmin_get_actions_for_list_item(_entity_config.name) %} | |
<div class="table-responsive"> | |
<table class="table"> | |
... lines 100 - 129 | |
<tbody> | |
{% block table_body %} | |
{% for item in paginator.currentPageResults %} | |
... lines 133 - 164 | |
{% endfor %} | |
{% endblock table_body %} | |
</tbody> | |
</table> | |
</div> | |
... lines 170 - 189 | |
{% endblock main %} | |
... lines 191 - 244 |
which we have access to in the item_actions
block:
... lines 1 - 94 | |
{% block main %} | |
{% set _list_item_actions = easyadmin_get_actions_for_list_item(_entity_config.name) %} | |
<div class="table-responsive"> | |
<table class="table"> | |
... lines 100 - 129 | |
<tbody> | |
{% block table_body %} | |
{% for item in paginator.currentPageResults %} | |
... line 133 | |
<tr data-id="{{ _item_id }}"> | |
... lines 135 - 143 | |
{% if _list_item_actions|length > 0 %} | |
... line 145 | |
<td data-label="{{ _column_label }}" class="actions"> | |
{% block item_actions %} | |
{{ include('@EasyAdmin/default/includes/_actions.html.twig', { | |
actions: _list_item_actions, | |
request_parameters: _request_parameters, | |
translation_domain: _entity_config.translation_domain, | |
trans_parameters: _trans_parameters, | |
item_id: _item_id | |
}, with_context = false) }} | |
{% endblock item_actions %} | |
</td> | |
{% endif %} | |
</tr> | |
... lines 159 - 164 | |
{% endfor %} | |
{% endblock table_body %} | |
</tbody> | |
</table> | |
</div> | |
... lines 170 - 189 | |
{% endblock main %} | |
... lines 191 - 244 |
Phew! All we need to do now is create that filter! In src/AppBundle/Twig
, create
a new PHP class: EasyAdminExtension
. To make this a Twig extension, extend
\Twig_Extension
:
... lines 1 - 2 | |
namespace AppBundle\Twig; | |
... lines 4 - 6 | |
class EasyAdminExtension extends \Twig_Extension | |
{ | |
... lines 9 - 26 | |
} |
Then, go to the Code
->Generate
menu - or Command
+N
on a Mac - and override
the getFilters()
method:
... lines 1 - 2 | |
namespace AppBundle\Twig; | |
... lines 4 - 6 | |
class EasyAdminExtension extends \Twig_Extension | |
{ | |
public function getFilters() | |
{ | |
... lines 11 - 16 | |
} | |
... lines 18 - 26 | |
} |
Here, return an array with the filter we need:
new \Twig_SimpleFilter('filter_admin_actions', [$this, 'filterActions'])
:
... lines 1 - 2 | |
namespace AppBundle\Twig; | |
... lines 4 - 6 | |
class EasyAdminExtension extends \Twig_Extension | |
{ | |
public function getFilters() | |
{ | |
return [ | |
new \Twig_SimpleFilter( | |
'filter_admin_actions', | |
[$this, 'filterActions'] | |
) | |
]; | |
} | |
... lines 18 - 26 | |
} |
Down below, create public function filterActions()
with two arguments. First,
it will be passed an $itemActions
array - that's the _list_item_actions
variable.
And second, $item
: whatever entity is being listed at that moment:
... lines 1 - 6 | |
class EasyAdminExtension extends \Twig_Extension | |
{ | |
... lines 9 - 18 | |
public function filterActions(array $itemActions, $item) | |
{ | |
... lines 21 - 25 | |
} | |
} |
Ok, let's fill in the logic: if $item instanceof Genus && $item->getIsPublished()
,
then unset($itemActions['delete'])
. At the bottom, return $itemActions
:
... lines 1 - 4 | |
use AppBundle\Entity\Genus; | |
class EasyAdminExtension extends \Twig_Extension | |
{ | |
... lines 9 - 18 | |
public function filterActions(array $itemActions, $item) | |
{ | |
if ($item instanceof Genus && $item->getIsPublished()) { | |
unset($itemActions['delete']); | |
} | |
return $itemActions; | |
} | |
} |
Phew! That should do it! This project uses the new Symfony 3.3 autowiring, auto-registration
and autoconfigure services.yml
goodness:
... lines 1 - 5 | |
services: | |
# default configuration for services in *this* file | |
_defaults: | |
autowire: true | |
autoconfigure: true | |
... lines 11 - 12 | |
AppBundle\: | |
resource: '../../src/AppBundle/*' | |
exclude: '../../src/AppBundle/{Entity,Repository,Tests}' | |
... lines 16 - 32 |
So... we don't need to configure anything: EasyAdminExtension
will automatically be registered
as a service and tagged with twig.extension
. In other words... it should just work.
Let's go. Refresh... and hold your breath.
Haha, it kind of worked! Delete is gone... but so is everything else. And you
may have noticed why. We did change the _list_item_actions
variable... but we
forgot to call the parent block. Add {{ parent() }}
:
... lines 1 - 2 | |
{% block item_actions %} | |
{% set _list_item_actions = _list_item_actions|filter_admin_actions(item) %} | |
{{ parent() }} | |
{% endblock %} |
Try it again. Got it! The delete icon is only there when the item is not published.
This was a tricky example... which is why we did it! But usually, customizing things
is easier. Technically, the user could still go directly to the URL to delete the
Genus
, but we'll see how to close that down later.
// composer.json
{
"require": {
"php": ">=5.5.9",
"symfony/symfony": "3.3.*", // v3.3.18
"doctrine/orm": "^2.5", // v2.7.2
"doctrine/doctrine-bundle": "^1.6", // 1.10.3
"doctrine/doctrine-cache-bundle": "^1.2", // 1.3.5
"symfony/swiftmailer-bundle": "^2.3", // v2.6.7
"symfony/monolog-bundle": "^2.8", // v2.12.1
"symfony/polyfill-apcu": "^1.0", // v1.17.0
"sensio/distribution-bundle": "^5.0", // v5.0.25
"sensio/framework-extra-bundle": "^3.0.2", // v3.0.29
"incenteev/composer-parameter-handler": "^2.0", // v2.1.4
"knplabs/knp-markdown-bundle": "^1.4", // 1.7.1
"doctrine/doctrine-migrations-bundle": "^1.1", // v1.3.2
"stof/doctrine-extensions-bundle": "^1.2", // v1.3.0
"composer/package-versions-deprecated": "^1.11", // 1.11.99
"javiereguiluz/easyadmin-bundle": "^1.16" // v1.17.21
},
"require-dev": {
"sensio/generator-bundle": "^3.0", // v3.1.7
"symfony/phpunit-bridge": "^3.0", // v3.4.40
"nelmio/alice": "^2.1", // v2.3.5
"doctrine/doctrine-fixtures-bundle": "^2.3" // v2.4.1
}
}