|
@@ -18,6 +18,8 @@ use Sonata\AdminBundle\Util\FormBuilderIterator;
|
|
|
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\PropertyAccessor;
|
|
|
|
|
|
/**
|
|
|
* Class AdminHelper.
|
|
@@ -113,42 +115,67 @@ class AdminHelper
|
|
|
// get the field element
|
|
|
$childFormBuilder = $this->getChildFormBuilder($formBuilder, $elementId);
|
|
|
|
|
|
- // retrieve the FieldDescription
|
|
|
- $fieldDescription = $admin->getFormFieldDescription($childFormBuilder->getName());
|
|
|
+ //Child form not found (probably nested one)
|
|
|
+ //if childFormBuilder was not found resulted in fatal error getName() method call on non object
|
|
|
+ if (!$childFormBuilder) {
|
|
|
+ $propertyAccessor = new PropertyAccessor();
|
|
|
+ $entity = $admin->getSubject();
|
|
|
|
|
|
- try {
|
|
|
- $value = $fieldDescription->getValue($form->getData());
|
|
|
- } catch (NoValueException $e) {
|
|
|
- $value = null;
|
|
|
- }
|
|
|
+ $path = $this->getElementAccessPath($elementId, $entity);
|
|
|
|
|
|
- // retrieve the posted data
|
|
|
- $data = $admin->getRequest()->get($formBuilder->getName());
|
|
|
+ $collection = $propertyAccessor->getValue($entity, $path);
|
|
|
|
|
|
- if (!isset($data[$childFormBuilder->getName()])) {
|
|
|
- $data[$childFormBuilder->getName()] = array();
|
|
|
- }
|
|
|
+ if ($collection instanceof \Doctrine\ORM\PersistentCollection || $collection instanceof \Doctrine\ODM\MongoDB\PersistentCollection) {
|
|
|
+ //since doctrine 2.4
|
|
|
+ $entityClassName = $collection->getTypeClass()->getName();
|
|
|
+ } elseif ($collection instanceof \Doctrine\Common\Collections\Collection) {
|
|
|
+ $entityClassName = $this->getEntityClassName($admin, explode('.', preg_replace('#\[\d*?\]#', '', $path)));
|
|
|
+ } else {
|
|
|
+ throw new \Exception('unknown collection class');
|
|
|
+ }
|
|
|
|
|
|
- $objectCount = count($value);
|
|
|
- $postCount = count($data[$childFormBuilder->getName()]);
|
|
|
+ $collection->add(new $entityClassName());
|
|
|
+ $propertyAccessor->setValue($entity, $path, $collection);
|
|
|
|
|
|
- $fields = array_keys($fieldDescription->getAssociationAdmin()->getFormFieldDescriptions());
|
|
|
+ $fieldDescription = null;
|
|
|
+ } else {
|
|
|
+ // retrieve the FieldDescription
|
|
|
+ $fieldDescription = $admin->getFormFieldDescription($childFormBuilder->getName());
|
|
|
|
|
|
- // for now, not sure how to do that
|
|
|
- $value = array();
|
|
|
- foreach ($fields as $name) {
|
|
|
- $value[$name] = '';
|
|
|
- }
|
|
|
+ try {
|
|
|
+ $value = $fieldDescription->getValue($form->getData());
|
|
|
+ } catch (NoValueException $e) {
|
|
|
+ $value = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // retrieve the posted data
|
|
|
+ $data = $admin->getRequest()->get($formBuilder->getName());
|
|
|
+
|
|
|
+ if (!isset($data[$childFormBuilder->getName()])) {
|
|
|
+ $data[$childFormBuilder->getName()] = array();
|
|
|
+ }
|
|
|
+
|
|
|
+ $objectCount = count($value);
|
|
|
+ $postCount = count($data[$childFormBuilder->getName()]);
|
|
|
+
|
|
|
+ $fields = array_keys($fieldDescription->getAssociationAdmin()->getFormFieldDescriptions());
|
|
|
+
|
|
|
+ // for now, not sure how to do that
|
|
|
+ $value = array();
|
|
|
+ foreach ($fields as $name) {
|
|
|
+ $value[$name] = '';
|
|
|
+ }
|
|
|
+
|
|
|
+ // add new elements to the subject
|
|
|
+ while ($objectCount < $postCount) {
|
|
|
+ // append a new instance into the object
|
|
|
+ $this->addNewInstance($form->getData(), $fieldDescription);
|
|
|
+ ++$objectCount;
|
|
|
+ }
|
|
|
|
|
|
- // add new elements to the subject
|
|
|
- while ($objectCount < $postCount) {
|
|
|
- // append a new instance into the object
|
|
|
$this->addNewInstance($form->getData(), $fieldDescription);
|
|
|
- ++$objectCount;
|
|
|
}
|
|
|
|
|
|
- $this->addNewInstance($form->getData(), $fieldDescription);
|
|
|
-
|
|
|
$finalForm = $admin->getFormBuilder()->getForm();
|
|
|
$finalForm->setData($subject);
|
|
|
|
|
@@ -169,7 +196,7 @@ class AdminHelper
|
|
|
public function addNewInstance($object, FieldDescriptionInterface $fieldDescription)
|
|
|
{
|
|
|
$instance = $fieldDescription->getAssociationAdmin()->getNewInstance();
|
|
|
- $mapping = $fieldDescription->getAssociationMapping();
|
|
|
+ $mapping = $fieldDescription->getAssociationMapping();
|
|
|
|
|
|
$method = sprintf('add%s', $this->camelize($mapping['fieldName']));
|
|
|
|
|
@@ -201,4 +228,113 @@ class AdminHelper
|
|
|
{
|
|
|
return BaseFieldDescription::camelize($property);
|
|
|
}
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Recursively find the class name of the admin responsible for the element at the end of an association chain.
|
|
|
+ *
|
|
|
+ * @param AdminInterface $admin
|
|
|
+ * @param array $elements
|
|
|
+ *
|
|
|
+ * @return string
|
|
|
+ */
|
|
|
+ protected function getEntityClassName(AdminInterface $admin, $elements)
|
|
|
+ {
|
|
|
+ $element = array_shift($elements);
|
|
|
+ $associationAdmin = $admin->getFormFieldDescription($element)->getAssociationAdmin();
|
|
|
+ if (count($elements) == 0) {
|
|
|
+ return $associationAdmin->getClass();
|
|
|
+ } else {
|
|
|
+ return $this->getEntityClassName($associationAdmin, $elements);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * get access path to element which works with PropertyAccessor.
|
|
|
+ *
|
|
|
+ * @param string $elementId expects string in format used in form id field. (uniqueIdentifier_model_sub_model or uniqueIdentifier_model_1_sub_model etc.)
|
|
|
+ * @param mixed $entity
|
|
|
+ *
|
|
|
+ * @return string
|
|
|
+ *
|
|
|
+ * @throws \Exception
|
|
|
+ */
|
|
|
+ public function getElementAccessPath($elementId, $entity)
|
|
|
+ {
|
|
|
+ $propertyAccessor = new PropertyAccessor();
|
|
|
+
|
|
|
+ $idWithoutUniqueIdentifier = implode('_', explode('_', substr($elementId, strpos($elementId, '_') + 1)));
|
|
|
+
|
|
|
+ //array access of id converted to format which PropertyAccessor understands
|
|
|
+ $initialPath = preg_replace('#(_(\d+)_)#', '[$2]', $idWithoutUniqueIdentifier);
|
|
|
+
|
|
|
+ $parts = preg_split('#\[\d+\]#', $initialPath);
|
|
|
+
|
|
|
+ $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 ($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);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return $returnValue;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * check if given path exists in $entity.
|
|
|
+ *
|
|
|
+ * @param PropertyAccessor $propertyAccessor
|
|
|
+ * @param mixed $entity
|
|
|
+ * @param string $path
|
|
|
+ *
|
|
|
+ * @return bool
|
|
|
+ *
|
|
|
+ * @throws \RuntimeException
|
|
|
+ */
|
|
|
+ private function pathExists(PropertyAccessor $propertyAccessor, $entity, $path)
|
|
|
+ {
|
|
|
+ //sf2 <= 2.3 did not have isReadable method for PropertyAccessor
|
|
|
+ if (method_exists($propertyAccessor, 'isReadable')) {
|
|
|
+ return $propertyAccessor->isReadable($entity, $path);
|
|
|
+ } else {
|
|
|
+ try {
|
|
|
+ $propertyAccessor->getValue($entity, $path);
|
|
|
+
|
|
|
+ return true;
|
|
|
+ } catch (NoSuchPropertyException $e) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|