Просмотр исходного кода

Fixed detection of properties with underscores on nested forms

Jordi Sala 9 лет назад
Родитель
Сommit
0b5a38ddd8
3 измененных файлов с 41 добавлено и 46 удалено
  1. 18 40
      Admin/AdminHelper.php
  2. 3 0
      CHANGELOG.md
  3. 20 6
      Tests/Admin/AdminHelperTest.php

+ 18 - 40
Admin/AdminHelper.php

@@ -19,6 +19,7 @@ use Sonata\AdminBundle\Util\FormViewIterator;
 use Symfony\Component\Form\FormBuilderInterface;
 use Symfony\Component\Form\FormView;
 use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
+use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException;
 use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
 
 /**
@@ -254,53 +255,28 @@ class AdminHelper
     {
         $propertyAccessor = $this->pool->getPropertyAccessor();
 
-        $idWithoutUniqueIdentifier = implode('_', explode('_', substr($elementId, strpos($elementId, '_') + 1)));
+        $idWithoutIdentifier = preg_replace('/^[^_]*_/', '', $elementId);
+        $initialPath = preg_replace('#(_(\d+)_)#', '[$2]_', $idWithoutIdentifier);
 
-        //array access of id converted to format which PropertyAccessor understands
-        $initialPath = preg_replace('#(_(\d+)_)#', '[$2]', $idWithoutUniqueIdentifier);
+        $parts = explode('_', $initialPath);
+        $totalPath = '';
+        $currentPath = '';
 
-        $parts = preg_split('#\[\d+\]#', $initialPath);
+        foreach ($parts as $part) {
+            $currentPath .= empty($currentPath) ? $part : '_'.$part;
+            $separator = empty($totalPath) ? '' : '.';
 
-        $partReturnValue = $returnValue = '';
-        $currentEntity = $entity;
-
-        foreach ($parts as $key => $value) {
-            $subParts = explode('_', $value);
-            $id = '';
-            $dot = '';
-
-            foreach ($subParts as $subValue) {
-                $id .= ($id) ? '_'.$subValue : $subValue;
-
-                if ($this->pathExists($propertyAccessor, $currentEntity, $partReturnValue.$dot.$id)) {
-                    $partReturnValue .= $dot.$id;
-                    $dot = '.';
-                    $id = '';
-                } else {
-                    $dot = '';
-                }
+            if ($this->pathExists($propertyAccessor, $entity, $totalPath.$separator.$currentPath)) {
+                $totalPath .= $separator.$currentPath;
+                $currentPath = '';
             }
+        }
 
-            if ($dot !== '.') {
-                throw new \Exception(sprintf('Could not get element id from %s Failing part: %s', $elementId, $subValue));
-            }
-
-            //check if array access was in this location originally
-            preg_match("#$value\[(\d+)#", $initialPath, $matches);
-
-            if (isset($matches[1])) {
-                $partReturnValue .= '['.$matches[1].']';
-            }
-
-            $returnValue .= $returnValue ? '.'.$partReturnValue : $partReturnValue;
-            $partReturnValue = '';
-
-            if (isset($parts[$key + 1])) {
-                $currentEntity = $propertyAccessor->getValue($entity, $returnValue);
-            }
+        if (!empty($currentPath)) {
+            throw new \Exception(sprintf('Could not get element id from %s Failing part: %s', $elementId, $currentPath));
         }
 
-        return $returnValue;
+        return $totalPath;
     }
 
     /**
@@ -346,6 +322,8 @@ class AdminHelper
             return true;
         } catch (NoSuchPropertyException $e) {
             return false;
+        } catch (UnexpectedTypeException $e) {
+            return false;
         }
     }
 }

+ 3 - 0
CHANGELOG.md

@@ -6,6 +6,9 @@ This project adheres to [Semantic Versioning](http://semver.org/).
 ### Added
 - Added AbstractAdmin, replacing Admin
 
+### Fixed
+- Fix detection of path when using nested properties with underscores in `AdminHelper:getElementAccessPath` method
+
 ### Changed
 - Updated AdminLTE theme to version 2.3.3
 

+ 20 - 6
Tests/Admin/AdminHelperTest.php

@@ -104,16 +104,30 @@ class AdminHelperTest extends \PHPUnit_Framework_TestCase
     public function testGetElementAccessPath()
     {
         $object = $this->getMock('stdClass', array('getPathToObject'));
-        $subObject = $this->getMock('stdClass', array('getAnd'));
-        $sub2Object = $this->getMock('stdClass', array('getMore'));
+        $subObject = $this->getMock('stdClass', array('getAnother'));
+        $sub2Object = $this->getMock('stdClass', array('getMoreThings'));
 
         $object->expects($this->atLeastOnce())->method('getPathToObject')->will($this->returnValue(array($subObject)));
-        $subObject->expects($this->atLeastOnce())->method('getAnd')->will($this->returnValue($sub2Object));
-        $sub2Object->expects($this->atLeastOnce())->method('getMore')->will($this->returnValue('Value'));
+        $subObject->expects($this->atLeastOnce())->method('getAnother')->will($this->returnValue($sub2Object));
+        $sub2Object->expects($this->atLeastOnce())->method('getMoreThings')->will($this->returnValue('Value'));
+
+        $path = $this->helper->getElementAccessPath('uniquePartOfId_path_to_object_0_another_more_things', $object);
+
+        $this->assertSame('path_to_object[0].another.more_things', $path);
+    }
+
+    public function testItThrowsExceptionWhenDoesNotFindTheFullPath()
+    {
+        $path = 'uniquePartOfId_path_to_object_0_more_calls';
+        $object = $this->getMock('stdClass', array('getPathToObject'));
+        $subObject = $this->getMock('stdClass', array('getMore'));
+
+        $object->expects($this->atLeastOnce())->method('getPathToObject')->will($this->returnValue(array($subObject)));
+        $subObject->expects($this->atLeastOnce())->method('getMore')->will($this->returnValue('Value'));
 
-        $path = $this->helper->getElementAccessPath('uniquePartOfId_path_to_object_0_and_more', $object);
+        $this->setExpectedException('Exception', 'Could not get element id from '.$path.' Failing part: calls');
 
-        $this->assertSame('path_to_object[0].and.more', $path);
+        $this->helper->getElementAccessPath($path, $object);
     }
 
     public function testAppendFormFieldElementNested()