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.
15 * This software consists of voluntary contributions made by many individuals
16 * and is licensed under the MIT license. For more information, see
17 * <http://www.doctrine-project.org>.
20 namespace Doctrine\ORM\Persisters;
22 use Doctrine\ORM\PersistentCollection,
23 Doctrine\ORM\UnitOfWork;
26 * Persister for one-to-many collections.
28 * @author Roman Borschel <roman@code-factory.org>
29 * @author Guilherme Blanco <guilhermeblanco@hotmail.com>
30 * @author Alexander <iam.asm89@gmail.com>
33 class OneToManyPersister extends AbstractCollectionPersister
36 * Generates the SQL UPDATE that updates a particular row's foreign
39 * @param PersistentCollection $coll
43 protected function _getDeleteRowSQL(PersistentCollection $coll)
45 $mapping = $coll->getMapping();
46 $class = $this->_em->getClassMetadata($mapping['targetEntity']);
48 return 'DELETE FROM ' . $this->quoteStrategy->getTableName($class, $this->platform)
49 . ' WHERE ' . implode('= ? AND ', $class->getIdentifierColumnNames()) . ' = ?';
56 protected function _getDeleteRowSQLParameters(PersistentCollection $coll, $element)
58 return array_values($this->_uow->getEntityIdentifier($element));
61 protected function _getInsertRowSQL(PersistentCollection $coll)
63 return "UPDATE xxx SET foreign_key = yyy WHERE foreign_key = zzz";
67 * Gets the SQL parameters for the corresponding SQL statement to insert the given
68 * element of the given collection into the database.
70 * @param PersistentCollection $coll
71 * @param mixed $element
73 protected function _getInsertRowSQLParameters(PersistentCollection $coll, $element)
76 /* Not used for OneToManyPersister */
77 protected function _getUpdateRowSQL(PersistentCollection $coll)
83 * Generates the SQL UPDATE that updates all the foreign keys to null.
85 * @param PersistentCollection $coll
87 protected function _getDeleteSQL(PersistentCollection $coll)
93 * Gets the SQL parameters for the corresponding SQL statement to delete
94 * the given collection.
96 * @param PersistentCollection $coll
98 protected function _getDeleteSQLParameters(PersistentCollection $coll)
104 public function count(PersistentCollection $coll)
106 $mapping = $coll->getMapping();
107 $targetClass = $this->_em->getClassMetadata($mapping['targetEntity']);
108 $sourceClass = $this->_em->getClassMetadata($mapping['sourceEntity']);
109 $id = $this->_em->getUnitOfWork()->getEntityIdentifier($coll->getOwner());
111 $whereClauses = array();
114 foreach ($targetClass->associationMappings[$mapping['mappedBy']]['joinColumns'] as $joinColumn) {
115 $whereClauses[] = $joinColumn['name'] . ' = ?';
117 $params[] = ($targetClass->containsForeignIdentifier)
118 ? $id[$sourceClass->getFieldForColumn($joinColumn['referencedColumnName'])]
119 : $id[$sourceClass->fieldNames[$joinColumn['referencedColumnName']]];
122 $filterTargetClass = $this->_em->getClassMetadata($targetClass->rootEntityName);
123 foreach ($this->_em->getFilters()->getEnabledFilters() as $filter) {
124 if ($filterExpr = $filter->addFilterConstraint($filterTargetClass, 't')) {
125 $whereClauses[] = '(' . $filterExpr . ')';
129 $sql = 'SELECT count(*)'
130 . ' FROM ' . $this->quoteStrategy->getTableName($targetClass, $this->platform) . ' t'
131 . ' WHERE ' . implode(' AND ', $whereClauses);
133 return $this->_conn->fetchColumn($sql, $params);
137 * @param PersistentCollection $coll
140 * @return \Doctrine\Common\Collections\ArrayCollection
142 public function slice(PersistentCollection $coll, $offset, $length = null)
144 $mapping = $coll->getMapping();
145 $uow = $this->_em->getUnitOfWork();
146 $persister = $uow->getEntityPersister($mapping['targetEntity']);
148 return $persister->getOneToManyCollection($mapping, $coll->getOwner(), $offset, $length);
152 * @param PersistentCollection $coll
153 * @param object $element
156 public function contains(PersistentCollection $coll, $element)
158 $mapping = $coll->getMapping();
159 $uow = $this->_em->getUnitOfWork();
161 // shortcut for new entities
162 $entityState = $uow->getEntityState($element, UnitOfWork::STATE_NEW);
164 if ($entityState === UnitOfWork::STATE_NEW) {
168 // Entity is scheduled for inclusion
169 if ($entityState === UnitOfWork::STATE_MANAGED && $uow->isScheduledForInsert($element)) {
173 $persister = $uow->getEntityPersister($mapping['targetEntity']);
175 // only works with single id identifier entities. Will throw an
176 // exception in Entity Persisters if that is not the case for the
178 $id = current( $uow->getEntityIdentifier($coll->getOwner()));
180 return $persister->exists($element, array($mapping['mappedBy'] => $id));
184 * @param PersistentCollection $coll
185 * @param object $element
188 public function removeElement(PersistentCollection $coll, $element)
190 $uow = $this->_em->getUnitOfWork();
192 // shortcut for new entities
193 $entityState = $uow->getEntityState($element, UnitOfWork::STATE_NEW);
195 if ($entityState === UnitOfWork::STATE_NEW) {
199 // If Entity is scheduled for inclusion, it is not in this collection.
200 // We can assure that because it would have return true before on array check
201 if ($entityState === UnitOfWork::STATE_MANAGED && $uow->isScheduledForInsert($element)) {
205 $mapping = $coll->getMapping();
206 $class = $this->_em->getClassMetadata($mapping['targetEntity']);
207 $sql = 'DELETE FROM ' . $this->quoteStrategy->getTableName($class, $this->platform)
208 . ' WHERE ' . implode('= ? AND ', $class->getIdentifierColumnNames()) . ' = ?';
210 return (bool) $this->_conn->executeUpdate($sql, $this->_getDeleteRowSQLParameters($coll, $element));