vendor/symfony/form/Extension/Core/DataAccessor/PropertyPathAccessor.php line 54

  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Component\Form\Extension\Core\DataAccessor;
  11. use Symfony\Component\Form\DataAccessorInterface;
  12. use Symfony\Component\Form\DataMapperInterface;
  13. use Symfony\Component\Form\Exception\AccessException;
  14. use Symfony\Component\Form\Extension\Core\DataMapper\DataMapper;
  15. use Symfony\Component\Form\FormInterface;
  16. use Symfony\Component\PropertyAccess\Exception\AccessException as PropertyAccessException;
  17. use Symfony\Component\PropertyAccess\Exception\NoSuchIndexException;
  18. use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
  19. use Symfony\Component\PropertyAccess\Exception\UninitializedPropertyException;
  20. use Symfony\Component\PropertyAccess\PropertyAccess;
  21. use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
  22. use Symfony\Component\PropertyAccess\PropertyPathInterface;
  23. /**
  24.  * Writes and reads values to/from an object or array using property path.
  25.  *
  26.  * @author Yonel Ceruto <yonelceruto@gmail.com>
  27.  * @author Bernhard Schussek <bschussek@gmail.com>
  28.  */
  29. class PropertyPathAccessor implements DataAccessorInterface
  30. {
  31.     private PropertyAccessorInterface $propertyAccessor;
  32.     public function __construct(?PropertyAccessorInterface $propertyAccessor null)
  33.     {
  34.         $this->propertyAccessor $propertyAccessor ?? PropertyAccess::createPropertyAccessor();
  35.     }
  36.     public function getValue(object|array $dataFormInterface $form): mixed
  37.     {
  38.         if (null === $propertyPath $form->getPropertyPath()) {
  39.             throw new AccessException('Unable to read from the given form data as no property path is defined.');
  40.         }
  41.         return $this->getPropertyValue($data$propertyPath);
  42.     }
  43.     public function setValue(object|array &$datamixed $valueFormInterface $form): void
  44.     {
  45.         if (null === $propertyPath $form->getPropertyPath()) {
  46.             throw new AccessException('Unable to write the given value as no property path is defined.');
  47.         }
  48.         $getValue = function () use ($data$form$propertyPath) {
  49.             $dataMapper $this->getDataMapper($form);
  50.             if ($dataMapper instanceof DataMapper && null !== $dataAccessor $dataMapper->getDataAccessor()) {
  51.                 return $dataAccessor->getValue($data$form);
  52.             }
  53.             return $this->getPropertyValue($data$propertyPath);
  54.         };
  55.         // If the field is of type DateTimeInterface and the data is the same skip the update to
  56.         // keep the original object hash
  57.         if ($value instanceof \DateTimeInterface && $value == $getValue()) {
  58.             return;
  59.         }
  60.         // If the data is identical to the value in $data, we are
  61.         // dealing with a reference
  62.         if (!\is_object($data) || !$form->getConfig()->getByReference() || $value !== $getValue()) {
  63.             try {
  64.                 $this->propertyAccessor->setValue($data$propertyPath$value);
  65.             } catch (NoSuchPropertyException $e) {
  66.                 throw new NoSuchPropertyException($e->getMessage().' Make the property public, add a setter, or set the "mapped" field option in the form type to be false.'0$e);
  67.             }
  68.         }
  69.     }
  70.     public function isReadable(object|array $dataFormInterface $form): bool
  71.     {
  72.         return null !== $form->getPropertyPath();
  73.     }
  74.     public function isWritable(object|array $dataFormInterface $form): bool
  75.     {
  76.         return null !== $form->getPropertyPath();
  77.     }
  78.     private function getPropertyValue(object|array $dataPropertyPathInterface $propertyPath): mixed
  79.     {
  80.         try {
  81.             return $this->propertyAccessor->getValue($data$propertyPath);
  82.         } catch (PropertyAccessException $e) {
  83.             if (\is_array($data) && $e instanceof NoSuchIndexException) {
  84.                 return null;
  85.             }
  86.             if (!$e instanceof UninitializedPropertyException
  87.                 // For versions without UninitializedPropertyException check the exception message
  88.                 && (class_exists(UninitializedPropertyException::class) || !str_contains($e->getMessage(), 'You should initialize it'))
  89.             ) {
  90.                 throw $e;
  91.             }
  92.             return null;
  93.         }
  94.     }
  95.     private function getDataMapper(FormInterface $form): ?DataMapperInterface
  96.     {
  97.         do {
  98.             $dataMapper $form->getConfig()->getDataMapper();
  99.         } while (null === $dataMapper && null !== $form $form->getParent());
  100.         return $dataMapper;
  101.     }
  102. }