Skip to content

Commit

Permalink
feature #812 Improve how associations are displayed in the "show" vie…
Browse files Browse the repository at this point in the history
…w (yceruto, javiereguiluz)

This PR was merged into the master branch.

Discussion
----------

Improve how associations are displayed in the "show" view

@yceruto I like your original idea, but instead of displaying the elements "imploded", I prefer to display them as a `<ul>` list (which is useful for elements with long names/titles):

**Before**

![before](https://cloud.githubusercontent.com/assets/73419/12378983/d80eb858-bd4f-11e5-8422-8607dbc9e202.png)

**After**

![after](https://cloud.githubusercontent.com/assets/73419/12378984/d8e1f632-bd4f-11e5-908e-7b148f6a06f3.png)

Commits
-------

971c26f Finished this feature
3ce5b06 Final changes
ead799d More refactorings
bcc6d53 Improved isActionAllowd() method
17cf99b Made code more robust
4886b4d Display links for the association collection items
bdad2de Display associations as a list of values
d671440 improving representation for collection
  • Loading branch information
javiereguiluz committed Feb 22, 2016
2 parents be32d78 + 971c26f commit d800051
Show file tree
Hide file tree
Showing 5 changed files with 160 additions and 43 deletions.
96 changes: 92 additions & 4 deletions Configuration/Configurator.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

namespace JavierEguiluz\Bundle\EasyAdminBundle\Configuration;

use Symfony\Component\PropertyAccess\PropertyAccessor;

/**
* Exposes the configuration of the backend fully and on a per-entity basis.
*
Expand All @@ -19,37 +21,123 @@
class Configurator
{
private $backendConfig;
private $accessor;

public function __construct(array $backendConfig)
public function __construct(array $backendConfig, PropertyAccessor $accessor)
{
$this->backendConfig = $backendConfig;
$this->accessor = $accessor;
}

/**
* Returns the entire backend configuration.
* Returns the entire backend configuration or just the configuration for
* the optional property path. Example: getBackendConfig('design.menu')
*
* @param string $propertyPath
*
* @return array
*/
public function getBackendConfig()
public function getBackendConfig($propertyPath = null)
{
return $this->backendConfig;
if (empty($propertyPath)) {
return $this->backendConfig;
}

// turns 'design.menu' into '[design][menu]', the format required by PropertyAccess
$propertyPath = '['.str_replace('.', '][', $propertyPath).']';

return $this->accessor->getValue($this->backendConfig, $propertyPath);
}

/**
* Returns the configuration for the given entity name.
*
* @param string $entityName
*
* @deprecated Use getEntityConfig()
* @return array The full entity configuration
*
* @throws \InvalidArgumentException when the entity isn't managed by EasyAdmin
*/
public function getEntityConfiguration($entityName)
{
return $this->getEntityConfig($entityName);
}

/**
* Returns the configuration for the given entity name.
*
* @param string $entityName
*
* @return array The full entity configuration
*
* @throws \InvalidArgumentException
*/
public function getEntityConfig($entityName)
{
if (!isset($this->backendConfig['entities'][$entityName])) {
throw new \InvalidArgumentException(sprintf('Entity "%s" is not managed by EasyAdmin.', $entityName));
}

return $this->backendConfig['entities'][$entityName];
}

/**
* Returns the full entity config for the given entity class.
*
* @param string $fqcn The full qualified class name of the entity
*
* @return array|null The full entity configuration
*/
public function getEntityConfigByClass($fqcn)
{
$backendConfig = $this->getBackendConfig();
foreach ($backendConfig['entities'] as $entityName => $entityConfig) {
if ($entityConfig['class'] === $fqcn) {
return $entityConfig;
}
}
}

/**
* Returns the full action configuration for the given 'entity' and 'view'.
*
* @param string $entityName
* @param string $view
* @param string $action
*
* @return array
*/
public function getActionConfig($entityName, $view, $action)
{
try {
$entityConfig = $this->getEntityConfig($entityName);
} catch (\Exception $e) {
$entityConfig = array();
}

return isset($entityConfig[$view]['actions'][$action]) ? $entityConfig[$view]['actions'][$action] : array();
}

/**
* Checks whether the given 'action' is enabled for the given 'entity' and
* 'view'.
*
* @param string $entityName
* @param string $view
* @param string $action
*
* @return bool
*/
public function isActionEnabled($entityName, $view, $action)
{
if ($view === $action) {
return true;
}

$entityConfig = $this->getEntityConfig($entityName);

return !in_array($action, $entityConfig['disabled_actions'])
&& array_key_exists($action, $entityConfig[$view]['actions']);
}
}
1 change: 1 addition & 0 deletions Resources/config/services.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

<service id="easyadmin.configurator" class="JavierEguiluz\Bundle\EasyAdminBundle\Configuration\Configurator">
<argument /> <!-- easyadmin.config parameter -->
<argument type="service" id="property_accessor" />
</service>

<service id="easyadmin.listener.exception" class="JavierEguiluz\Bundle\EasyAdminBundle\EventListener\ExceptionListener">
Expand Down
19 changes: 18 additions & 1 deletion Resources/views/default/field_association.html.twig
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
{# a *-to-many collection of values #}
{% if value is iterable %}
<span class="badge">{{ value|length }}</span>
{% if 'show' == view %}
<ul>
{% for item in value %}
<li>
{% if link_parameters is defined %}
{% set primary_key_value = attribute(item, link_parameters.primary_key_name) %}
<a href="{{ path('easyadmin', link_parameters|merge({ id: primary_key_value })) }}">{{ item }}</a>
{% else %}
{{ item }}
{% endif %}
</li>
{% endfor %}
</ul>
{% elseif 'list' == view %}
<span class="badge">{{ value|length }}</span>
{% endif %}
{# a simple *-to-one value associated with an entity managed by this backend #}
{% elseif link_parameters is defined %}
<a href="{{ path('easyadmin', link_parameters|merge({ referer: app.request.requestUri })) }}">{{ value|easyadmin_truncate }}</a>
{% else %}
Expand Down
5 changes: 3 additions & 2 deletions Tests/Configuration/ConfiguratorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use InvalidArgumentException;
use JavierEguiluz\Bundle\EasyAdminBundle\Configuration\Configurator;
use JavierEguiluz\Bundle\EasyAdminBundle\DependencyInjection\EasyAdminExtension;
use Symfony\Component\PropertyAccess\PropertyAccessor;

class ConfiguratorTest extends \PHPUnit_Framework_TestCase
{
Expand All @@ -31,7 +32,7 @@ public function setUp()
public function testEmptyConfiguration()
{
$backendConfig = array('easy_admin' => null);
$configurator = new Configurator($backendConfig);
$configurator = new Configurator($backendConfig, new PropertyAccessor());
$configurator->getEntityConfiguration('TestEntity');
}

Expand All @@ -42,7 +43,7 @@ public function testEmptyConfiguration()
public function testAccessingAnUnmanagedEntity()
{
$backendConfig = array('easy_admin' => array('entities' => array('AppBundle\\Entity\\TestEntity')));
$configurator = new Configurator($backendConfig);
$configurator = new Configurator($backendConfig, new PropertyAccessor());
$configurator->getEntityConfiguration('UnmanagedEntity');
}
}
Expand Down
82 changes: 46 additions & 36 deletions Twig/EasyAdminTwigExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -66,21 +66,7 @@ public function getFilters()
*/
public function getBackendConfiguration($key = null)
{
$config = $this->configurator->getBackendConfig();

if (!empty($key)) {
$parts = explode('.', $key);

foreach ($parts as $part) {
if (!isset($config[$part])) {
$config = null;
break;
}
$config = $config[$part];
}
}

return $config;
return $this->configurator->getBackendConfig($key);
}

/**
Expand Down Expand Up @@ -162,30 +148,50 @@ public function renderEntityField(\Twig_Environment $twig, $view, $entityName, $
return $twig->render($entityConfiguration['templates']['label_empty'], $templateParameters);
}

if ('association' === $fieldType) {
$targetEntityConfig = $this->configurator->getEntityConfigByClass($fieldMetadata['targetEntity']);
if (null === $targetEntityConfig) {
// the associated entity is not managed by EasyAdmin
return $twig->render($entityConfiguration['templates']['field_association'], $templateParameters);
}

$isShowActionAllowed = $this->isActionEnabled($view, 'show', $targetEntityConfig['name']);
}

if ('association' === $fieldType && ($fieldMetadata['associationType'] & ClassMetadata::TO_ONE)) {
// the try..catch block is required because we can't use
// $accessor->isReadable(), which is unavailable in Symfony 2.3
try {
$targetEntityClassName = $this->getClassShortName($fieldMetadata['targetEntity']);
$targetEntityConfig = $this->getEntityConfiguration($targetEntityClassName);
$primaryKeyValue = $this->accessor->getValue($value, $targetEntityConfig['primary_key_field_name']);
} catch (\Exception $e) {
// the try..catch block is needed because we can't use the
// $accessor->isReadable(), which is unavailable in Symfony 2.3
$templateParameters['value'] = $targetEntityClassName;
$primaryKeyValue = null;
}

// get the string representation of the associated entity
// get the string representation of the associated *-to-one entity
if (method_exists($value, '__toString')) {
$templateParameters['value'] = (string) $value;
} elseif (isset($primaryKeyValue)) {
} elseif (null !== $primaryKeyValue) {
$templateParameters['value'] = sprintf('%s #%s', $targetEntityConfig['name'], $primaryKeyValue);
} else {

$templateParameters['value'] = $this->getClassShortName($fieldMetadata['targetEntity']);
}

// if the associated entity is managed by EasyAdmin, display a link to it
if (null !== $targetEntityConfig && isset($primaryKeyValue)) {
// if the associated entity is managed by EasyAdmin, and the "show"
// action is enabled for the associated entity, display a link to it
if (null !== $targetEntityConfig && null !== $primaryKeyValue && $isShowActionAllowed) {
$templateParameters['link_parameters'] = array('entity' => $targetEntityConfig['name'], 'action' => 'show', 'id' => $primaryKeyValue);
}
}

if ('association' === $fieldType && ($fieldMetadata['associationType'] & ClassMetadata::TO_MANY)) {
// if the associated entity is managed by EasyAdmin, and the "show"
// action is enabled for the associated entity, display a link to it
if (null !== $targetEntityConfig && $isShowActionAllowed) {
$templateParameters['link_parameters'] = array('entity' => $targetEntityConfig['name'], 'action' => 'show', 'primary_key_name' => $targetEntityConfig['primary_key_field_name']);
}
}

return $twig->render($fieldMetadata['template'], $templateParameters);
} catch (\Exception $e) {
if ($this->debug) {
Expand All @@ -206,10 +212,7 @@ public function renderEntityField(\Twig_Environment $twig, $view, $entityName, $
*/
public function isActionEnabled($view, $action, $entityName)
{
$entityConfiguration = $this->configurator->getEntityConfiguration($entityName);

return !in_array($action, $entityConfiguration['disabled_actions'])
&& array_key_exists($action, $entityConfiguration[$view]['actions']);
return $this->configurator->isActionEnabled($entityName, $view, $action);
}

/**
Expand All @@ -222,11 +225,7 @@ public function isActionEnabled($view, $action, $entityName)
*/
public function getActionConfiguration($view, $action, $entityName)
{
$entityConfiguration = $this->configurator->getEntityConfiguration($entityName);

return isset($entityConfiguration[$view]['actions'][$action])
? $entityConfiguration[$view]['actions'][$action]
: array();
return $this->configurator->getActionConfig($entityName, $view, $action);
}

/**
Expand All @@ -240,9 +239,14 @@ public function getActionConfiguration($view, $action, $entityName)
*/
public function getActionsForItem($view, $entityName)
{
$entityConfiguration = $this->configurator->getEntityConfiguration($entityName);
$disabledActions = $entityConfiguration['disabled_actions'];
$viewActions = $entityConfiguration[$view]['actions'];
try {
$entityConfig = $this->configurator->getEntityConfig($entityName);
} catch (\Exception $e) {
return array();
}

$disabledActions = $entityConfig['disabled_actions'];
$viewActions = $entityConfig[$view]['actions'];

$actionsExcludedForItems = array(
'list' => array('new', 'search'),
Expand All @@ -266,6 +270,12 @@ public function getActionsForItem($view, $entityName)
*/
public function truncateText(\Twig_Environment $env, $value, $length = 64, $preserve = false, $separator = '...')
{
try {
$value = (string) $value;
} catch (\Exception $e) {
$value = '';
}

if (function_exists('mb_get_info')) {
if (mb_strlen($value, $env->getCharset()) > $length) {
if ($preserve) {
Expand Down

0 comments on commit d800051

Please sign in to comment.