vendor/symfony/form/ChoiceList/Factory/PropertyAccessDecorator.php line 154

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\ChoiceList\Factory;
  11. use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
  12. use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
  13. use Symfony\Component\Form\ChoiceList\View\ChoiceListView;
  14. use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException;
  15. use Symfony\Component\PropertyAccess\PropertyAccess;
  16. use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
  17. use Symfony\Component\PropertyAccess\PropertyPath;
  18. use Symfony\Component\PropertyAccess\PropertyPathInterface;
  19. /**
  20. * Adds property path support to a choice list factory.
  21. *
  22. * Pass the decorated factory to the constructor:
  23. *
  24. * $decorator = new PropertyAccessDecorator($factory);
  25. *
  26. * You can now pass property paths for generating choice values, labels, view
  27. * indices, HTML attributes and for determining the preferred choices and the
  28. * choice groups:
  29. *
  30. * // extract values from the $value property
  31. * $list = $createListFromChoices($objects, 'value');
  32. *
  33. * @author Bernhard Schussek <[email protected]>
  34. */
  35. class PropertyAccessDecorator implements ChoiceListFactoryInterface
  36. {
  37. private $decoratedFactory;
  38. private $propertyAccessor;
  39. public function __construct(ChoiceListFactoryInterface $decoratedFactory, ?PropertyAccessorInterface $propertyAccessor = null)
  40. {
  41. $this->decoratedFactory = $decoratedFactory;
  42. $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor();
  43. }
  44. /**
  45. * Returns the decorated factory.
  46. *
  47. * @return ChoiceListFactoryInterface
  48. */
  49. public function getDecoratedFactory()
  50. {
  51. return $this->decoratedFactory;
  52. }
  53. /**
  54. * {@inheritdoc}
  55. *
  56. * @param mixed $value
  57. * @param mixed $filter
  58. *
  59. * @return ChoiceListInterface
  60. */
  61. public function createListFromChoices(iterable $choices, $value = null/* , $filter = null */)
  62. {
  63. $filter = \func_num_args() > 2 ? func_get_arg(2) : null;
  64. if (\is_string($value)) {
  65. $value = new PropertyPath($value);
  66. }
  67. if ($value instanceof PropertyPathInterface) {
  68. $accessor = $this->propertyAccessor;
  69. $value = function ($choice) use ($accessor, $value) {
  70. // The callable may be invoked with a non-object/array value
  71. // when such values are passed to
  72. // ChoiceListInterface::getValuesForChoices(). Handle this case
  73. // so that the call to getValue() doesn't break.
  74. return \is_object($choice) || \is_array($choice) ? $accessor->getValue($choice, $value) : null;
  75. };
  76. }
  77. if (\is_string($filter)) {
  78. $filter = new PropertyPath($filter);
  79. }
  80. if ($filter instanceof PropertyPath) {
  81. $accessor = $this->propertyAccessor;
  82. $filter = static function ($choice) use ($accessor, $filter) {
  83. return (\is_object($choice) || \is_array($choice)) && $accessor->getValue($choice, $filter);
  84. };
  85. }
  86. return $this->decoratedFactory->createListFromChoices($choices, $value, $filter);
  87. }
  88. /**
  89. * {@inheritdoc}
  90. *
  91. * @param mixed $value
  92. * @param mixed $filter
  93. *
  94. * @return ChoiceListInterface
  95. */
  96. public function createListFromLoader(ChoiceLoaderInterface $loader, $value = null/* , $filter = null */)
  97. {
  98. $filter = \func_num_args() > 2 ? func_get_arg(2) : null;
  99. if (\is_string($value)) {
  100. $value = new PropertyPath($value);
  101. }
  102. if ($value instanceof PropertyPathInterface) {
  103. $accessor = $this->propertyAccessor;
  104. $value = function ($choice) use ($accessor, $value) {
  105. // The callable may be invoked with a non-object/array value
  106. // when such values are passed to
  107. // ChoiceListInterface::getValuesForChoices(). Handle this case
  108. // so that the call to getValue() doesn't break.
  109. return \is_object($choice) || \is_array($choice) ? $accessor->getValue($choice, $value) : null;
  110. };
  111. }
  112. if (\is_string($filter)) {
  113. $filter = new PropertyPath($filter);
  114. }
  115. if ($filter instanceof PropertyPath) {
  116. $accessor = $this->propertyAccessor;
  117. $filter = static function ($choice) use ($accessor, $filter) {
  118. return (\is_object($choice) || \is_array($choice)) && $accessor->getValue($choice, $filter);
  119. };
  120. }
  121. return $this->decoratedFactory->createListFromLoader($loader, $value, $filter);
  122. }
  123. /**
  124. * {@inheritdoc}
  125. *
  126. * @param mixed $preferredChoices
  127. * @param mixed $label
  128. * @param mixed $index
  129. * @param mixed $groupBy
  130. * @param mixed $attr
  131. * @param mixed $labelTranslationParameters
  132. *
  133. * @return ChoiceListView
  134. */
  135. public function createView(ChoiceListInterface $list, $preferredChoices = null, $label = null, $index = null, $groupBy = null, $attr = null/* , $labelTranslationParameters = [] */)
  136. {
  137. $labelTranslationParameters = \func_num_args() > 6 ? func_get_arg(6) : [];
  138. $accessor = $this->propertyAccessor;
  139. if (\is_string($label)) {
  140. $label = new PropertyPath($label);
  141. }
  142. if ($label instanceof PropertyPathInterface) {
  143. $label = function ($choice) use ($accessor, $label) {
  144. return $accessor->getValue($choice, $label);
  145. };
  146. }
  147. if (\is_string($preferredChoices)) {
  148. $preferredChoices = new PropertyPath($preferredChoices);
  149. }
  150. if ($preferredChoices instanceof PropertyPathInterface) {
  151. $preferredChoices = function ($choice) use ($accessor, $preferredChoices) {
  152. try {
  153. return $accessor->getValue($choice, $preferredChoices);
  154. } catch (UnexpectedTypeException $e) {
  155. // Assume not preferred if not readable
  156. return false;
  157. }
  158. };
  159. }
  160. if (\is_string($index)) {
  161. $index = new PropertyPath($index);
  162. }
  163. if ($index instanceof PropertyPathInterface) {
  164. $index = function ($choice) use ($accessor, $index) {
  165. return $accessor->getValue($choice, $index);
  166. };
  167. }
  168. if (\is_string($groupBy)) {
  169. $groupBy = new PropertyPath($groupBy);
  170. }
  171. if ($groupBy instanceof PropertyPathInterface) {
  172. $groupBy = function ($choice) use ($accessor, $groupBy) {
  173. try {
  174. return $accessor->getValue($choice, $groupBy);
  175. } catch (UnexpectedTypeException $e) {
  176. // Don't group if path is not readable
  177. return null;
  178. }
  179. };
  180. }
  181. if (\is_string($attr)) {
  182. $attr = new PropertyPath($attr);
  183. }
  184. if ($attr instanceof PropertyPathInterface) {
  185. $attr = function ($choice) use ($accessor, $attr) {
  186. return $accessor->getValue($choice, $attr);
  187. };
  188. }
  189. if (\is_string($labelTranslationParameters)) {
  190. $labelTranslationParameters = new PropertyPath($labelTranslationParameters);
  191. }
  192. if ($labelTranslationParameters instanceof PropertyPath) {
  193. $labelTranslationParameters = static function ($choice) use ($accessor, $labelTranslationParameters) {
  194. return $accessor->getValue($choice, $labelTranslationParameters);
  195. };
  196. }
  197. return $this->decoratedFactory->createView(
  198. $list,
  199. $preferredChoices,
  200. $label,
  201. $index,
  202. $groupBy,
  203. $attr,
  204. $labelTranslationParameters
  205. );
  206. }
  207. }