diff --git a/Configuration/Configurator.php b/Configuration/Configurator.php index 63841668b1..d654d96976 100644 --- a/Configuration/Configurator.php +++ b/Configuration/Configurator.php @@ -34,6 +34,7 @@ class Configurator 'dataType' => null, // Data type (text, date, integer, boolean, ...) of the Doctrine property associated with the field 'virtual' => false, // is a virtual field or a real Doctrine entity property? 'sortable' => true, // listings can be sorted according to the values of this field + 'template' => null, // the path of the template used to render the field in 'show' and 'list' views ); private $doctrineTypeToFormTypeMap = array( @@ -104,6 +105,8 @@ public function getEntityConfiguration($entityName) $entityConfiguration = $this->introspectGettersAndSetters($entityConfiguration); + $entityConfiguration = $this->processFieldTemplates($entityConfiguration); + $this->entitiesConfig[$entityName] = $entityConfiguration; return $entityConfiguration; @@ -458,6 +461,40 @@ private function introspectGettersAndSetters($entityConfiguration) return $entityConfiguration; } + + /** + * 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 $entityConfiguration + * + * @return array + */ + private function processFieldTemplates(array $entityConfiguration) + { + foreach (array('list', 'show') as $view) { + foreach ($entityConfiguration[$view]['fields'] as $fieldName => $fieldMetadata) { + if (null !== $fieldMetadata['template']) { + continue; + } + + // this prevents the template from displaying the 'id' primary key formatted as a number + if ('id' === $fieldName) { + $template = $entityConfiguration['templates']['field_id']; + } elseif (array_key_exists('field_'.$fieldMetadata['type'], $entityConfiguration['templates'])) { + $template = $entityConfiguration['templates']['field_'.$fieldMetadata['type']]; + } else { + $template = $entityConfiguration['templates']['label_undefined']; + } + + $entityConfiguration[$view]['fields'][$fieldName]['template'] = $template; + } + } + + return $entityConfiguration; + } + /** * Returns the most appropriate Symfony Form type for the given Doctrine type. * diff --git a/DependencyInjection/EasyAdminExtension.php b/DependencyInjection/EasyAdminExtension.php index 3a84ac8d0b..b60e31ad3e 100644 --- a/DependencyInjection/EasyAdminExtension.php +++ b/DependencyInjection/EasyAdminExtension.php @@ -18,6 +18,8 @@ class EasyAdminExtension extends Extension { + private $views = array('edit', 'list', 'new', 'show'); + private $defaultActionConfiguration = array( 'name' => null, // either the name of a controller method or an application route (it depends on the 'type' option) 'type' => 'method', // 'method' if the action is a controller method; 'route' if it's an application route @@ -201,7 +203,7 @@ public function processEntityActions(array $backendConfiguration) $entityConfiguration['disabled_actions'] = $disabledActions; // second, define the actions of each entity view - foreach (array('edit', 'list', 'new', 'show') as $view) { + foreach ($this->views as $view) { $defaultActions = $this->getDefaultActions($view); $backendActions = isset($backendConfiguration[$view]['actions']) ? $backendConfiguration[$view]['actions'] : array(); $backendActions = $this->normalizeActionsConfiguration($backendActions, $defaultActions); @@ -392,8 +394,9 @@ private function filterRemovedActions(array $actionsConfiguration) */ private function processEntityTemplates(array $backendConfiguration) { - $applicationTemplateDir = $this->kernelRootDir.'/Resources/views'; + $templatesDir = $this->kernelRootDir.'/Resources/views'; + // first, resolve the general template overriding mechanism foreach ($backendConfiguration['entities'] as $entityName => $entityConfiguration) { foreach ($this->defaultBackendTemplates as $templateName => $defaultTemplatePath) { // 1st level priority: easy_admin.entities..templates. config option @@ -403,10 +406,10 @@ private function processEntityTemplates(array $backendConfiguration) } elseif (isset($backendConfiguration['design']['templates'][$templateName])) { $template = $backendConfiguration['design']['templates'][$templateName]; // 3rd level priority: app/Resources/views/easy_admin//.html.twig - } elseif (file_exists($applicationTemplateDir.'/easy_admin/'.$entityName.'/'.$templateName.'.html.twig')) { + } elseif (file_exists($templatesDir.'/easy_admin/'.$entityName.'/'.$templateName.'.html.twig')) { $template = 'easy_admin/'.$entityName.'/'.$templateName.'.html.twig'; // 4th level priority: app/Resources/views/easy_admin/.html.twig - } elseif (file_exists($applicationTemplateDir.'/easy_admin/'.$templateName.'.html.twig')) { + } elseif (file_exists($templatesDir.'/easy_admin/'.$templateName.'.html.twig')) { $template = 'easy_admin/'.$templateName.'.html.twig'; // 5th level priority: @EasyAdmin/default/.html.twig } else { @@ -419,6 +422,42 @@ private function processEntityTemplates(array $backendConfiguration) $backendConfiguration['entities'][$entityName] = $entityConfiguration; } + // second, walk through all entity fields to determine their specific template + foreach ($backendConfiguration['entities'] as $entityName => $entityConfiguration) { + foreach (array('list', 'show') as $view) { + foreach ($entityConfiguration[$view]['fields'] as $fieldName => $fieldMetadata) { + // if the field defines its own template, resolve its location + if (isset($fieldMetadata['template'])) { + $templateName = $fieldMetadata['template']; + + // template name should not contain the .html.twig extension + // however, for usability reasons, we silently fix this issue if needed + if ('.html.twig' === substr($templateName, -10)) { + $templateName = substr($templateName, 0, -10); + } + + // 1st level priority: app/Resources/views/easy_admin//.html.twig + if (file_exists($templatesDir.'/easy_admin/'.$entityName.'/'.$templateName.'.html.twig')) { + $templatePath = 'easy_admin/'.$entityName.'/'.$templateName.'.html.twig'; + // 2nd level priority: app/Resources/views/easy_admin/.html.twig + } elseif (file_exists($templatesDir.'/easy_admin/'.$templateName.'.html.twig')) { + $templatePath = 'easy_admin/'.$templateName.'.html.twig'; + } else { + throw new \RuntimeException(sprintf('The "%s" field of the "%s" entity uses a custom template called "%s" which doesn\'t exist in "app/Resources/views/easy_admin/" directory.', $fieldName, $entityName, $templateName)); + } + } else { + // At this point, we don't know the exact data type associated with each field. + // The template is initialized to null and it will be resolved at runtime in the Configurator class + $templatePath = null; + } + + $entityConfiguration[$view]['fields'][$fieldName]['template'] = $templatePath; + } + } + + $backendConfiguration['entities'][$entityName] = $entityConfiguration; + } + return $backendConfiguration; } diff --git a/Resources/config/services.xml b/Resources/config/services.xml index 5f4937b9c7..5e2cd2d404 100644 --- a/Resources/config/services.xml +++ b/Resources/config/services.xml @@ -7,6 +7,7 @@ + %kernel.debug% diff --git a/Resources/doc/getting-started/4-views-and-actions.md b/Resources/doc/getting-started/4-views-and-actions.md index 39fb6a281e..83aa4c310b 100644 --- a/Resources/doc/getting-started/4-views-and-actions.md +++ b/Resources/doc/getting-started/4-views-and-actions.md @@ -296,6 +296,9 @@ These are the options that you can define for each field: using the default Bootstrap based form theme, this value is applied to the `
` element which wraps the label, the widget and the error messages of the field. + * `template` (optional): the name of the custom template used to render the + contents of the field in the `list` and `show` views. This option is fully + explained in the [Advanced Design Customization] [advanced-design-customization] tutorial. * `type` (optional): the type of data displayed in the `list`, `search` and `show` views and the form widget displayed in the `edit` and `new` views. These are the supported types: @@ -308,6 +311,12 @@ These are the options that you can define for each field: * `toggle`, displays a boolean value as a flip switch in the `list` and `search` views (as explained later in this chapter). +In addition to these "official" options, you can define any custom option for +the fields. These custom options are passed to the template that renders each +field, allowing to create very powerful backend customizations, as explained +in the [Advanced Design Customization] [advanced-design-customization] +tutorial. + ### Translate Property Labels Before translating the labels, make sure that the `translator` service is @@ -653,3 +662,4 @@ article of the official Symfony documentation to learn how to define custom form types. [custom-actions]: ../tutorials/customizing-backend-actions.md +[advanced-design-customization]: ../tutorials/advanced-design-customization.md diff --git a/Resources/doc/images/easyadmin-design-customization-custom-data-types.png b/Resources/doc/images/easyadmin-design-customization-custom-data-types.png new file mode 100644 index 0000000000..17160f9531 Binary files /dev/null and b/Resources/doc/images/easyadmin-design-customization-custom-data-types.png differ diff --git a/Resources/doc/tutorials/advanced-design-customization.md b/Resources/doc/tutorials/advanced-design-customization.md index 636e2edd5e..cf18d8b36f 100644 --- a/Resources/doc/tutorials/advanced-design-customization.md +++ b/Resources/doc/tutorials/advanced-design-customization.md @@ -281,12 +281,117 @@ about their features. Inside the `field_*` and `label_*` templates you have access to the following variables: - * `view`, the name of the view where the field is being rendered (`show` or - `list`). + * `field_options`, the options configured for this field in the backend + configuration file. + * `item`, the entity instance. * `value`, the content of the field being rendered, which can be a variable of any type (string, numeric, boolean, array, etc.) - * `format`, available only for the date and numeric field types. It defines - the formatting that should be applied to the value before displaying it. + * `view`, the name of the view where the field is being rendered (`show` or + `list`). + +### Rendering Properties with Custom Templates + +The default property templates are flexible enough for most backends. However, +when your backend is very complex, it may be useful to use a custom template to +define the way some property is rendered in the `list` or `show` views. + +To do so, define the name of the custom template in the `template` option of +the property: + +```yaml +easy_admin: + # ... + entities: + Invoice: + list: + fields: + - { property: 'total', template: 'invoice_total' } +``` + +The above configuration makes the backend use the `invoice_total.html.twig` +template instead of the default `field_float.html.twig` template. Custom +templates are looked for in the following locations (the first existing +template is used): + + 1. `app/Resources/views/easy_admin//.html.twig` + template. + 2. `app/Resources/views/easy_admin/.html.twig` + template. + +Custom templates receive the same parameters as built-in templates ( +`field_options`, `item`, `value`, `view`). + +### Adding Custom Logic to Property Templates + +All property templates receive a parameter called `field_options` with the full +list of options defined in the configuration file for that property. If you +add custom options, they will also be available in the `field_options` +parameter. This allows you to add custom logic to templates very easily. + +Imagine that you want to translate some text contents in the `list` view. To do +so, define a custom option called `trans` which indicates if the property +content should be translated and another option called `domain` which defines +the name of the translation domain to use. + +```yaml +# app/config.yml +Product: + class: AppBundle\Entity\Product + label: 'Products' + list: + fields: + - id + - { property: 'name', trans: true, domain: 'messages' } + # ... +``` + +Supposing that the `name` property is of type `string`, you just need to +override the built-in `field_string.html.twig` template: + +```twig +{# app/Resources/views/easy_admin/field_string.html.twig #} + +{% if field_options.trans|default(false) %} + {# translate fields defined as "translatable" #} + {{ value|trans({}, field_options.domain|default('messages')) }} +{% else %} + {# if not translatable, simply include the default template #} + {{ include('@EasyAdmin/default/field_string.html.twig') }} +{% endif %} +``` + +If the custom logic is too complex, it may be better to use your own custom +template to not mess built-in templates too much. In this example, the +collection of tags associated with a product is displayed in a way that is too +customized to use a built-in template: + +```yaml +# app/config.yml +Product: + class: AppBundle\Entity\Product + label: 'Products' + list: + fields: + - id + # ... + - { property: 'tags', type: 'tag_collection', label_colors: ['primary', 'success', 'info'] } +``` + +The custom `tag_collection.html.twig` would look as follows: + +```twig +{# app/Resources/views/easy_admin/tag_collection.html.twig #} + +{% set colors = field_options.label_colors|default(['primary']) %} + +{% for tag in value %} + {{ tag }} +{% endfor %} +``` + +And this property would be rendered in the `list` view as follows: + +![Default listing interface](../images/easyadmin-design-customization-custom-data-types.png) Translating Backend Elements in Custom Templates ------------------------------------------------ diff --git a/Resources/views/default/field_bigint.html.twig b/Resources/views/default/field_bigint.html.twig index 3c1544f9e5..ff4037f9f8 100644 --- a/Resources/views/default/field_bigint.html.twig +++ b/Resources/views/default/field_bigint.html.twig @@ -1,5 +1,5 @@ -{% if format %} - {{ format|format(value) }} +{% if field_options.format %} + {{ field_options.format|format(value) }} {% else %} {{ value|number_format }} {% endif %} diff --git a/Resources/views/default/field_date.html.twig b/Resources/views/default/field_date.html.twig index 3a0e676863..07250a0755 100644 --- a/Resources/views/default/field_date.html.twig +++ b/Resources/views/default/field_date.html.twig @@ -1 +1 @@ -{{ value|date(format) }} +{{ value|date(field_options.format) }} diff --git a/Resources/views/default/field_datetime.html.twig b/Resources/views/default/field_datetime.html.twig index 3a0e676863..07250a0755 100644 --- a/Resources/views/default/field_datetime.html.twig +++ b/Resources/views/default/field_datetime.html.twig @@ -1 +1 @@ -{{ value|date(format) }} +{{ value|date(field_options.format) }} diff --git a/Resources/views/default/field_datetimez.html.twig b/Resources/views/default/field_datetimez.html.twig index 3a0e676863..07250a0755 100644 --- a/Resources/views/default/field_datetimez.html.twig +++ b/Resources/views/default/field_datetimez.html.twig @@ -1 +1 @@ -{{ value|date(format) }} +{{ value|date(field_options.format) }} diff --git a/Resources/views/default/field_decimal.html.twig b/Resources/views/default/field_decimal.html.twig index 2f0c872b65..92b6d51b7d 100644 --- a/Resources/views/default/field_decimal.html.twig +++ b/Resources/views/default/field_decimal.html.twig @@ -1,5 +1,5 @@ -{% if format %} - {{ format|format(value) }} +{% if field_options.format %} + {{ field_options.format|format(value) }} {% else %} {{ value|number_format(2) }} {% endif %} diff --git a/Resources/views/default/field_float.html.twig b/Resources/views/default/field_float.html.twig index 2f0c872b65..92b6d51b7d 100644 --- a/Resources/views/default/field_float.html.twig +++ b/Resources/views/default/field_float.html.twig @@ -1,5 +1,5 @@ -{% if format %} - {{ format|format(value) }} +{% if field_options.format %} + {{ field_options.format|format(value) }} {% else %} {{ value|number_format(2) }} {% endif %} diff --git a/Resources/views/default/field_integer.html.twig b/Resources/views/default/field_integer.html.twig index 3c1544f9e5..ff4037f9f8 100644 --- a/Resources/views/default/field_integer.html.twig +++ b/Resources/views/default/field_integer.html.twig @@ -1,5 +1,5 @@ -{% if format %} - {{ format|format(value) }} +{% if field_options.format %} + {{ field_options.format|format(value) }} {% else %} {{ value|number_format }} {% endif %} diff --git a/Resources/views/default/field_smallint.html.twig b/Resources/views/default/field_smallint.html.twig index 3c1544f9e5..ff4037f9f8 100644 --- a/Resources/views/default/field_smallint.html.twig +++ b/Resources/views/default/field_smallint.html.twig @@ -1,5 +1,5 @@ -{% if format %} - {{ format|format(value) }} +{% if field_options.format %} + {{ field_options.format|format(value) }} {% else %} {{ value|number_format }} {% endif %} diff --git a/Resources/views/default/field_time.html.twig b/Resources/views/default/field_time.html.twig index 3a0e676863..07250a0755 100644 --- a/Resources/views/default/field_time.html.twig +++ b/Resources/views/default/field_time.html.twig @@ -1 +1 @@ -{{ value|date(format) }} +{{ value|date(field_options.format) }} diff --git a/Tests/Controller/CustomFieldTemplateTest.php b/Tests/Controller/CustomFieldTemplateTest.php new file mode 100644 index 0000000000..22aa36faad --- /dev/null +++ b/Tests/Controller/CustomFieldTemplateTest.php @@ -0,0 +1,85 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace JavierEguiluz\Bundle\EasyAdminBundle\Tests\Controller; + +use Symfony\Component\DomCrawler\Crawler; +use JavierEguiluz\Bundle\EasyAdminBundle\Tests\Fixtures\AbstractTestCase; + +class CustomFieldTemplateTest extends AbstractTestCase +{ + public function setUp() + { + parent::setUp(); + + $this->initClient(array('environment' => 'custom_field_template')); + } + + public function testListViewCustomFieldTemplate() + { + $crawler = $this->requestListView(); + + $this->assertContains('Custom template for "name" field in the "list" view.', $crawler->filter('#main table td[data-label="Name"]')->eq(0)->text()); + $this->assertContains('The value of the custom option is "custom_list_value".', $crawler->filter('#main table td[data-label="Name"]')->eq(0)->text()); + + $parsedConfiguration = $this->client->getKernel()->getContainer()->getParameter('easyadmin.config'); + $this->assertEquals('easy_admin/custom_field_template.html.twig', $parsedConfiguration['entities']['Category']['list']['fields']['name']['template']); + } + + public function testShowViewCustomFieldTemplate() + { + $crawler = $this->requestShowView(); + + $this->assertContains('Custom template for "name" field in the "show" view.', $crawler->filter('#main .form-control')->eq(0)->text()); + $this->assertContains('The value of the custom option is "custom_show_value".', $crawler->filter('#main .form-control')->eq(0)->text()); + } + + public function testListViewCustomFieldTemplateWrongName() + { + $crawler = $this->requestListView(); + + $this->assertContains('Custom template for "id" field in the "list" view.', $crawler->filter('#main table td[data-label="ID"]')->eq(0)->text()); + + $parsedConfiguration = $this->client->getKernel()->getContainer()->getParameter('easyadmin.config'); + $this->assertEquals('easy_admin/custom_field_template.html.twig', $parsedConfiguration['entities']['Category']['list']['fields']['id']['template']); + } + + public function testShowViewCustomFieldTemplateWrongName() + { + $crawler = $this->requestShowView(); + + $this->assertContains('Custom template for "id" field in the "show" view.', $crawler->filter('#main .form-control')->eq(1)->text()); + } + + /** + * @return Crawler + */ + private function requestListView() + { + return $this->getBackendPage(array( + 'action' => 'list', + 'entity' => 'Category', + 'view' => 'list', + )); + } + + /** + * @return Crawler + */ + private function requestShowView() + { + return $this->getBackendPage(array( + 'action' => 'show', + 'entity' => 'Category', + 'id' => '200', + )); + } +} diff --git a/Tests/DependencyInjection/EasyAdminExtensionTest.php b/Tests/DependencyInjection/EasyAdminExtensionTest.php index f44b056c7c..0168d774c9 100644 --- a/Tests/DependencyInjection/EasyAdminExtensionTest.php +++ b/Tests/DependencyInjection/EasyAdminExtensionTest.php @@ -174,6 +174,15 @@ public function testOverriddenTemplateNamesAreLimited() $this->parseConfigurationFile(__DIR__.'/fixtures/exceptions/overridden_template_names_are_limited.yml'); } + /** + * @expectedException RuntimeException + * @expectedExceptionMessage The "id" field of the "TestEntity" entity uses a custom template called "this_template_does_not_exist" which doesn't exist in "app/Resources/views/easy_admin/" directory. + */ + public function testCustomFieldTemplateDoesNotExist() + { + $this->parseConfigurationFile(__DIR__.'/fixtures/exceptions/custom_field_template_does_not_exist.yml'); + } + /** * Tests the template overriding mechanism when a given entity defines * its own custom templates in app/Resources/views/easy_admin//.html.twig files diff --git a/Tests/DependencyInjection/fixtures/configurations/output/config_040.yml b/Tests/DependencyInjection/fixtures/configurations/output/config_040.yml index 2dd75469be..8a7ab15bd1 100644 --- a/Tests/DependencyInjection/fixtures/configurations/output/config_040.yml +++ b/Tests/DependencyInjection/fixtures/configurations/output/config_040.yml @@ -38,8 +38,10 @@ easy_admin: fields: field5: property: field5 + template: null field6: property: field6 + template: null actions: show: name: show @@ -75,8 +77,10 @@ easy_admin: fields: field7: property: field7 + template: null field8: property: field8 + template: null actions: delete: name: delete diff --git a/Tests/DependencyInjection/fixtures/configurations/output/config_041.yml b/Tests/DependencyInjection/fixtures/configurations/output/config_041.yml index e31d3dd03a..255f48b7bb 100644 --- a/Tests/DependencyInjection/fixtures/configurations/output/config_041.yml +++ b/Tests/DependencyInjection/fixtures/configurations/output/config_041.yml @@ -8,6 +8,7 @@ easy_admin: property: field1 type: image base_path: /field_img/ + template: null actions: show: name: show diff --git a/Tests/DependencyInjection/fixtures/configurations/output/config_042.yml b/Tests/DependencyInjection/fixtures/configurations/output/config_042.yml index 174b6e7d3c..79048bd3c8 100644 --- a/Tests/DependencyInjection/fixtures/configurations/output/config_042.yml +++ b/Tests/DependencyInjection/fixtures/configurations/output/config_042.yml @@ -9,6 +9,7 @@ easy_admin: property: field1 type: image base_path: /entity_img/ + template: null actions: show: name: show diff --git a/Tests/DependencyInjection/fixtures/configurations/output/config_043.yml b/Tests/DependencyInjection/fixtures/configurations/output/config_043.yml index cf885f541a..64f91c4628 100644 --- a/Tests/DependencyInjection/fixtures/configurations/output/config_043.yml +++ b/Tests/DependencyInjection/fixtures/configurations/output/config_043.yml @@ -9,6 +9,7 @@ easy_admin: property: field1 type: image base_path: /field_img/ + template: null actions: show: name: show diff --git a/Tests/DependencyInjection/fixtures/exceptions/custom_field_template_does_not_exist.yml b/Tests/DependencyInjection/fixtures/exceptions/custom_field_template_does_not_exist.yml new file mode 100644 index 0000000000..a2331e4cec --- /dev/null +++ b/Tests/DependencyInjection/fixtures/exceptions/custom_field_template_does_not_exist.yml @@ -0,0 +1,11 @@ +# TEST +# when a custom template is defined for a field, that template must exist as a file + +# CONFIGURATION +easy_admin: + entities: + TestEntity: + class: 'AppBundle\Entity\TestEntity' + list: + fields: + - { property: 'id', template: 'this_template_does_not_exist' } diff --git a/Tests/Fixtures/App/Resources/views/easy_admin/custom_field_template.html.twig b/Tests/Fixtures/App/Resources/views/easy_admin/custom_field_template.html.twig new file mode 100644 index 0000000000..509e2483ac --- /dev/null +++ b/Tests/Fixtures/App/Resources/views/easy_admin/custom_field_template.html.twig @@ -0,0 +1,3 @@ +Custom template for "{{ field_options.property }}" field in the "{{ view }}" view. + +The value of the custom option is "{{ field_options.custom_option|default('null') }}". diff --git a/Tests/Fixtures/App/config/config_custom_field_template.yml b/Tests/Fixtures/App/config/config_custom_field_template.yml new file mode 100644 index 0000000000..70b785a7f6 --- /dev/null +++ b/Tests/Fixtures/App/config/config_custom_field_template.yml @@ -0,0 +1,25 @@ +imports: + - { resource: config.yml } + +framework: + # This file overrides the EasyAdmin controller + router: { resource: "%kernel.root_dir%/config/routing_override.yml" } + +easy_admin: + entities: + Category: + class: JavierEguiluz\Bundle\EasyAdminBundle\Tests\Fixtures\AppTestBundle\Entity\Category + label: 'Categories' + list: + fields: + - { property: 'name', template: 'custom_field_template', custom_option: 'custom_list_value' } + # Template name should NOT contain the .html.twig suffix. However, the + # application should silently fix this error and work as expected + - { property: 'id', template: 'custom_field_template.html.twig' } + + show: + fields: + - { property: 'name', template: 'custom_field_template', custom_option: 'custom_show_value' } + # Template name should NOT contain the .html.twig suffix. However, the + # application should silently fix this error and work as expected + - { property: 'id', template: 'custom_field_template.html.twig' } diff --git a/Twig/EasyAdminTwigExtension.php b/Twig/EasyAdminTwigExtension.php index 2db040d0d6..a950cb6246 100644 --- a/Twig/EasyAdminTwigExtension.php +++ b/Twig/EasyAdminTwigExtension.php @@ -17,10 +17,12 @@ class EasyAdminTwigExtension extends \Twig_Extension { private $configurator; + private $debug; - public function __construct(Configurator $configurator) + public function __construct(Configurator $configurator, $debug = false) { $this->configurator = $configurator; + $this->debug = $debug; } public function getFunctions() @@ -111,22 +113,20 @@ public function renderEntityField(\Twig_Environment $twig, $view, $entityName, $ try { $fieldType = $fieldMetadata['dataType']; + $templateParameters = array( + 'field_options' => $fieldMetadata, + 'item' => $item, + 'value' => $value, + 'view' => $view, + ); if (null === $value) { - return $twig->render($entityConfiguration['templates']['label_null'], array('view' => $view)); + return $twig->render($entityConfiguration['templates']['label_null'], $templateParameters); } // when a virtual field doesn't define it's type, consider it a string if (true === $fieldMetadata['virtual'] && null === $fieldType) { - return $twig->render($entityConfiguration['templates']['field_text'], array('value' => strval($value), 'view' => $view)); - } - - if ('id' === $fieldName) { - return $twig->render($entityConfiguration['templates']['field_id'], array('value' => $value, 'view' => $view)); - } - - if (in_array($fieldType, array('date', 'datetime', 'datetimetz', 'time', 'bigint', 'integer', 'smallint', 'decimal', 'float'))) { - return $twig->render($entityConfiguration['templates']['field_'.$fieldType], array('value' => $value, 'view' => $view, 'format' => isset($fieldMetadata['format']) ? $fieldMetadata['format'] : null)); + $templateParameters['value'] = strval($value); } if (in_array($fieldType, array('image'))) { @@ -138,21 +138,16 @@ public function renderEntityField(\Twig_Environment $twig, $view, $entityName, $ ? rtrim($fieldMetadata['base_path'], '/').'/'.ltrim($value, '/') : '/'.ltrim($value, '/'); } - - return $twig->render($entityConfiguration['templates']['field_image'], array('value' => $imageUrl, 'view' => $view)); + $templateParameters['value'] = $imageUrl; } - if (in_array($fieldType, array('array', 'simple_array'))) { - if (empty($value)) { - return $twig->render($entityConfiguration['templates']['label_empty']); - } - - return $twig->render($entityConfiguration['templates']['field_'.$fieldType], array('value' => $value, 'view' => $view)); + if (in_array($fieldType, array('array', 'simple_array')) && empty($value)) { + return $twig->render($entityConfiguration['templates']['label_empty'], $templateParameters); } if (in_array($fieldType, array('association'))) { if ($value instanceof PersistentCollection) { - return $twig->render($entityConfiguration['templates']['field_association'], array('value' => $value, 'view' => $view)); + return $twig->render($entityConfiguration['templates']['field_association'], $templateParameters); } $associatedEntityClassParts = explode('\\', $fieldMetadata['targetEntity']); @@ -163,22 +158,26 @@ public function renderEntityField(\Twig_Environment $twig, $view, $entityName, $ $associatedEntityPrimaryKey = $associatedEntityConfig['primary_key_field_name']; } catch (\Exception $e) { // if the entity isn't managed by EasyAdmin, don't link to it and just display its raw value - return $twig->render($entityConfiguration['templates']['field_association'], array('value' => $value, 'view' => $view)); + return $twig->render($entityConfiguration['templates']['field_association'], $templateParameters); } $primaryKeyGetter = 'get'.ucfirst($associatedEntityPrimaryKey); if (method_exists($value, $primaryKeyGetter)) { $linkParameters = array('entity' => $associatedEntityClassName, 'action' => 'show', 'view' => $view, 'id' => $value->$primaryKeyGetter()); + $templateParameters['link_parameters'] = $linkParameters; - return $twig->render($entityConfiguration['templates']['field_association'], array('value' => $value, 'link_parameters' => $linkParameters, 'view' => $view)); + return $twig->render($entityConfiguration['templates']['field_association'], $templateParameters); } - return $twig->render($entityConfiguration['templates']['field_association'], array('value' => $value, 'view' => $view)); + return $twig->render($entityConfiguration['templates']['field_association'], $templateParameters); } - // all the other data types: boolean, string, text, toggle - return $twig->render($entityConfiguration['templates']['field_'.$fieldType], array('value' => $value, 'view' => $view)); + return $twig->render($fieldMetadata['template'], $templateParameters); } catch (\Exception $e) { + if ($this->debug) { + throw $e; + } + return $twig->render($entityConfiguration['templates']['label_undefined'], array('view' => $view)); } }