public function interpretQuery(TokenSequencerInterface $query) { $this->reset(); $this->query = $query; $sql = "DELETE FROM " . $this->renderArbitraryReference($query->getEntityMetadata()->getCollection()); while ($token = $query->getNextToken()) { $sql .= " " . $this->renderToken($token); } return $sql; }
protected function renderInCondition() { $sql = "IN "; $sql .= $this->renderToken($this->query->getNextToken()); // open $list = []; while (($token = $this->query->getNextToken()) && $token->getType() != "close") { $list[] = $this->renderToken($token); } $sql .= implode(", ", $list); if (!empty($token)) { $sql .= $this->renderToken($token); // close } return $sql; }
protected function addIncludes(TokenSequencerInterface $query, $includeRelationships = null) { $includeRelationships = is_null($includeRelationships) ? $this->includeRelationshipsByDefault : $includeRelationships; if (!empty($includeRelationships)) { if (!is_array($includeRelationships) || isset($includeRelationships[0])) { $includeRelationships = ["this" => $includeRelationships]; } $allRelationships = ["this" => $this->entityMetadata->getRelationships()]; $relationships = []; $parents = []; // add all the relationship metadata we need into the allRelationships array foreach (array_keys($includeRelationships) as $alias) { if ($alias == "this") { // already added the subject entity's relationships continue; } foreach ($allRelationships as $subset) { if (!empty($subset[$alias])) { $relationshipEntity = $subset[$alias][EntityMetadata::METADATA_ENTITY]; $relationshipMetadata = $this->metadataProvider->getEntityMetadata($relationshipEntity); $allRelationships[$alias] = $relationshipMetadata->getRelationships(); break; } } } foreach ($includeRelationships as $alias => $includes) { if (empty($allRelationships[$alias])) { continue; } $thisRelationships = $allRelationships[$alias]; // if we have an array of includes, filter thisRelationships if (is_array($includes)) { $thisRelationships = array_intersect_key($allRelationships[$alias], array_flip($includes)); } // record the parent of each of the child aliases, so we can reference them when we include the entity foreach (array_keys($thisRelationships) as $childAlias) { $parents[$childAlias] = $alias; } // add thisRelationships to the final list $relationships = array_replace($relationships, $thisRelationships); } // get the metadata for each entity and include it on the query foreach ($relationships as $alias => $relationship) { $metadata = $this->metadataProvider->getEntityMetadata($relationship[EntityMetadata::METADATA_ENTITY]); if ($alias == $metadata->getEntity()) { $alias = ""; } $parent = ""; if (!empty($parents[$alias]) && $parents[$alias] != "this") { $parent = $parents[$alias]; } $query->includeEntity($metadata, $alias, $parent); } } }
public function interpretQuery(TokenSequencerInterface $query) { $this->reset(); $this->query = $query; $includes = $query->getIncludes(); $mainMetadata = $query->getEntityMetadata(); $mainCollection = $mainMetadata->getCollection(); $token = $query->getNextToken(); $this->fields = []; while (!empty($token) && in_array($token->getType(), ["function", "field"])) { if ($token->getType() == "function") { /** @var Value $token */ // render the aggregate function SQL e.g. COUNT(*), SUM(field), etc... $fieldSql = $this->renderFunction($token); // create an alias for this aggregate and append the SQL $collectionAlias = $this->findFreeAlias($token->getValue(), $this->fields); $fieldSql .= $this->renderAlias($collectionAlias); } else { /** @var Reference $token */ $fieldName = $mainCollection . "." . $token->getValue(); $collectionAlias = $this->getSelectFieldAlias($fieldName); $fieldSql = $this->renderArbitraryReference($fieldName, $collectionAlias); } // add the sql to the fields array and load the next token $this->fields[$collectionAlias] = $fieldSql; $token = $query->getNextToken(); } // if we had no aggregate functions in the sequence, then this is a standard select query // so, we need to get the fields to return from the entity metadata for each if (empty($this->fields)) { $metadata = $mainMetadata; $collectionAlias = $mainCollection; do { if (empty($metadata)) { continue; } // for each entity field, create an aliased SQL reference $entityFields = $metadata->getFieldNames(); sort($entityFields); foreach ($entityFields as $field) { $field = $collectionAlias . "." . $field; $thisFieldAlias = $this->getSelectFieldAlias($field); $this->fields[$thisFieldAlias] = $this->renderArbitraryReference($field, $thisFieldAlias); } } while (list($collectionAlias, $metadata) = each($includes)); } if (empty($this->fields)) { throw new InterpretationException("Cannot interpret find query, there are no fields to return"); } // if this query has an include, it's more complex and requires separate processing if (count($includes) > 0) { return $this->renderIncludeQuery($token); } // simple, no include query. Render all remaining tokens $sql = "SELECT " . implode(", ", $this->fields) . " FROM " . $this->renderArbitraryReference($mainCollection); // render all other tokens do { if (empty($token)) { break; } $sql .= " " . $this->renderToken($token); } while ($token = $this->query->getNextToken()); return $sql; }
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; }
public function interpretQuery(TokenSequencerInterface $query) { $this->reset(); $this->query = $query; $metadata = $this->query->getEntityMetadata(); // is this an update or insert query /** @var Entity $token */ $token = $this->query->getNextToken(); $entity = $token->getEntity(); $this->primaryKey = $metadata->getPrimaryKey(); if (is_object($entity)) { $pkGetter = "get" . $this->toStudlyCaps($this->primaryKey); if (!method_exists($entity, $pkGetter)) { throw new InterpretationException("Could not get the primary key '{$this->primaryKey}'. The method '{$pkGetter}' does not exist on the entity (" . get_class($entity) . ")"); } $id = $entity->{$pkGetter}(); } else { $id = isset($entity[$this->primaryKey]) ? $entity[$this->primaryKey] : null; } $this->fieldSql = []; $pkField = $this->toSplitCase($this->primaryKey); foreach ($metadata->getFieldNames() as $field) { $this->fieldSql[$field] = $this->renderArbitraryReference($field); } // if any of this entities relationships are one to one, check if we need to set "our" field foreach ($metadata->getRelationships() as $alias => $relationship) { if ($relationship[EntityMetadata::METADATA_RELATIONSHIP_TYPE] == EntityMetadata::RELATIONSHIP_TYPE_ONE_TO_ONE && !empty($relationship[EntityMetadata::METADATA_RELATIONSHIP_OUR_FIELD])) { $field = $relationship[EntityMetadata::METADATA_RELATIONSHIP_OUR_FIELD]; $this->fieldSql[$field] = $this->renderArbitraryReference($field); $theirField = $relationship[EntityMetadata::METADATA_RELATIONSHIP_THEIR_FIELD]; if (empty($theirField)) { $theirField = "id"; } $this->relatedProperties[$field] = ["property" => $relationship[EntityMetadata::METADATA_RELATIONSHIP_PROPERTY], "theirField" => $theirField]; } } $collection = $this->renderArbitraryReference($metadata->getCollection()); $options = $query->getOptions(); $pkMetadata = $metadata->getPrimaryKeyMetadata(); // this is an insert if, we don't have a value for the ID (auto incrementing PK) or the PK isn't auto incrementing // and the query does not specifically request us to update $autoIncrementing = $pkMetadata[EntityMetadata::METADATA_FIELD_AUTO_INCREMENTING]; $isInsert = empty($id) || !$autoIncrementing && (empty($options["saveType"]) || $options["saveType"] != "update"); // remove the primary key from the field list if we're updating or this PK is auto incrementing if (!$isInsert || $autoIncrementing) { unset($this->fieldSql[$pkField]); } if ($isInsert) { // insert; $this->sqlCommand = "insert"; $sql = "INSERT INTO {$collection}"; $sql .= " (" . implode(", ", $this->fieldSql) . ") VALUES "; $entities = []; do { $entities[] = $this->renderToken($token); } while ($token = $this->query->getNextToken()); $sql .= implode(", ", $entities); if ($autoIncrementing) { $this->primaryKeySequence = $collection . "_" . $metadata->getPrimaryKey() . "_seq"; } } else { // update $this->sqlCommand = "update"; $sql = "UPDATE {$collection} SET "; $sql .= $this->renderToken($token); $sql .= " WHERE " . $this->renderArbitraryReference($this->primaryKey) . " = :searchId"; $this->values["searchId"] = $id; } return $sql; }
public function parseTokenSequence(TokenSequencerInterface $tokens) { $sequence = $tokens->getSequence(); $type = $tokens->getType(); $position = $this->parseTokens($type, $sequence); if (!empty($sequence[$position])) { throw new TokenParseException("Unexpected tokens at the end of the sequence. Reached position {$position} of " . count($sequence)); } }