ORM.php 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. <?php
  2. namespace Gedmo\Translatable\Mapping\Event\Adapter;
  3. use Doctrine\DBAL\Types\Type;
  4. use Doctrine\ORM\Proxy\Proxy;
  5. use Doctrine\ORM\Query;
  6. use Doctrine\ORM\Mapping\ClassMetadataInfo;
  7. use Gedmo\Mapping\Event\Adapter\ORM as BaseAdapterORM;
  8. use Gedmo\Translatable\Mapping\Event\TranslatableAdapter;
  9. use Gedmo\Tool\Wrapper\AbstractWrapper;
  10. /**
  11. * Doctrine event adapter for ORM adapted
  12. * for Translatable behavior
  13. *
  14. * @author Gediminas Morkevicius <gediminas.morkevicius@gmail.com>
  15. * @package Gedmo\Translatable\Mapping\Event\Adapter
  16. * @subpackage ORM
  17. * @link http://www.gediminasm.org
  18. * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
  19. */
  20. final class ORM extends BaseAdapterORM implements TranslatableAdapter
  21. {
  22. /**
  23. * {@inheritDoc}
  24. */
  25. public function usesPersonalTranslation($translationClassName)
  26. {
  27. return $this
  28. ->getObjectManager()
  29. ->getClassMetadata($translationClassName)
  30. ->getReflectionClass()
  31. ->isSubclassOf('Gedmo\Translatable\Entity\MappedSuperclass\AbstractPersonalTranslation')
  32. ;
  33. }
  34. /**
  35. * {@inheritDoc}
  36. */
  37. public function getDefaultTranslationClass()
  38. {
  39. return 'Gedmo\\Translatable\\Entity\\Translation';
  40. }
  41. /**
  42. * {@inheritDoc}
  43. */
  44. public function loadTranslations($object, $translationClass, $locale)
  45. {
  46. $em = $this->getObjectManager();
  47. $wrapped = AbstractWrapper::wrap($object, $em);
  48. $result = array();
  49. if ($this->usesPersonalTranslation($translationClass)) {
  50. // first try to load it using collection
  51. $found = false;
  52. foreach ($wrapped->getMetadata()->associationMappings as $assoc) {
  53. $isRightCollection = $assoc['targetEntity'] === $translationClass
  54. && $assoc['mappedBy'] === 'object'
  55. && $assoc['type'] === ClassMetadataInfo::ONE_TO_MANY
  56. ;
  57. if ($isRightCollection) {
  58. $collection = $wrapped->getPropertyValue($assoc['fieldName']);
  59. foreach ($collection as $trans) {
  60. if ($trans->getLocale() === $locale) {
  61. $result[] = array(
  62. 'field' => $trans->getField(),
  63. 'content' => $trans->getContent()
  64. );
  65. }
  66. }
  67. $found = true;
  68. break;
  69. }
  70. }
  71. // if collection is not set, fetch it through relation
  72. if (!$found) {
  73. $dql = 'SELECT t.content, t.field FROM ' . $translationClass . ' t';
  74. $dql .= ' WHERE t.locale = :locale';
  75. $dql .= ' AND t.object = :object';
  76. $q = $em->createQuery($dql);
  77. $q->setParameters(compact('object', 'locale'));
  78. $result = $q->getArrayResult();
  79. }
  80. } else {
  81. // load translated content for all translatable fields
  82. $objectId = $wrapped->getIdentifier();
  83. // construct query
  84. $dql = 'SELECT t.content, t.field FROM ' . $translationClass . ' t';
  85. $dql .= ' WHERE t.foreignKey = :objectId';
  86. $dql .= ' AND t.locale = :locale';
  87. $dql .= ' AND t.objectClass = :objectClass';
  88. // fetch results
  89. $objectClass = $wrapped->getMetadata()->name;
  90. $q = $em->createQuery($dql);
  91. $q->setParameters(compact('objectId', 'locale', 'objectClass'));
  92. $result = $q->getArrayResult();
  93. }
  94. return $result;
  95. }
  96. /**
  97. * {@inheritDoc}
  98. */
  99. public function findTranslation(AbstractWrapper $wrapped, $locale, $field, $translationClass)
  100. {
  101. $em = $this->getObjectManager();
  102. // first look in identityMap, will save one SELECT query
  103. foreach ($em->getUnitOfWork()->getIdentityMap() as $className => $objects) {
  104. if ($className === $translationClass) {
  105. foreach ($objects as $trans) {
  106. $isRequestedTranslation = !$trans instanceof Proxy
  107. && $trans->getLocale() === $locale
  108. && $trans->getField() === $field
  109. ;
  110. if ($isRequestedTranslation) {
  111. if ($this->usesPersonalTranslation($translationClass)) {
  112. $isRequestedTranslation = $trans->getObject() === $wrapped->getObject();
  113. } else {
  114. $isRequestedTranslation = $trans->getForeignKey() === $wrapped->getIdentifier()
  115. && $trans->getObjectClass() === $wrapped->getMetadata()->name
  116. ;
  117. }
  118. }
  119. if ($isRequestedTranslation) {
  120. return $trans;
  121. }
  122. }
  123. }
  124. }
  125. $qb = $em->createQueryBuilder();
  126. $qb->select('trans')
  127. ->from($translationClass, 'trans')
  128. ->where(
  129. 'trans.locale = :locale',
  130. 'trans.field = :field'
  131. )
  132. ;
  133. $qb->setParameters(compact('locale', 'field'));
  134. if ($this->usesPersonalTranslation($translationClass)) {
  135. $qb->andWhere('trans.object = :object');
  136. $qb->setParameter('object', $wrapped->getObject());
  137. } else {
  138. $qb->andWhere('trans.foreignKey = :objectId');
  139. $qb->andWhere('trans.objectClass = :objectClass');
  140. $qb->setParameter('objectId', $wrapped->getIdentifier());
  141. $qb->setParameter('objectClass', $wrapped->getMetadata()->name);
  142. }
  143. $q = $qb->getQuery();
  144. $q->setMaxResults(1);
  145. $result = $q->getResult();
  146. if ($result) {
  147. return array_shift($result);
  148. }
  149. return null;
  150. }
  151. /**
  152. * {@inheritDoc}
  153. */
  154. public function removeAssociatedTranslations(AbstractWrapper $wrapped, $transClass)
  155. {
  156. $qb = $this
  157. ->getObjectManager()
  158. ->createQueryBuilder()
  159. ->delete($transClass, 'trans')
  160. ;
  161. if ($this->usesPersonalTranslation($transClass)) {
  162. $qb->where('trans.object = :object');
  163. $qb->setParameter('object', $wrapped->getObject());
  164. } else {
  165. $qb->where(
  166. 'trans.foreignKey = :objectId',
  167. 'trans.objectClass = :class'
  168. );
  169. $qb->setParameter('objectId', $wrapped->getIdentifier());
  170. $qb->setParameter('class', $wrapped->getMetadata()->name);
  171. }
  172. return $qb->getQuery()->getSingleScalarResult();
  173. }
  174. /**
  175. * {@inheritDoc}
  176. */
  177. public function insertTranslationRecord($translation)
  178. {
  179. $em = $this->getObjectManager();
  180. $meta = $em->getClassMetadata(get_class($translation));
  181. $data = array();
  182. foreach ($meta->getReflectionProperties() as $fieldName => $reflProp) {
  183. if (!$meta->isIdentifier($fieldName)) {
  184. $data[$meta->getColumnName($fieldName)] = $reflProp->getValue($translation);
  185. }
  186. }
  187. $table = $meta->getTableName();
  188. if (!$em->getConnection()->insert($table, $data)) {
  189. throw new \Gedmo\Exception\RuntimeException('Failed to insert new Translation record');
  190. }
  191. }
  192. /**
  193. * {@inheritDoc}
  194. */
  195. public function getTranslationValue($object, $field, $value = false)
  196. {
  197. $em = $this->getObjectManager();
  198. $wrapped = AbstractWrapper::wrap($object, $em);
  199. $meta = $wrapped->getMetadata();
  200. $type = Type::getType($meta->getTypeOfField($field));
  201. if ($value === false) {
  202. $value = $wrapped->getPropertyValue($field);
  203. }
  204. return $type->convertToDatabaseValue($value, $em->getConnection()->getDatabasePlatform());
  205. }
  206. /**
  207. * {@inheritDoc}
  208. */
  209. public function setTranslationValue($object, $field, $value)
  210. {
  211. $em = $this->getObjectManager();
  212. $wrapped = AbstractWrapper::wrap($object, $em);
  213. $meta = $wrapped->getMetadata();
  214. $type = Type::getType($meta->getTypeOfField($field));
  215. $value = $type->convertToPHPValue($value, $em->getConnection()->getDatabasePlatform());
  216. $wrapped->setPropertyValue($field, $value);
  217. }
  218. }