Skip to content

Commit

Permalink
feature #244 Allow to easily customize/override any backend element (…
Browse files Browse the repository at this point in the history
…javiereguiluz)

This PR was merged into the master branch.

Discussion
----------

Allow to easily customize/override any backend element

I've created this PR as an early preview of the mechanism that it will soon allow to configure/customize/override each backend element.

Some comments:

  * This already works for the main backend templates (layout, edit, new, list, etc.)
  * Tomorrow I'll add the templates to customize form fields and the rest of stuff.
  * In #238 I proposed a super simple override mechanism base on the `include()` Twig function. Sadly, that doesn't work because when you include a template, you are **not** including its blocks. This means that blocks are not defined in the parent template and nothing works as expected. This is a well-known Twig limitation that is not going to be fixed.
  * Instead of the previous mechanism, now we determine the template to use for each backend element and entity. We do this when the container is built, so we'll get a nice performance boost (the override mechanism will be run only once instead of executing it for every request).

Commits
-------

3fd7765 Minor tweaks for Twig blocks
f80196a Added new blocks in the show.html.twig template
ee89078 The Twig extension no longer needs the @router services because it doesn't generate any URL
cc3ba6a Templates (and text files in general) should end with a new line character
2cd5e38 Minor fix in a comment
5f83b4f Type hinted variables with interfaces instead of concrete implementations
572c607 Improved the return type of a method description
b5456de Added a lot of blocks in the list.html.twig template
f1240c9 Added blocks in edit, form and new templates
4d5736e Moved the default/form/entity_form.html.twig template to default/form.html.twig
d31ed39 Defined a lot of blocks for the layout.html.twig template
747ae8a Removed an unreachable code
7d425e9 Fiexd an error in the image field type
3c35aca Removed an unused variable
697f365 Fixed test for the previous change
9de4a74 Group options by purpose not alphabetically
e99b291 Added new tests
c0d8795 Improved the documentation about the custom templates
79b3fa8 Allowed entities to define their own custom templates
9a7ad1d Fixed one comment
3279aa8 Improve one test
be9979b Add support for customizing the fragments used to render each field type
3e48bf9 Merge branch 'master' into fix_238
46865a9 The main backend templates can now be easily overridden
5f0e755 Removed the unused _flashes.html.twig template
7584d3b Renamed _list_paginator.html.twig template to _paginator.html.twig
  • Loading branch information
javiereguiluz committed Apr 25, 2015
2 parents e5285cd + 3fd7765 commit ea029ab
Show file tree
Hide file tree
Showing 148 changed files with 4,810 additions and 407 deletions.
10 changes: 5 additions & 5 deletions Controller/AdminController.php
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ protected function listAction()
$fields = $this->entity['list']['fields'];
$paginator = $this->findAll($this->entity['class'], $this->request->query->get('page', 1), $this->config['list']['max_results'], $this->request->query->get('sortField'), $this->request->query->get('sortDirection'));

return $this->render('@EasyAdmin/list.html.twig', array(
return $this->render($this->entity['templates']['list'], array(
'paginator' => $paginator,
'fields' => $fields,
'view' => 'list',
Expand Down Expand Up @@ -173,7 +173,7 @@ protected function editAction()
return $this->redirect($this->generateUrl('admin', array('action' => 'list', 'view' => 'list', 'entity' => $this->entity['name'])));
}

return $this->render('@EasyAdmin/edit.html.twig', array(
return $this->render($this->entity['templates']['edit'], array(
'form' => $editForm->createView(),
'entity_fields' => $fields,
'item' => $item,
Expand Down Expand Up @@ -201,7 +201,7 @@ protected function showAction()
$fields = $this->entity['show']['fields'];
$deleteForm = $this->createDeleteForm($this->entity['name'], $id);

return $this->render('@EasyAdmin/show.html.twig', array(
return $this->render($this->entity['templates']['show'], array(
'item' => $item,
'fields' => $fields,
'view' => 'show',
Expand Down Expand Up @@ -234,7 +234,7 @@ protected function newAction()
return $this->redirect($this->generateUrl('admin', array('action' => 'list', 'view' => 'new', 'entity' => $this->entity['name'])));
}

return $this->render('@EasyAdmin/new.html.twig', array(
return $this->render($this->entity['templates']['new'], array(
'form' => $newForm->createView(),
'entity_fields' => $fields,
'item' => $item,
Expand Down Expand Up @@ -281,7 +281,7 @@ protected function searchAction()
$paginator = $this->findBy($this->entity['class'], $this->request->query->get('query'), $searchableFields, $this->request->query->get('page', 1), $this->config['list']['max_results']);
$fields = $this->entity['list']['fields'];

return $this->render('@EasyAdmin/list.html.twig', array(
return $this->render($this->entity['templates']['list'], array(
'paginator' => $paginator,
'fields' => $fields,
'view' => 'search',
Expand Down
36 changes: 36 additions & 0 deletions DependencyInjection/Configuration.php
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,42 @@ private function addDesignSection(ArrayNodeDefinition $rootNode)
->end()
->end()
->end()

->arrayNode('templates')
->info('The custom templates used to render each backend element.')
->performNoDeepMerging()
->children()
->scalarNode('layout')->info('Used to decorate the main templates (list, edit, new and show)')->end()
->scalarNode('edit')->info('Used to render the page where entities are edited')->end()
->scalarNode('list')->info('Used to render the listing page and the search results page')->end()
->scalarNode('new')->info('Used to render the page where new entities are created')->end()
->scalarNode('show')->info('Used to render the contents stored by a given entity')->end()
->scalarNode('form')->info('Used to render the form displayed in the new and edit pages')->end()
->scalarNode('paginator')->info('Used to render the paginator in the list page')->end()
->scalarNode('field_array')->info('Used to render array field types')->end()
->scalarNode('field_association')->info('Used to render fields that store Doctrine associations')->end()
->scalarNode('field_bigint')->info('Used to render bigint field types')->end()
->scalarNode('field_boolean')->info('Used to render boolean field types')->end()
->scalarNode('field_date')->info('Used to render date field types')->end()
->scalarNode('field_datetime')->info('Used to render datetime field types')->end()
->scalarNode('field_datetimetz')->info('Used to render datetimetz field types')->end()
->scalarNode('field_decimal')->info('Used to render decimal field types')->end()
->scalarNode('field_float')->info('Used to render float field types')->end()
->scalarNode('field_id')->info('Used to render the field called "id". This avoids formatting its value as any other regular number (with decimals and thousand separators) ')->end()
->scalarNode('field_image')->info('Used to render image field types (a special type that displays the image contents)')->end()
->scalarNode('field_integer')->info('Used to render integer field types')->end()
->scalarNode('field_simple_array')->info('Used to render simple array field types')->end()
->scalarNode('field_smallint')->info('Used to render smallint field types')->end()
->scalarNode('field_string')->info('Used to render string field types')->end()
->scalarNode('field_text')->info('Used to render text field types')->end()
->scalarNode('field_time')->info('Used to render time field types')->end()
->scalarNode('field_toggle')->info('Used to render toggle field types (a special type that display booleans as flip switches)')->end()
->scalarNode('label_empty')->info('Used when the field to render is an empty collection')->end()
->scalarNode('label_inaccessible')->info('Used when is not possible to access the value of the field to render (there is no getter or public property)')->end()
->scalarNode('label_null')->info('Used when the value of the field to render is null')->end()
->scalarNode('label_undefined')->info('Used when any kind of error or exception happens when trying to access the value of the field to render')->end()
->end()
->end()
->end()
->end()
->end()
Expand Down
76 changes: 76 additions & 0 deletions DependencyInjection/EasyAdminExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,49 @@ class EasyAdminExtension extends Extension
'icon' => null, // the name of the FontAwesome icon to display next to the 'label' (doesn't include the 'fa-' prefix)
);

private $defaultBackendTemplates = array(
'layout' => '@EasyAdmin/default/layout.html.twig',
'edit' => '@EasyAdmin/default/edit.html.twig',
'list' => '@EasyAdmin/default/list.html.twig',
'new' => '@EasyAdmin/default/new.html.twig',
'show' => '@EasyAdmin/default/show.html.twig',
'form' => '@EasyAdmin/default/form.html.twig',
'paginator' => '@EasyAdmin/default/paginator.html.twig',
'field_array' => '@EasyAdmin/default/field_array.html.twig',
'field_association' => '@EasyAdmin/default/field_association.html.twig',
'field_bigint' => '@EasyAdmin/default/field_bigint.html.twig',
'field_boolean' => '@EasyAdmin/default/field_boolean.html.twig',
'field_date' => '@EasyAdmin/default/field_date.html.twig',
'field_datetime' => '@EasyAdmin/default/field_datetime.html.twig',
'field_datetimetz' => '@EasyAdmin/default/field_datetimetz.html.twig',
'field_decimal' => '@EasyAdmin/default/field_decimal.html.twig',
'field_float' => '@EasyAdmin/default/field_float.html.twig',
'field_id' => '@EasyAdmin/default/field_id.html.twig',
'field_image' => '@EasyAdmin/default/field_image.html.twig',
'field_integer' => '@EasyAdmin/default/field_integer.html.twig',
'field_simple_array' => '@EasyAdmin/default/field_simple_array.html.twig',
'field_smallint' => '@EasyAdmin/default/field_smallint.html.twig',
'field_string' => '@EasyAdmin/default/field_string.html.twig',
'field_text' => '@EasyAdmin/default/field_text.html.twig',
'field_time' => '@EasyAdmin/default/field_time.html.twig',
'field_toggle' => '@EasyAdmin/default/field_toggle.html.twig',
'label_empty' => '@EasyAdmin/default/label_empty.html.twig',
'label_inaccessible' => '@EasyAdmin/default/label_inaccessible.html.twig',
'label_null' => '@EasyAdmin/default/label_null.html.twig',
'label_undefined' => '@EasyAdmin/default/label_undefined.html.twig',
);

private $kernelRootDir;

public function load(array $configs, ContainerBuilder $container)
{
$this->kernelRootDir = $container->getParameter('kernel.root_dir');

// process bundle's configuration parameters
$backendConfiguration = $this->processConfiguration(new Configuration(), $configs);
$backendConfiguration['entities'] = $this->getEntitiesConfiguration($backendConfiguration['entities']);
$backendConfiguration = $this->processEntityActions($backendConfiguration);
$backendConfiguration = $this->processEntityTemplates($backendConfiguration);

$container->setParameter('easyadmin.config', $backendConfiguration);

Expand Down Expand Up @@ -324,6 +361,45 @@ private function filterRemovedActions(array $actionsConfiguration)
});
}

/**
* Determines the template used to render each backend element. This is not
* trivial because templates can depend on the entity displayed and they
* define an advanced override mechanism.
*
* @param array $backendConfiguration
*
* @return array
*/
private function processEntityTemplates(array $backendConfiguration)
{
foreach ($backendConfiguration['entities'] as $entityName => $entityConfiguration) {
foreach ($this->defaultBackendTemplates as $templateName => $defaultTemplatePath) {
// 1st level priority: easy_admin.entities.<entityName>.templates.<templateName> config option
if (isset($entityConfiguration['templates'][$templateName])) {
$template = $entityConfiguration['templates'][$templateName];
// 2nd level priority: easy_admin.design.templates.<templateName> config option
} elseif (isset($backendConfiguration['design']['templates'][$templateName])) {
$template = $backendConfiguration['design']['templates'][$templateName];
// 3nd level priority: app/Resources/views/easy_admin/<entityName>/<templateName>.html.twig
} elseif (file_exists($templateFilePath = $this->kernelRootDir.'/Resources/views/easy_admin/'.$entityName.'/'.$templateName.'html.twig')) {
$template = $templateFilePath;
// 4th level priority: app/Resources/views/easy_admin/<templateName>.html.twig
} elseif (file_exists($templateFilePath = $this->kernelRootDir.'/Resources/views/easy_admin/'.$templateName.'html.twig')) {
$template = $templateFilePath;
// 5th level priority: @EasyAdmin/default/<templateName>.html.twig
} else {
$template = $defaultTemplatePath;
}

$entityConfiguration['templates'][$templateName] = $template;
}

$backendConfiguration['entities'][$entityName] = $entityConfiguration;
}

return $backendConfiguration;
}

/**
* Normalizes and initializes the configuration of the given entities to
* simplify the option processing of the other methods and functions.
Expand Down
2 changes: 1 addition & 1 deletion Reflection/EntityMetadataInspector.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
use Symfony\Bridge\Doctrine\ManagerRegistry;

/**
* Introspects information about the properties of the given class.
* Introspects information about the properties of the given entity class.
*/
class EntityMetadataInspector
{
Expand Down
1 change: 0 additions & 1 deletion Resources/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
<services>

<service id="easyadmin.twig.extension" class="JavierEguiluz\Bundle\EasyAdminBundle\Twig\EasyAdminTwigExtension" public="false">
<argument type="service" id="router"></argument>
<argument type="service" id="easyadmin.configurator"></argument>
<tag name="twig.extension" />
</service>
Expand Down
Loading

0 comments on commit ea029ab

Please sign in to comment.