protected function saveManyToMany(array $relationship, $alias, Collection $collection, EntityMetadata $childMetadata, $ourValue, $ourField, $theirField)
 {
     if (!empty($this->relationshipCascade[$alias])) {
         /** @var RepositoryInterface $childRepo */
         $childRepo = $this->relationshipCascade[$alias];
         // save all entities currently in the collection
         foreach ($collection->getAddedEntities() as $child) {
             $childRepo->save($child);
         }
     }
     // We will be doing operations on an intermediary join table. As Reposition works with EntityMetadata, we
     // need to create the metadata for the intermediary
     $intermediaryMetadata = $this->metadataProvider->getEntityMetadataForIntermediary($relationship[EntityMetadata::METADATA_RELATIONSHIP_JOIN_TABLE]);
     // generate the relationship metadata used on this collection
     $intermediaryRelationship = [EntityMetadata::METADATA_RELATIONSHIP_TYPE => EntityMetadata::RELATIONSHIP_TYPE_MANY_TO_ONE];
     $manyToManyRelationships = ["ours" => $this->entityMetadata->getCollection() . "_" . $ourField, "theirs" => $childMetadata->getCollection() . "_" . $theirField];
     foreach ($manyToManyRelationships as $relationshipAlias => $intermediaryOurField) {
         $intermediaryRelationship[EntityMetadata::METADATA_RELATIONSHIP_OUR_FIELD] = $intermediaryOurField;
         $intermediaryRelationship[EntityMetadata::METADATA_RELATIONSHIP_ALIAS] = $relationshipAlias;
         $intermediaryMetadata->addRelationshipMetadata($this->entityMetadata->getEntity(), $intermediaryRelationship);
     }
     // process added and removed children
     $added = $collection->getAddedEntities();
     $removed = $collection->getRemovedEntities();
     if (!empty($added)) {
         $insert = $this->queryBuilder->save($intermediaryMetadata, ["saveType" => "insert"]);
         // create an array of values to use, for each entity we're adding
         foreach ($added as $child) {
             // we have to use the actual field names here as we're saving an array, rather than a class instance
             $entityArray = [$manyToManyRelationships["ours"] => $ourValue, $manyToManyRelationships["theirs"] => $childMetadata->getEntityValue($child, $theirField)];
             $insert->entity($entityArray);
         }
         $this->doQuery($insert, ["output" => "raw"]);
     }
     if (!empty($removed)) {
         $delete = $this->queryBuilder->delete($intermediaryMetadata);
         // delete rows where the parent value is X and the child value is in the list
         $filters = ["ours" => $ourValue, "theirs" => $this->condition("in", $removed)];
         $this->createWhereFromFilters($delete, $filters);
         $this->doQuery($delete, ["output" => "raw"]);
     }
 }
Example #2
0
 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;
 }