protected function saveOneToMany($alias, Collection $collection, EntityMetadata $childMetadata, $ourValue, $theirField) { $childPk = $childMetadata->getPrimaryKey(); if (!empty($this->relationshipCascade[$alias])) { // cascading relationships must deal with each entity individually /** @var RepositoryInterface $childRepo */ $childRepo = $this->relationshipCascade[$alias]; // save all entities currently in the collection foreach ($collection as $child) { $childRepo->save($child); } $removed = $collection->getRemovedEntities(); if (!empty($removed)) { // delete any that have been removed $delete = $this->queryBuilder->delete($childMetadata); $this->createWhereFromFilters($delete, [$childPk => $this->condition("in", $removed)]); $this->doQuery($delete, ["output" => "raw"]); } } // For non cascading operations, both adding and removing are updates; one sets the parent value on the child, // the other removes any existing values. // The two are so similar we can abstract the differences to the following array: $updates = [["entities" => $collection->toArray(false), "value" => $ourValue], ["entities" => $collection->getRemovedEntities(), "value" => null]]; foreach ($updates as $update) { // obviously, we don't have to do anything if there are no entities to process if (empty($update["entities"])) { continue; } $query = $this->queryBuilder->update($childMetadata)->ref($theirField)->op("=")->val($update["value"]); $this->createWhereFromFilters($query, [$childPk => $this->condition("in", $update["entities"])]); $this->doQuery($query, ["output" => "raw"]); } }
public function includeEntity(EntityMetadata $childMetadata, $collectionAlias = "", $parent = "", TokenSequencerInterface $additionalFilters = null) { // check we're dealing with a query if (!$this->isQuery()) { throw new TokenParseException("Cannot include an entity on an expression sequence."); } // get data on the child entity $childEntity = $childMetadata->getEntity(); $childCollection = $childMetadata->getCollection(); $childAlias = empty($collectionAlias) ? $childCollection : $collectionAlias; // determine which entity to join on (the parent) $parentMetadata = $this->getEntityMetadata(); if (!empty($parent)) { if (empty($this->includes[$parent])) { throw new TokenParseException("Cannot include the entity '{$childEntity}'. The parent entity '{$parent}' has not yet been included"); } /** @var EntityMetadata $parentMetadata */ $parentMetadata = $this->includes[$parent]; if ($parent == $parentMetadata->getEntity()) { $parent = $parentMetadata->getCollection(); } } // make sure we have a parent that we can use as a table name or alias if (empty($parent) || $parent == $parentMetadata->getEntity()) { $parent = $parentMetadata->getCollection(); } // get the relationship between the child and parent $relationshipAlias = empty($collectionAlias) ? $childEntity : $collectionAlias; $relationship = $parentMetadata->getRelationship($relationshipAlias); if (empty($relationship)) { throw new TokenParseException("The parent entity '{$parentMetadata->getEntity()}' has no relationship defined for '{$relationshipAlias}'"); } // find the fields we will need to use in the join condition // we need the primary key and/or the parent field defined in the relationship $parentKey = $parentMetadata->getPrimaryKey(); $parentField = $parent . "." . (empty($relationship[EntityMetadata::METADATA_RELATIONSHIP_OUR_FIELD]) ? $parentKey : $relationship[EntityMetadata::METADATA_RELATIONSHIP_OUR_FIELD]); // we need the primary key and/or the child field defined in the relationship $childKey = $childMetadata->getPrimaryKey(); $childField = $childAlias . "." . (empty($relationship[EntityMetadata::METADATA_RELATIONSHIP_THEIR_FIELD]) ? $childKey : $relationship[EntityMetadata::METADATA_RELATIONSHIP_THEIR_FIELD]); // many to many relationships require an extra join, so treat them differently if ($relationship[EntityMetadata::METADATA_RELATIONSHIP_TYPE] != EntityMetadata::RELATIONSHIP_TYPE_MANY_TO_MANY) { // create the join condition $onClause = new TokenSequencer($this->tokenFactory); $onClause->ref($parentField)->op("=")->ref($childField); // if we have additional join filters, add them now if (!empty($additionalFilters)) { $onClause->andL()->mergeSequence($additionalFilters->getSequence()); } // create the join $this->join($childCollection, $onClause, $collectionAlias); } else { // many to many. Requires intermediary join table $joinTable = $relationship[EntityMetadata::METADATA_RELATIONSHIP_JOIN_TABLE]; // create the join condition from the parent to the intermediate $parentToMany = new TokenSequencer($this->tokenFactory); $parentToMany->ref($parentField)->op("=")->ref("{$joinTable}.{$parent}_id"); // create the join condition from the intermediate to the child, including any additional join filters $manyToChild = new TokenSequencer($this->tokenFactory); $manyToChild->ref("{$joinTable}.{$childAlias}_id")->op("=")->ref($childField); if (!empty($additionalFilters)) { $manyToChild->andL()->mergeSequence($additionalFilters->getSequence()); } // create the joins $this->join($joinTable, $parentToMany)->join($childCollection, $manyToChild, $collectionAlias); } // add the include $this->includes[$childAlias] = $childMetadata; return $this; }