ModelManager.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372
  1. <?php
  2. /*
  3. * This file is part of the Sonata 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\Model\ORM;
  11. use Sonata\AdminBundle\Model\ModelManagerInterface;
  12. use Sonata\AdminBundle\Admin\ORM\FieldDescription;
  13. use Sonata\AdminBundle\Admin\FieldDescriptionInterface;
  14. use Sonata\AdminBundle\Datagrid\DatagridInterface;
  15. use Doctrine\ORM\EntityManager;
  16. use Doctrine\ORM\QueryBuilder;
  17. use Symfony\Component\Form\Exception\PropertyAccessDeniedException;
  18. class ModelManager implements ModelManagerInterface
  19. {
  20. protected $entityManager;
  21. /**
  22. *
  23. * @param \Doctrine\ORM\EntityManager $entityManager
  24. */
  25. public function __construct(EntityManager $entityManager)
  26. {
  27. $this->entityManager = $entityManager;
  28. }
  29. /**
  30. * Returns the related model's metadata
  31. *
  32. * @abstract
  33. * @param string $name
  34. * @return \Doctrine\ORM\Mapping\ClassMetadataInfo
  35. */
  36. public function getMetadata($class)
  37. {
  38. return $this->entityManager->getMetadataFactory()->getMetadataFor($class);
  39. }
  40. /**
  41. * Returns true is the model has some metadata
  42. *
  43. * @return boolean
  44. */
  45. public function hasMetadata($class)
  46. {
  47. return $this->entityManager->getMetadataFactory()->hasMetadataFor($class);
  48. }
  49. /**
  50. * Returns a new FieldDescription
  51. *
  52. * @abstract
  53. * @return \Sonata\AdminBundle\Admin\ORM\FieldDescription
  54. */
  55. public function getNewFieldDescriptionInstance($class, $name, array $options = array())
  56. {
  57. $metadata = $this->getMetadata($class);
  58. $fieldDescription = new FieldDescription;
  59. $fieldDescription->setName($name);
  60. $fieldDescription->setOptions($options);
  61. if (isset($metadata->associationMappings[$name])) {
  62. $fieldDescription->setAssociationMapping($metadata->associationMappings[$name]);
  63. }
  64. if (isset($metadata->fieldMappings[$name])) {
  65. $fieldDescription->setFieldMapping($metadata->fieldMappings[$name]);
  66. }
  67. return $fieldDescription;
  68. }
  69. public function create($object)
  70. {
  71. $this->entityManager->persist($object);
  72. $this->entityManager->flush();
  73. }
  74. public function update($object)
  75. {
  76. $this->entityManager->persist($object);
  77. $this->entityManager->flush();
  78. }
  79. public function delete($object)
  80. {
  81. $this->entityManager->remove($object);
  82. $this->entityManager->flush();
  83. }
  84. /**
  85. * Find one object from the given class repository.
  86. *
  87. * @param string $class Class name
  88. * @param string|int $id Identifier. Can be a string with several IDs concatenated, separated by '-'.
  89. * @return Object
  90. */
  91. public function findOne($class, $id)
  92. {
  93. $values = array_combine($this->getIdentifierFieldNames($class), explode('-', $id));
  94. return $this->entityManager->getRepository($class)->find($values);
  95. }
  96. public function find($class, array $criteria = array())
  97. {
  98. return $this->entityManager->getRepository($class)->findBy($criteria);
  99. }
  100. /**
  101. * @return \Doctrine\ORM\EntityManager
  102. */
  103. public function getEntityManager()
  104. {
  105. return $this->entityManager;
  106. }
  107. /**
  108. * @param string $parentAssociationMapping
  109. * @param string $class
  110. * @return \Sonata\AdminBundle\Admin\ORM\FieldDescription
  111. */
  112. public function getParentFieldDescription($parentAssociationMapping, $class)
  113. {
  114. $fieldName = $parentAssociationMapping['fieldName'];
  115. $metadata = $this->getMetadata($class);
  116. $associatingMapping = $metadata->associationMappings[$parentAssociationMapping];
  117. $fieldDescription = $this->getNewFieldDescriptionInstance($class, $fieldName);
  118. $fieldDescription->setName($parentAssociationMapping);
  119. $fieldDescription->setAssociationMapping($associatingMapping);
  120. return $fieldDescription;
  121. }
  122. /**
  123. * @param string $alias
  124. * @return \Doctrine\ORM\QueryBuilder a query instance
  125. */
  126. public function createQuery($class, $alias = 'o')
  127. {
  128. $repository = $this->getEntityManager()->getRepository($class);
  129. return $repository->createQueryBuilder($alias);
  130. }
  131. public function executeQuery($query)
  132. {
  133. if ($query instanceof QueryBuilder) {
  134. return $query->getQuery()->execute();
  135. }
  136. return $query->execute();
  137. }
  138. /**
  139. * @param string $class
  140. * @return string
  141. */
  142. public function getModelIdentifier($class)
  143. {
  144. return $this->getMetadata($class)->identifier;
  145. }
  146. public function getIdentifierValues($entity)
  147. {
  148. if (!$this->getEntityManager()->getUnitOfWork()->isInIdentityMap($entity)) {
  149. throw new \RuntimeException('Entities passed to the choice field must be managed');
  150. }
  151. return $this->getEntityManager()->getUnitOfWork()->getEntityIdentifier($entity);
  152. }
  153. public function getIdentifierFieldNames($class)
  154. {
  155. return $this->getMetadata($class)->getIdentifierFieldNames();
  156. }
  157. public function getNormalizedIdentifier($entity)
  158. {
  159. // the entities is not managed
  160. if (!$entity || !$this->getEntityManager()->getUnitOfWork()->isInIdentityMap($entity)) {
  161. return null;
  162. }
  163. $values = $this->getIdentifierValues($entity);
  164. return implode('-', $values);
  165. }
  166. /**
  167. * Deletes a set of $class identified by the provided $idx array
  168. *
  169. * @param string $class
  170. * @param array $idx
  171. * @return void
  172. */
  173. public function batchDelete($class, $idx)
  174. {
  175. $queryBuilder = $this->createQuery($class, 'o');
  176. $objects = $queryBuilder
  177. ->select('o')
  178. ->add('where', $queryBuilder->expr()->in('o.id', $idx))
  179. ->getQuery()
  180. ->execute();
  181. foreach ($objects as $object) {
  182. $this->entityManager->remove($object);
  183. }
  184. $this->entityManager->flush();
  185. }
  186. /**
  187. * Returns a new model instance
  188. * @param string $class
  189. * @return
  190. */
  191. public function getModelInstance($class)
  192. {
  193. return new $class;
  194. }
  195. /**
  196. * Returns the parameters used in the columns header
  197. *
  198. * @param \Sonata\AdminBundle\Admin\FieldDescriptionInterface $fieldDescription
  199. * @param \Sonata\AdminBundle\Datagrid\DatagridInterface $datagrid
  200. * @return string
  201. */
  202. public function getSortParameters(FieldDescriptionInterface $fieldDescription, DatagridInterface $datagrid)
  203. {
  204. $values = $datagrid->getValues();
  205. if ($fieldDescription->getOption('sortable') == $values['_sort_by']) {
  206. if ($values['_sort_order'] == 'ASC') {
  207. $values['_sort_order'] = 'DESC';
  208. } else {
  209. $values['_sort_order'] = 'ASC';
  210. }
  211. } else {
  212. $values['_sort_order'] = 'ASC';
  213. $values['_sort_by'] = $fieldDescription->getOption('sortable');
  214. }
  215. return $values;
  216. }
  217. /**
  218. * @param sring $class
  219. * @return array
  220. */
  221. public function getDefaultSortValues($class)
  222. {
  223. return array(
  224. '_sort_order' => 'ASC',
  225. '_sort_by' => implode(',', $this->getModelIdentifier($class))
  226. );
  227. }
  228. /**
  229. * @param string $class
  230. * @param object $instance
  231. * @return void
  232. */
  233. function modelTransform($class, $instance)
  234. {
  235. return $instance;
  236. }
  237. /**
  238. * @param string $class
  239. * @param array $array
  240. * @return object
  241. */
  242. function modelReverseTransform($class, array $array = array())
  243. {
  244. $instance = $this->getModelInstance($class);
  245. $metadata = $this->getMetadata($class);
  246. $reflClass = $metadata->reflClass;
  247. foreach ($array as $name => $value) {
  248. $reflection_property = false;
  249. // property or association ?
  250. if (array_key_exists($name, $metadata->fieldMappings)) {
  251. $property = $metadata->fieldMappings[$name]['fieldName'];
  252. $reflection_property = $metadata->reflFields[$name];
  253. } else if (array_key_exists($name, $metadata->associationMappings)) {
  254. $property = $metadata->associationMappings[$name]['fieldName'];
  255. } else {
  256. $property = $name;
  257. }
  258. $setter = 'set'.$this->camelize($name);
  259. if ($reflClass->hasMethod($setter)) {
  260. if (!$reflClass->getMethod($setter)->isPublic()) {
  261. throw new PropertyAccessDeniedException(sprintf('Method "%s()" is not public in class "%s"', $setter, $reflClass->getName()));
  262. }
  263. $instance->$setter($value);
  264. } else if ($reflClass->hasMethod('__set')) {
  265. // needed to support magic method __set
  266. $instance->$property = $value;
  267. } else if ($reflClass->hasProperty($property)) {
  268. if (!$reflClass->getProperty($property)->isPublic()) {
  269. throw new PropertyAccessDeniedException(sprintf('Property "%s" is not public in class "%s". Maybe you should create the method "set%s()"?', $property, $reflClass->getName(), ucfirst($property)));
  270. }
  271. $instance->$property = $value;
  272. } else if ($reflection_property) {
  273. $reflection_property->setValue($instance, $value);
  274. }
  275. }
  276. return $instance;
  277. }
  278. /**
  279. * method taken from PropertyPath
  280. *
  281. * @param $property
  282. * @return mixed
  283. */
  284. protected function camelize($property)
  285. {
  286. return preg_replace(array('/(^|_)+(.)/e', '/\.(.)/e'), array("strtoupper('\\2')", "'_'.strtoupper('\\1')"), $property);
  287. }
  288. /**
  289. * @param string $class
  290. * @return \Doctrine\Common\Collections\ArrayCollection
  291. */
  292. public function getModelCollectionInstance($class)
  293. {
  294. return new \Doctrine\Common\Collections\ArrayCollection();
  295. }
  296. function collectionClear(&$collection)
  297. {
  298. return $collection->clear();
  299. }
  300. function collectionHasElement(&$collection, &$element)
  301. {
  302. return $collection->contains($element);
  303. }
  304. function collectionAddElement(&$collection, &$element)
  305. {
  306. return $collection->add($element);
  307. }
  308. function collectionRemoveElement(&$collection, &$element)
  309. {
  310. return $collection->removeElement($element);
  311. }
  312. }