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