SimpleObjectHydrator.php 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. <?php
  2. /*
  3. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  4. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  5. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  6. * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  7. * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  8. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  9. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  10. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  11. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  12. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  13. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  14. *
  15. * This software consists of voluntary contributions made by many individuals
  16. * and is licensed under the LGPL. For more information, see
  17. * <http://www.doctrine-project.org>.
  18. */
  19. namespace Doctrine\ORM\Internal\Hydration;
  20. use \PDO,
  21. Doctrine\DBAL\Types\Type,
  22. Doctrine\ORM\Mapping\ClassMetadata,
  23. Doctrine\ORM\Event\LifecycleEventArgs,
  24. Doctrine\ORM\Events,
  25. Doctrine\ORM\Query;
  26. class SimpleObjectHydrator extends AbstractHydrator
  27. {
  28. /**
  29. * @var ClassMetadata
  30. */
  31. private $class;
  32. /**
  33. * @var array
  34. */
  35. private $declaringClasses = array();
  36. /**
  37. * {@inheritdoc}
  38. */
  39. protected function hydrateAllData()
  40. {
  41. $result = array();
  42. $cache = array();
  43. while ($row = $this->_stmt->fetch(PDO::FETCH_ASSOC)) {
  44. $this->hydrateRowData($row, $cache, $result);
  45. }
  46. $this->_em->getUnitOfWork()->triggerEagerLoads();
  47. return $result;
  48. }
  49. /**
  50. * {@inheritdoc}
  51. */
  52. protected function prepare()
  53. {
  54. if (count($this->_rsm->aliasMap) !== 1) {
  55. throw new \RuntimeException("Cannot use SimpleObjectHydrator with a ResultSetMapping that contains more than one object result.");
  56. }
  57. if ($this->_rsm->scalarMappings) {
  58. throw new \RuntimeException("Cannot use SimpleObjectHydrator with a ResultSetMapping that contains scalar mappings.");
  59. }
  60. $this->class = $this->_em->getClassMetadata(reset($this->_rsm->aliasMap));
  61. // We only need to add declaring classes if we have inheritance.
  62. if ($this->class->inheritanceType === ClassMetadata::INHERITANCE_TYPE_NONE) {
  63. return;
  64. }
  65. foreach ($this->_rsm->declaringClasses AS $column => $class) {
  66. $this->declaringClasses[$column] = $this->_em->getClassMetadata($class);
  67. }
  68. }
  69. /**
  70. * {@inheritdoc}
  71. */
  72. protected function hydrateRowData(array $sqlResult, array &$cache, array &$result)
  73. {
  74. $entityName = $this->class->name;
  75. $data = array();
  76. // We need to find the correct entity class name if we have inheritance in resultset
  77. if ($this->class->inheritanceType !== ClassMetadata::INHERITANCE_TYPE_NONE) {
  78. $discrColumnName = $this->_platform->getSQLResultCasing($this->class->discriminatorColumn['name']);
  79. if ($sqlResult[$discrColumnName] === '') {
  80. throw HydrationException::emptyDiscriminatorValue(key($this->_rsm->aliasMap));
  81. }
  82. $entityName = $this->class->discriminatorMap[$sqlResult[$discrColumnName]];
  83. unset($sqlResult[$discrColumnName]);
  84. }
  85. foreach ($sqlResult as $column => $value) {
  86. // Hydrate column information if not yet present
  87. if ( ! isset($cache[$column])) {
  88. if (($info = $this->hydrateColumnInfo($entityName, $column)) === null) {
  89. continue;
  90. }
  91. $cache[$column] = $info;
  92. }
  93. // Convert field to a valid PHP value
  94. if (isset($cache[$column]['field'])) {
  95. $type = Type::getType($cache[$column]['class']->fieldMappings[$cache[$column]['name']]['type']);
  96. $value = $type->convertToPHPValue($value, $this->_platform);
  97. }
  98. // Prevent overwrite in case of inherit classes using same property name (See AbstractHydrator)
  99. if (isset($cache[$column]) && ( ! isset($data[$cache[$column]['name']]) || $value !== null)) {
  100. $data[$cache[$column]['name']] = $value;
  101. }
  102. }
  103. if (isset($this->_hints[Query::HINT_REFRESH_ENTITY])) {
  104. $this->registerManaged($this->class, $this->_hints[Query::HINT_REFRESH_ENTITY], $data);
  105. }
  106. $uow = $this->_em->getUnitOfWork();
  107. $entity = $uow->createEntity($entityName, $data, $this->_hints);
  108. $result[] = $entity;
  109. }
  110. /**
  111. * Retrieve column information form ResultSetMapping.
  112. *
  113. * @param string $entityName
  114. * @param string $column
  115. *
  116. * @return array
  117. */
  118. protected function hydrateColumnInfo($entityName, $column)
  119. {
  120. switch (true) {
  121. case (isset($this->_rsm->fieldMappings[$column])):
  122. $class = isset($this->declaringClasses[$column])
  123. ? $this->declaringClasses[$column]
  124. : $this->class;
  125. // If class is not part of the inheritance, ignore
  126. if ( ! ($class->name === $entityName || is_subclass_of($entityName, $class->name))) {
  127. return null;
  128. }
  129. return array(
  130. 'class' => $class,
  131. 'name' => $this->_rsm->fieldMappings[$column],
  132. 'field' => true,
  133. );
  134. case (isset($this->_rsm->relationMap[$column])):
  135. $class = isset($this->_rsm->relationMap[$column])
  136. ? $this->_rsm->relationMap[$column]
  137. : $this->class;
  138. // If class is not self referencing, ignore
  139. if ( ! ($class === $entityName || is_subclass_of($entityName, $class))) {
  140. return null;
  141. }
  142. // TODO: Decide what to do with associations. It seems original code is incomplete.
  143. // One solution is to load the association, but it might require extra efforts.
  144. return array('name' => $column);
  145. default:
  146. return array(
  147. 'name' => $this->_rsm->metaMappings[$column]
  148. );
  149. }
  150. }
  151. }