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

Open in your IDE?
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <[email protected]>
  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\UninitializedPropertyException;
  19. use Symfony\Component\PropertyAccess\PropertyAccess;
  20. use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
  21. use Symfony\Component\PropertyAccess\PropertyPathInterface;
  22. /**
  23. * Writes and reads values to/from an object or array using property path.
  24. *
  25. * @author Yonel Ceruto <[email protected]>
  26. * @author Bernhard Schussek <[email protected]>
  27. */
  28. class PropertyPathAccessor implements DataAccessorInterface
  29. {
  30. private $propertyAccessor;
  31. public function __construct(?PropertyAccessorInterface $propertyAccessor = null)
  32. {
  33. $this->propertyAccessor = $propertyAccessor ?? PropertyAccess::createPropertyAccessor();
  34. }
  35. /**
  36. * {@inheritdoc}
  37. */
  38. public function getValue($data, FormInterface $form)
  39. {
  40. if (null === $propertyPath = $form->getPropertyPath()) {
  41. throw new AccessException('Unable to read from the given form data as no property path is defined.');
  42. }
  43. return $this->getPropertyValue($data, $propertyPath);
  44. }
  45. /**
  46. * {@inheritdoc}
  47. */
  48. public function setValue(&$data, $propertyValue, FormInterface $form): void
  49. {
  50. if (null === $propertyPath = $form->getPropertyPath()) {
  51. throw new AccessException('Unable to write the given value as no property path is defined.');
  52. }
  53. $getValue = function () use ($data, $form, $propertyPath) {
  54. $dataMapper = $this->getDataMapper($form);
  55. if ($dataMapper instanceof DataMapper && null !== $dataAccessor = $dataMapper->getDataAccessor()) {
  56. return $dataAccessor->getValue($data, $form);
  57. }
  58. return $this->getPropertyValue($data, $propertyPath);
  59. };
  60. // If the field is of type DateTimeInterface and the data is the same skip the update to
  61. // keep the original object hash
  62. if ($propertyValue instanceof \DateTimeInterface && $propertyValue == $getValue()) {
  63. return;
  64. }
  65. // If the data is identical to the value in $data, we are
  66. // dealing with a reference
  67. if (!\is_object($data) || !$form->getConfig()->getByReference() || $propertyValue !== $getValue()) {
  68. $this->propertyAccessor->setValue($data, $propertyPath, $propertyValue);
  69. }
  70. }
  71. /**
  72. * {@inheritdoc}
  73. */
  74. public function isReadable($data, FormInterface $form): bool
  75. {
  76. return null !== $form->getPropertyPath();
  77. }
  78. /**
  79. * {@inheritdoc}
  80. */
  81. public function isWritable($data, FormInterface $form): bool
  82. {
  83. return null !== $form->getPropertyPath();
  84. }
  85. private function getPropertyValue($data, PropertyPathInterface $propertyPath)
  86. {
  87. try {
  88. return $this->propertyAccessor->getValue($data, $propertyPath);
  89. } catch (PropertyAccessException $e) {
  90. if (\is_array($data) && $e instanceof NoSuchIndexException) {
  91. return null;
  92. }
  93. if (!$e instanceof UninitializedPropertyException
  94. // For versions without UninitializedPropertyException check the exception message
  95. && (class_exists(UninitializedPropertyException::class) || false === strpos($e->getMessage(), 'You should initialize it'))
  96. ) {
  97. throw $e;
  98. }
  99. return null;
  100. }
  101. }
  102. private function getDataMapper(FormInterface $form): ?DataMapperInterface
  103. {
  104. do {
  105. $dataMapper = $form->getConfig()->getDataMapper();
  106. } while (null === $dataMapper && null !== $form = $form->getParent());
  107. return $dataMapper;
  108. }
  109. }