123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340 |
- <?php
- /*
- * This file is part of the Sonata Project package.
- *
- * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
- namespace Sonata\AdminBundle\Admin;
- use Doctrine\Common\Inflector\Inflector;
- use Doctrine\Common\Util\ClassUtils;
- use Sonata\AdminBundle\Exception\NoValueException;
- 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.
- *
- * @author Thomas Rabaix <thomas.rabaix@sonata-project.org>
- */
- class AdminHelper
- {
- /**
- * @var Pool
- */
- protected $pool;
- /**
- * @param Pool $pool
- */
- public function __construct(Pool $pool)
- {
- $this->pool = $pool;
- }
- /**
- * @throws \RuntimeException
- *
- * @param FormBuilderInterface $formBuilder
- * @param string $elementId
- *
- * @return FormBuilderInterface
- */
- public function getChildFormBuilder(FormBuilderInterface $formBuilder, $elementId)
- {
- foreach (new FormBuilderIterator($formBuilder) as $name => $formBuilder) {
- if ($name == $elementId) {
- return $formBuilder;
- }
- }
- return;
- }
- /**
- * @param FormView $formView
- * @param string $elementId
- *
- * @return null|FormView
- */
- public function getChildFormView(FormView $formView, $elementId)
- {
- foreach (new \RecursiveIteratorIterator(new FormViewIterator($formView), \RecursiveIteratorIterator::SELF_FIRST) as $name => $formView) {
- if ($name === $elementId) {
- return $formView;
- }
- }
- return;
- }
- /**
- * @deprecated
- *
- * @param string $code
- *
- * @return AdminInterface
- */
- public function getAdmin($code)
- {
- return $this->pool->getInstance($code);
- }
- /**
- * Note:
- * This code is ugly, but there is no better way of doing it.
- * For now the append form element action used to add a new row works
- * only for direct FieldDescription (not nested one).
- *
- * @throws \RuntimeException
- *
- * @param AdminInterface $admin
- * @param object $subject
- * @param string $elementId
- *
- * @return array
- */
- public function appendFormFieldElement(AdminInterface $admin, $subject, $elementId)
- {
- // retrieve the subject
- $formBuilder = $admin->getFormBuilder();
- $form = $formBuilder->getForm();
- $form->setData($subject);
- $form->handleRequest($admin->getRequest());
- // get the field element
- $childFormBuilder = $this->getChildFormBuilder($formBuilder, $elementId);
- //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();
- $path = $this->getElementAccessPath($elementId, $entity);
- $collection = $propertyAccessor->getValue($entity, $path);
- 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');
- }
- $collection->add(new $entityClassName());
- $propertyAccessor->setValue($entity, $path, $collection);
- $fieldDescription = null;
- } else {
- // retrieve the FieldDescription
- $fieldDescription = $admin->getFormFieldDescription($childFormBuilder->getName());
- 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;
- }
- $this->addNewInstance($form->getData(), $fieldDescription);
- }
- $finalForm = $admin->getFormBuilder()->getForm();
- $finalForm->setData($subject);
- // bind the data
- $finalForm->setData($form->getData());
- return array($fieldDescription, $finalForm);
- }
- /**
- * Add a new instance to the related FieldDescriptionInterface value.
- *
- * @param object $object
- * @param FieldDescriptionInterface $fieldDescription
- *
- * @throws \RuntimeException
- */
- public function addNewInstance($object, FieldDescriptionInterface $fieldDescription)
- {
- $instance = $fieldDescription->getAssociationAdmin()->getNewInstance();
- $mapping = $fieldDescription->getAssociationMapping();
- $method = sprintf('add%s', $this->camelize($mapping['fieldName']));
- if (!method_exists($object, $method)) {
- $method = rtrim($method, 's');
- if (!method_exists($object, $method)) {
- $method = sprintf('add%s', $this->camelize(Inflector::singularize($mapping['fieldName'])));
- if (!method_exists($object, $method)) {
- throw new \RuntimeException(sprintf('Please add a method %s in the %s class!', $method, ClassUtils::getClass($object)));
- }
- }
- }
- $object->$method($instance);
- }
- /**
- * Camelize a string.
- *
- * @static
- *
- * @param string $property
- *
- * @return string
- */
- public function camelize($property)
- {
- 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;
- }
- }
- }
- }
|