OneToManyPersister.php 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  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\Persisters;
  20. use Doctrine\ORM\PersistentCollection,
  21. Doctrine\ORM\UnitOfWork;
  22. /**
  23. * Persister for one-to-many collections.
  24. *
  25. * @author Roman Borschel <roman@code-factory.org>
  26. * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
  27. * @author Alexander <iam.asm89@gmail.com>
  28. * @since 2.0
  29. */
  30. class OneToManyPersister extends AbstractCollectionPersister
  31. {
  32. /**
  33. * Generates the SQL UPDATE that updates a particular row's foreign
  34. * key to null.
  35. *
  36. * @param PersistentCollection $coll
  37. * @return string
  38. * @override
  39. */
  40. protected function _getDeleteRowSQL(PersistentCollection $coll)
  41. {
  42. $mapping = $coll->getMapping();
  43. $class = $this->_em->getClassMetadata($mapping['targetEntity']);
  44. return 'DELETE FROM ' . $class->getQuotedTableName($this->_conn->getDatabasePlatform())
  45. . ' WHERE ' . implode('= ? AND ', $class->getIdentifierColumnNames()) . ' = ?';
  46. }
  47. /**
  48. * {@inheritdoc}
  49. *
  50. */
  51. protected function _getDeleteRowSQLParameters(PersistentCollection $coll, $element)
  52. {
  53. return array_values($this->_uow->getEntityIdentifier($element));
  54. }
  55. protected function _getInsertRowSQL(PersistentCollection $coll)
  56. {
  57. return "UPDATE xxx SET foreign_key = yyy WHERE foreign_key = zzz";
  58. }
  59. /**
  60. * Gets the SQL parameters for the corresponding SQL statement to insert the given
  61. * element of the given collection into the database.
  62. *
  63. * @param PersistentCollection $coll
  64. * @param mixed $element
  65. */
  66. protected function _getInsertRowSQLParameters(PersistentCollection $coll, $element)
  67. {}
  68. /* Not used for OneToManyPersister */
  69. protected function _getUpdateRowSQL(PersistentCollection $coll)
  70. {
  71. return;
  72. }
  73. /**
  74. * Generates the SQL UPDATE that updates all the foreign keys to null.
  75. *
  76. * @param PersistentCollection $coll
  77. */
  78. protected function _getDeleteSQL(PersistentCollection $coll)
  79. {
  80. }
  81. /**
  82. * Gets the SQL parameters for the corresponding SQL statement to delete
  83. * the given collection.
  84. *
  85. * @param PersistentCollection $coll
  86. */
  87. protected function _getDeleteSQLParameters(PersistentCollection $coll)
  88. {}
  89. /**
  90. * {@inheritdoc}
  91. */
  92. public function count(PersistentCollection $coll)
  93. {
  94. $mapping = $coll->getMapping();
  95. $targetClass = $this->_em->getClassMetadata($mapping['targetEntity']);
  96. $sourceClass = $this->_em->getClassMetadata($mapping['sourceEntity']);
  97. $id = $this->_em->getUnitOfWork()->getEntityIdentifier($coll->getOwner());
  98. $whereClauses = array();
  99. $params = array();
  100. foreach ($targetClass->associationMappings[$mapping['mappedBy']]['joinColumns'] AS $joinColumn) {
  101. $whereClauses[] = $joinColumn['name'] . ' = ?';
  102. $params[] = ($targetClass->containsForeignIdentifier)
  103. ? $id[$sourceClass->getFieldForColumn($joinColumn['referencedColumnName'])]
  104. : $id[$sourceClass->fieldNames[$joinColumn['referencedColumnName']]];
  105. }
  106. $filterTargetClass = $this->_em->getClassMetadata($targetClass->rootEntityName);
  107. foreach ($this->_em->getFilters()->getEnabledFilters() as $filter) {
  108. if ($filterExpr = $filter->addFilterConstraint($filterTargetClass, 't')) {
  109. $whereClauses[] = '(' . $filterExpr . ')';
  110. }
  111. }
  112. $sql = 'SELECT count(*)'
  113. . ' FROM ' . $targetClass->getQuotedTableName($this->_conn->getDatabasePlatform()) . ' t'
  114. . ' WHERE ' . implode(' AND ', $whereClauses);
  115. return $this->_conn->fetchColumn($sql, $params);
  116. }
  117. /**
  118. * @param PersistentCollection $coll
  119. * @param int $offset
  120. * @param int $length
  121. * @return \Doctrine\Common\Collections\ArrayCollection
  122. */
  123. public function slice(PersistentCollection $coll, $offset, $length = null)
  124. {
  125. $mapping = $coll->getMapping();
  126. $uow = $this->_em->getUnitOfWork();
  127. $persister = $uow->getEntityPersister($mapping['targetEntity']);
  128. return $persister->getOneToManyCollection($mapping, $coll->getOwner(), $offset, $length);
  129. }
  130. /**
  131. * @param PersistentCollection $coll
  132. * @param object $element
  133. * @return boolean
  134. */
  135. public function contains(PersistentCollection $coll, $element)
  136. {
  137. $mapping = $coll->getMapping();
  138. $uow = $this->_em->getUnitOfWork();
  139. // shortcut for new entities
  140. $entityState = $uow->getEntityState($element, UnitOfWork::STATE_NEW);
  141. if ($entityState === UnitOfWork::STATE_NEW) {
  142. return false;
  143. }
  144. // Entity is scheduled for inclusion
  145. if ($entityState === UnitOfWork::STATE_MANAGED && $uow->isScheduledForInsert($element)) {
  146. return false;
  147. }
  148. $persister = $uow->getEntityPersister($mapping['targetEntity']);
  149. // only works with single id identifier entities. Will throw an
  150. // exception in Entity Persisters if that is not the case for the
  151. // 'mappedBy' field.
  152. $id = current( $uow->getEntityIdentifier($coll->getOwner()));
  153. return $persister->exists($element, array($mapping['mappedBy'] => $id));
  154. }
  155. /**
  156. * @param PersistentCollection $coll
  157. * @param object $element
  158. * @return boolean
  159. */
  160. public function removeElement(PersistentCollection $coll, $element)
  161. {
  162. $uow = $this->_em->getUnitOfWork();
  163. // shortcut for new entities
  164. $entityState = $uow->getEntityState($element, UnitOfWork::STATE_NEW);
  165. if ($entityState === UnitOfWork::STATE_NEW) {
  166. return false;
  167. }
  168. // If Entity is scheduled for inclusion, it is not in this collection.
  169. // We can assure that because it would have return true before on array check
  170. if ($entityState === UnitOfWork::STATE_MANAGED && $uow->isScheduledForInsert($element)) {
  171. return false;
  172. }
  173. $mapping = $coll->getMapping();
  174. $class = $this->_em->getClassMetadata($mapping['targetEntity']);
  175. $sql = 'DELETE FROM ' . $class->getQuotedTableName($this->_conn->getDatabasePlatform())
  176. . ' WHERE ' . implode('= ? AND ', $class->getIdentifierColumnNames()) . ' = ?';
  177. return (bool) $this->_conn->executeUpdate($sql, $this->_getDeleteRowSQLParameters($coll, $element));
  178. }
  179. }