Bladeren bron

[FieldDescription] Ensure the object is not used when getting the value of a virtual field (action, batch, etc.)

Mathieu Lemoine 10 jaren geleden
bovenliggende
commit
9bd9f157dc

+ 8 - 6
Admin/Admin.php

@@ -783,9 +783,10 @@ abstract class Admin implements AdminInterface, DomainObjectInterface
 
         if (count($this->getBatchActions()) > 0) {
             $fieldDescription = $this->getModelManager()->getNewFieldDescriptionInstance($this->getClass(), 'batch', array(
-                'label'    => 'batch',
-                'code'     => '_batch',
-                'sortable' => false,
+                'label'         => 'batch',
+                'code'          => '_batch',
+                'sortable'      => false,
+                'virtual_field' => true,
             ));
 
             $fieldDescription->setAdmin($this);
@@ -802,9 +803,10 @@ abstract class Admin implements AdminInterface, DomainObjectInterface
 
         if ($this->hasRequest() && $this->getRequest()->isXmlHttpRequest()) {
             $fieldDescription = $this->getModelManager()->getNewFieldDescriptionInstance($this->getClass(), 'select', array(
-                'label'    => false,
-                'code'     => '_select',
-                'sortable' => false,
+                'label'         => false,
+                'code'          => '_select',
+                'sortable'      => false,
+                'virtual_field' => false,
             ));
 
             $fieldDescription->setAdmin($this);

+ 14 - 0
Admin/BaseFieldDescription.php

@@ -321,6 +321,10 @@ abstract class BaseFieldDescription implements FieldDescriptionInterface
      */
     public function getFieldValue($object, $fieldName)
     {
+        if ($this->isVirtual()) {
+           return null;
+        }
+
         $camelizedFieldName = self::camelize($fieldName);
 
         $getters = array();
@@ -481,4 +485,14 @@ abstract class BaseFieldDescription implements FieldDescriptionInterface
     {
         return $this->getOption('translation_domain') ?: $this->getAdmin()->getTranslationDomain();
     }
+
+    /**
+     * Return true if field is virtual.
+     *
+     * @return boolean
+     */
+    public function isVirtual()
+    {
+        return false !== $this->getOption('virtual_field', false);
+    }
 }

+ 5 - 0
Datagrid/ListMapper.php

@@ -85,6 +85,11 @@ class ListMapper extends BaseMapper
             }
         }
 
+        // Ensure batch and action pseudo-fields are tagged as virtual
+        if (in_array($type, array('action', 'batch', 'select'))) {
+            $fieldDescriptionOptions['virtual_field'] = true;
+        }
+
         if ($name instanceof FieldDescriptionInterface) {
             $fieldDescription = $name;
             $fieldDescription->mergeOptions($fieldDescriptionOptions);

+ 12 - 0
Resources/doc/cookbook/recipe_virtual_field.rst

@@ -0,0 +1,12 @@
+Virtual Field Descriptions
+==========================
+
+Some fields including in the various Mappers don't rely on any actual field in
+the Model (e.g., ``_action`` or ``batch``).
+
+In order to prevent any side-effects when trying to retrieve the value of this
+field (which doesn't exist), the option ``virtual_field`` is specified for these
+fields.
+
+When the template is instanciated, or whenever the value of the field is
+required, ``null`` will simply be returned without prying on the Model itself.

+ 1 - 0
Resources/doc/index.rst

@@ -72,3 +72,4 @@ Cookbook
    cookbook/recipe_customizing_a_mosaic_list
    cookbook/recipe_overwrite_admin_configuration
    cookbook/recipe_improve_performance_large_datasets
+   cookbook/recipe_virtual_field

+ 9 - 0
Tests/Admin/BaseFieldDescriptionTest.php

@@ -157,6 +157,15 @@ class BaseFieldDescriptionTest extends \PHPUnit_Framework_TestCase
         $description->getFieldValue($mock, 'fake');
     }
 
+    public function testGetVirtualValue()
+    {
+        $description = new FieldDescription();
+        $mock = $this->getMock('stdClass', array('getFoo'));
+
+        $description->setOption('virtual_field', true);
+        $description->getFieldValue($mock, 'fake');
+    }
+
     /**
      * @expectedException RuntimeException
      */

+ 11 - 0
Tests/Datagrid/ListMapperTest.php

@@ -212,6 +212,17 @@ class ListMapperTest extends \PHPUnit_Framework_TestCase
         $this->fail('Failed asserting that exception of type "\RuntimeException" is thrown.');
     }
 
+    public function testAutoAddVirtualOption()
+    {
+        foreach (array('action', 'batch', 'select') as $type) {
+            $this->listMapper->add('_'.$type, $type);
+        }
+
+        foreach ($this->fieldDescriptionCollection as $field) {
+           $this->assertTrue($field->isVirtual(), 'Failed asserting that FieldDescription with type "'.$field->getType().'" is tagged with virtual flag.');
+        }
+    }
+
     public function testReorder()
     {
         $fieldDescription1 = $this->getFieldDescriptionMock('fooName1', 'fooLabel1');