PageRenderTime 39ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/library/vendors/doctrine/odm/lib/Doctrine/ODM/MongoDB/Persisters/CollectionPersister.php

https://bitbucket.org/paulscott56/c4-framework
PHP | 247 lines | 118 code | 14 blank | 115 comment | 14 complexity | 9e048e4d85a708f13e917687572b59df MD5 | raw file
  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\ODM\MongoDB\Persisters;
  20. use Doctrine\ODM\MongoDB\PersistentCollection,
  21. Doctrine\ODM\MongoDB\DocumentManager,
  22. Doctrine\ODM\MongoDB\Persisters\PersistenceBuilder,
  23. Doctrine\ODM\MongoDB\UnitOfWork,
  24. Doctrine\ODM\MongoDB\Mapping\ClassMetadata;
  25. /**
  26. * The CollectionPersister is responsible for persisting collections of embedded documents
  27. * or referenced documents. When a PersistentCollection is scheduledForDeletion in the UnitOfWork
  28. * by calling PersistentCollection::clear() or is de-referenced in the domain application
  29. * code it results in a CollectionPersister::delete(). When a single document is removed
  30. * from a PersitentCollection it is removed in the call to CollectionPersister::deleteRows()
  31. * and new documents added to the PersistentCollection are inserted in the call to
  32. * CollectionPersister::insertRows().
  33. *
  34. * @license http://www.opensource.org/licenses/lgpl-license.php LGPL
  35. * @link www.doctrine-project.com
  36. * @since 1.0
  37. * @author Jonathan H. Wage <jonwage@gmail.com>
  38. * @author Bulat Shakirzyanov <bulat@theopenskyproject.com>
  39. * @author Roman Borschel <roman@code-factory.org>
  40. */
  41. class CollectionPersister
  42. {
  43. /**
  44. * The DocumentManager instance.
  45. *
  46. * @var Doctrine\ODM\MongoDB\DocumentManager
  47. */
  48. private $dm;
  49. /**
  50. * The PersistenceBuilder instance.
  51. *
  52. * @var Doctrine\ODM\MongoDB\Persisters\PersistenceBuilder
  53. */
  54. private $pb;
  55. /**
  56. * Mongo command prefix
  57. *
  58. * @var string
  59. */
  60. private $cmd;
  61. /**
  62. * Contructs a new CollectionPersister instance.
  63. *
  64. * @param DocumentManager $dm
  65. * @param PersistenceBuilder $pb
  66. * @param UnitOfWork $uow
  67. * @param string $cmd
  68. */
  69. public function __construct(DocumentManager $dm, PersistenceBuilder $pb, UnitOfWork $uow, $cmd)
  70. {
  71. $this->dm = $dm;
  72. $this->pb = $pb;
  73. $this->uow = $uow;
  74. $this->cmd = $cmd;
  75. }
  76. /**
  77. * Deletes a PersistentCollection instance completely from a document using $unset.
  78. *
  79. * @param PersistentCollection $coll
  80. * @param array $options
  81. */
  82. public function delete(PersistentCollection $coll, array $options)
  83. {
  84. $mapping = $coll->getMapping();
  85. if ($mapping['isInverseSide']) {
  86. return; // ignore inverse side
  87. }
  88. list($propertyPath, $parent) = $this->getPathAndParent($coll);
  89. $query = array($this->cmd . 'unset' => array($propertyPath => true));
  90. $this->executeQuery($parent, $query, $options);
  91. }
  92. /**
  93. * Updates a PersistentCollection instance deleting removed rows and inserting new rows.
  94. *
  95. * @param PersistentCollection $coll
  96. * @param array $options
  97. */
  98. public function update(PersistentCollection $coll, array $options)
  99. {
  100. $mapping = $coll->getMapping();
  101. if ($mapping['isInverseSide']) {
  102. return; // ignore inverse side
  103. }
  104. $this->deleteRows($coll, $options);
  105. $this->insertRows($coll, $options);
  106. }
  107. /**
  108. * Deletes removed rows from a PersistentCollection instance.
  109. *
  110. * @param PersistentCollection $coll
  111. * @param array $options
  112. */
  113. private function deleteRows(PersistentCollection $coll, array $options)
  114. {
  115. $deleteDiff = $coll->getDeleteDiff();
  116. if ($deleteDiff) {
  117. list($propertyPath, $parent) = $this->getPathAndParent($coll);
  118. $query = array($this->cmd.'unset' => array());
  119. foreach ($deleteDiff as $key => $document) {
  120. $query[$this->cmd.'unset'][$propertyPath.'.'.$key] = true;
  121. }
  122. $this->executeQuery($parent, $query, $options);
  123. /**
  124. * @todo This is a hack right now because we don't have a proper way to remove
  125. * an element from an array by its key. Unsetting the key results in the element
  126. * being left in the array as null so we have to pull null values.
  127. *
  128. * "Using "$unset" with an expression like this "array.$" will result in the array item becoming null, not being removed. You can issue an update with "{$pull:{x:null}}" to remove all nulls."
  129. * http://www.mongodb.org/display/DOCS/Updating#Updating-%24unset
  130. */
  131. $mapping = $coll->getMapping();
  132. if ($mapping['strategy'] !== 'set') {
  133. $this->executeQuery($parent, array($this->cmd.'pull' => array($propertyPath => null)), $options);
  134. }
  135. }
  136. }
  137. /**
  138. * Inserts new rows for a PersistentCollection instance.
  139. *
  140. * @param PersistentCollection $coll
  141. * @param array $options
  142. */
  143. private function insertRows(PersistentCollection $coll, array $options)
  144. {
  145. $mapping = $coll->getMapping();
  146. list($propertyPath, $parent) = $this->getPathAndParent($coll);
  147. if ($mapping['strategy'] === 'set') {
  148. $setData = array();
  149. foreach ($coll as $key => $document) {
  150. if (isset($mapping['reference'])) {
  151. $setData[$key] = $this->pb->prepareReferencedDocumentValue($mapping, $document);
  152. } else {
  153. $setData[$key] = $this->pb->prepareEmbeddedDocumentValue($mapping, $document);
  154. }
  155. }
  156. $query = array($this->cmd.'set' => array($propertyPath => $setData));
  157. $this->executeQuery($parent, $query, $options);
  158. } else {
  159. $strategy = isset($mapping['strategy']) ? $mapping['strategy'] : 'pushAll';
  160. $insertDiff = $coll->getInsertDiff();
  161. if ($insertDiff) {
  162. $query = array($this->cmd.$strategy => array());
  163. foreach ($insertDiff as $document) {
  164. if (isset($mapping['reference'])) {
  165. $query[$this->cmd.$strategy][$propertyPath][] = $this->pb->prepareReferencedDocumentValue($mapping, $document);
  166. } else {
  167. $query[$this->cmd.$strategy][$propertyPath][] = $this->pb->prepareEmbeddedDocumentValue($mapping, $document);
  168. }
  169. }
  170. $this->executeQuery($parent, $query, $options);
  171. }
  172. }
  173. }
  174. /**
  175. * Gets the document database identifier value for the given document.
  176. *
  177. * @param object $document
  178. * @param ClassMetadata $class
  179. * @return mixed $id
  180. */
  181. private function getDocumentId($document, ClassMetadata $class)
  182. {
  183. return $class->getDatabaseIdentifierValue($this->uow->getDocumentIdentifier($document));
  184. }
  185. /**
  186. * Gets the parent information for a given PersistentCollection. It will retrieve the top
  187. * level persistent @Document that the PersistentCollection lives in. We can use this to issue
  188. * queries when updating a PersistentCollection that is multiple levels deep inside an
  189. * embedded document.
  190. *
  191. * <code>
  192. * list($path, $parent) = $this->getPathAndParent($coll)
  193. * </code>
  194. *
  195. * @param PersistentCollection $coll
  196. * @return array $pathAndParent
  197. */
  198. private function getPathAndParent(PersistentCollection $coll)
  199. {
  200. $mapping = $coll->getMapping();
  201. $fields = array();
  202. $parent = $coll->getOwner();
  203. while (null !== ($association = $this->uow->getParentAssociation($parent))) {
  204. list($m, $owner, $field) = $association;
  205. if (isset($m['reference'])) {
  206. break;
  207. }
  208. $parent = $owner;
  209. $fields[] = $field;
  210. }
  211. $propertyPath = implode('.', array_reverse($fields));
  212. $path = $mapping['name'];
  213. if ($propertyPath) {
  214. $path = $propertyPath.'.'.$path;
  215. }
  216. return array($path, $parent);
  217. }
  218. /**
  219. * Executes a query updating the given document.
  220. *
  221. * @param object $document
  222. * @param array $query
  223. * @param array $options
  224. */
  225. private function executeQuery($document, array $query, array $options)
  226. {
  227. $className = get_class($document);
  228. $class = $this->dm->getClassMetadata($className);
  229. $id = $class->getDatabaseIdentifierValue($this->uow->getDocumentIdentifier($document));
  230. $collection = $this->dm->getDocumentCollection($className);
  231. $collection->update(array('_id' => $id), $query, $options);
  232. }
  233. }