ModelChoiceLoader.php 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. <?php
  2. /*
  3. * This file is part of the Sonata Project package.
  4. *
  5. * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
  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 Sonata\AdminBundle\Form\ChoiceList;
  11. use Doctrine\Common\Util\ClassUtils;
  12. use Sonata\AdminBundle\Model\ModelManagerInterface;
  13. use Sonata\CoreBundle\Model\Adapter\AdapterInterface;
  14. use Symfony\Component\Form\ChoiceList\ArrayChoiceList;
  15. use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
  16. use Symfony\Component\Form\Exception\RuntimeException;
  17. use Symfony\Component\PropertyAccess\PropertyAccess;
  18. use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
  19. use Symfony\Component\PropertyAccess\PropertyPath;
  20. /**
  21. * Class ModelChoiceLoader.
  22. *
  23. * @author Thomas Rabaix <thomas.rabaix@sonata-project.org>
  24. */
  25. class ModelChoiceLoader implements ChoiceLoaderInterface
  26. {
  27. /**
  28. * @var \Sonata\AdminBundle\Model\ModelManagerInterface
  29. */
  30. private $modelManager;
  31. /**
  32. * @var string
  33. */
  34. private $class;
  35. private $property;
  36. private $query;
  37. private $choices;
  38. /**
  39. * @var PropertyPath
  40. */
  41. private $propertyPath;
  42. /**
  43. * @var PropertyAccessorInterface
  44. */
  45. private $propertyAccessor;
  46. private $choiceList;
  47. /**
  48. * @param ModelManagerInterface $modelManager
  49. * @param string $class
  50. * @param null $property
  51. * @param null $query
  52. * @param array $choices
  53. * @param PropertyAccessorInterface|null $propertyAccessor
  54. */
  55. public function __construct(ModelManagerInterface $modelManager, $class, $property = null, $query = null, $choices = array(), PropertyAccessorInterface $propertyAccessor = null)
  56. {
  57. $this->modelManager = $modelManager;
  58. $this->class = $class;
  59. $this->property = $property;
  60. $this->query = $query;
  61. $this->choices = $choices;
  62. $this->identifier = $this->modelManager->getIdentifierFieldNames($this->class);
  63. // The property option defines, which property (path) is used for
  64. // displaying entities as strings
  65. if ($property) {
  66. $this->propertyPath = new PropertyPath($property);
  67. $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::createPropertyAccessor();
  68. }
  69. }
  70. /**
  71. * {@inheritdoc}
  72. */
  73. public function loadChoiceList($value = null)
  74. {
  75. if (!$this->choiceList) {
  76. if ($this->query) {
  77. $entities = $this->modelManager->executeQuery($this->query);
  78. } elseif (is_array($this->choices) && count($this->choices) > 0) {
  79. $entities = $this->choices;
  80. } else {
  81. $entities = $this->modelManager->findBy($this->class);
  82. }
  83. $choices = array();
  84. foreach ($entities as $key => $entity) {
  85. if ($this->propertyPath) {
  86. // If the property option was given, use it
  87. $valueObject = $this->propertyAccessor->getValue($entity, $this->propertyPath);
  88. } else {
  89. // Otherwise expect a __toString() method in the entity
  90. try {
  91. $valueObject = (string) $entity;
  92. } catch (\Exception $e) {
  93. throw new RuntimeException(sprintf('Unable to convert the entity "%s" to string, provide "property" option or implement "__toString()" method in your entity.', ClassUtils::getClass($entity)), 0, $e);
  94. }
  95. }
  96. $id = implode(AdapterInterface::ID_SEPARATOR, $this->getIdentifierValues($entity));
  97. if (!array_key_exists($valueObject, $choices)) {
  98. $choices[$valueObject] = array();
  99. }
  100. $choices[$valueObject][] = $id;
  101. }
  102. $finalChoices = array();
  103. foreach ($choices as $valueObject => $idx) {
  104. if (count($idx) > 1) { // avoid issue with identical values ...
  105. foreach ($idx as $id) {
  106. $finalChoices[sprintf('%s (id: %s)', $valueObject, $id)] = $id;
  107. }
  108. } else {
  109. $finalChoices[$valueObject] = current($idx);
  110. }
  111. }
  112. $this->choiceList = new ArrayChoiceList($finalChoices, $value);
  113. }
  114. return $this->choiceList;
  115. }
  116. /**
  117. * {@inheritdoc}
  118. */
  119. public function loadChoicesForValues(array $values, $value = null)
  120. {
  121. return $this->loadChoiceList($value)->getChoicesForValues($values);
  122. }
  123. /**
  124. * {@inheritdoc}
  125. */
  126. public function loadValuesForChoices(array $choices, $value = null)
  127. {
  128. return $this->loadChoiceList($value)->getValuesForChoices($choices);
  129. }
  130. /**
  131. * @param object $entity
  132. *
  133. * @return array
  134. */
  135. private function getIdentifierValues($entity)
  136. {
  137. try {
  138. return $this->modelManager->getIdentifierValues($entity);
  139. } catch (\Exception $e) {
  140. throw new \InvalidArgumentException(sprintf('Unable to retrieve the identifier values for entity %s', ClassUtils::getClass($entity)), 0, $e);
  141. }
  142. }
  143. }