protected static function canAggregateInMySql(Repository $repository, $columnName, &$relationshipsToAutoHydrate) { $schema = $repository->getSchema(); $columns = $schema->getColumns(); if (isset($columns[$columnName])) { return true; } if (strpos($columnName, ".") !== false) { // If the column name contains a dot, the part before the dot is the name of a relationship to another model list($relationship, $columnName) = explode(".", $columnName, 2); $relationships = SolutionSchema::getAllRelationshipsForModel($repository->getModelClass()); // Check for the name being that of a relationship if (isset($relationships[$relationship]) && $relationships[$relationship] instanceof OneToMany) { $targetModelName = $relationships[$relationship]->getTargetModelName(); $targetSchema = SolutionSchema::getModelSchema($targetModelName); $targetColumns = $targetSchema->getColumns(); // Check for the column name in the schema of the related model if (isset($targetColumns[$columnName])) { $relationshipsToAutoHydrate[] = $relationship; return true; } } } return false; }
protected static function doFilterWithRepository(Repository $repository, Filter $originalFilter, &$params, &$relationshipsToAutoHydrate) { $relationshipsToAutoHydrate[] = $originalFilter->collectionProperty; // Get the relationship $relationships = SolutionSchema::getAllRelationshipsForModel($repository->getModelClass()); /** * @var OneToMany $relationship */ $relationship = $relationships[$originalFilter->collectionProperty]; $columnName = $relationship->getNavigationPropertyName() . "`.`" . $originalFilter->columnName; $paramName = uniqid() . str_replace("`.`", "", $columnName); $params[$paramName] = $originalFilter->equalTo; $originalFilter->filteredByRepository = true; return "`{$columnName}` = :{$paramName}"; }
public function setFilterValuesOnModel(Model $model) { // Create a row in the intermediate collection so that if the filter was ran again the model // would now qualify. $relationships = SolutionSchema::getAllRelationshipsForModel("\\" . get_class($model)); /** * @var OneToMany $relationship */ $relationship = $relationships[$this->collectionProperty]; $modelName = $relationship->getTargetModelName(); $newModel = SolutionSchema::getModel($modelName); $newModel[$model->UniqueIdentifierColumnName] = $model->UniqueIdentifier; $newModel[$this->columnName] = $this->equalTo; $newModel->save(); return $newModel; }
/** * Determines if $columnName could be filtered with the MySql repository. * * If $columnName contains a dot (.) then we will check to see if we can auto hydrate the navigation * property. * * Note $propertiesToAutoHydrate is passed by reference as this how the filtering stack is able to * communication back to the repository which properties require auto hydration (if supported). * * @param Repository $repository * @param $columnName * @param $propertiesToAutoHydrate * @return bool True if the MySql Repository can add this filter to it's where clause. */ protected static function canFilter(Repository $repository, $columnName, &$propertiesToAutoHydrate) { $schema = $repository->getSchema(); $columns = $schema->getColumns(); if (!isset($columns[$columnName])) { if (stripos($columnName, ".") !== false) { $parts = explode(".", $columnName); if (sizeof($parts) == 2) { $relationship = $parts[0]; $relationships = SolutionSchema::getAllRelationshipsForModel($repository->getModelClass()); if (isset($relationships[$relationship]) && $relationships[$relationship] instanceof OneToOne) { // This is a foreign field and as the __isset() returned true there must be a relationship for this $propertiesToAutoHydrate[] = $relationship; } } else { return false; } } else { return false; } } return true; }
/** * Computes the given aggregates and returns an array of answers * * An answer will be null if the repository is unable to answer it. * * @param \Rhubarb\Stem\Aggregates\Aggregate[] $aggregates * @param \Rhubarb\Stem\Collections\Collection $collection * * @return array */ public function calculateAggregates($aggregates, Collection $collection) { $propertiesToAutoHydrate = []; if (!$this->canFilterExclusivelyByRepository($collection, $namedParams, $propertiesToAutoHydrate)) { return null; } $relationships = SolutionSchema::getAllRelationshipsForModel($this->getModelClass()); $propertiesToAutoHydrate = array_unique($propertiesToAutoHydrate); $joins = []; $joinColumns = []; foreach ($propertiesToAutoHydrate as $joinRelationship) { /** * @var OneToMany $relationship */ $relationship = $relationships[$joinRelationship]; $targetModelName = $relationship->getTargetModelName(); $targetModelClass = SolutionSchema::getModelClass($targetModelName); /** * @var Model $targetModel */ $targetModel = new $targetModelClass(); $targetSchema = $targetModel->getSchema(); $columns = $targetSchema->getColumns(); foreach ($columns as $columnName => $column) { $joinColumns[$targetModelName . $columnName] = "`{$joinRelationship}`.`{$columnName}`"; $joinOriginalToAliasLookup[$targetModelName . "." . $columnName] = $targetModelName . $columnName; if (!isset($joinColumnsByModel[$targetModelName])) { $joinColumnsByModel[$targetModelName] = []; } $joinColumnsByModel[$targetModelName][$targetModelName . $columnName] = $columnName; } $joins[] = "LEFT JOIN `{$targetSchema->schemaName}` AS `{$joinRelationship}` ON `{$this->schema->schemaName}`.`" . $relationship->getSourceColumnName() . "` = `{$joinRelationship}`.`" . $relationship->getTargetColumnName() . "`"; } $joinString = ""; if (sizeof($joins)) { $joinString = " " . implode(" ", $joins); $joinClauses = []; foreach ($joinColumns as $aliasName => $columnName) { $joinClauses[] = "`" . str_replace('.', '`.`', $columnName) . "` AS `" . $aliasName . "`"; } } $clauses = []; $clausePositions = []; $results = []; $i = -1; $c = -1; $relationships = []; foreach ($aggregates as $aggregate) { $i++; $clause = $aggregate->aggregateWithRepository($this, $relationships); if ($clause != "") { $c++; $clauses[] = str_replace('.', '`.`', $clause); $clausePositions[$c] = $i; } else { $results[$i] = null; } } if (sizeof($clauses)) { $schema = $this->getSchema(); $namedParams = []; $propertiesToAutoHydrate = []; $groupClause = ""; if ($joinString) { $groupClause = " GROUP BY `{$schema->schemaName}`.`{$schema->uniqueIdentifierColumnName}`"; } $sql = "SELECT " . implode(", ", $clauses) . " FROM `{$schema->schemaName}`" . $joinString; $filter = $collection->getFilter(); if ($filter !== null) { $filterSql = $filter->filterWithRepository($this, $namedParams, $propertiesToAutoHydrate); if ($filterSql != "") { $sql .= " WHERE " . $filterSql; } } $sql .= $groupClause; $row = array_values(self::returnFirstRow($sql, $namedParams)); foreach ($clausePositions as $rowPosition => $resultPosition) { $results[$resultPosition] = $row[$rowPosition]; } } return $results; }
public function __isset($propertyName) { if (parent::__isset($propertyName)) { return true; } $schema = $this->getSchema(); $columns = $schema->getColumns(); // Check for schema columns if (isset($columns[$propertyName])) { return true; } // Check for dot operator if (strpos($propertyName, ".") !== false) { $parts = explode(".", $propertyName); $firstStep = $this[$parts[0]]; if ($firstStep === null || !$firstStep instanceof Model) { // If the next item in the chain is not model object we can't ask it to // set it's value. return false; } if (isset($firstStep[$parts[1]])) { return true; } } // Check for relationships $className = get_class($this); if (!isset(self::$relationships[$className])) { self::$relationships[$className] = SolutionSchema::getAllRelationshipsForModel($this->modelName); } if (isset(self::$relationships[$className][$propertyName])) { return true; } return false; }
public function addAggregateColumn(Aggregate $aggregate) { $columnName = $aggregate->getAggregateColumnName(); if (strpos($columnName, ".") === false) { throw new AggregateNotSupportedException("Sorry, addAggregateColumn requires that the aggregate operate on a one-to-many relationship property"); } $parts = explode(".", $columnName); $relationship = $parts[0]; // Aggregate Columns must be added on properties that are one to many relationships as we need // to put group bys into the query. $relationships = SolutionSchema::getAllRelationshipsForModel($this->getModelClassName()); if (!isset($relationships[$relationship])) { throw new AggregateNotSupportedException("Sorry, addAggregateColumn requires that the aggregate operate on a one-to-many relationship property"); } if (!$relationships[$relationship] instanceof OneToMany) { throw new AggregateNotSupportedException("Sorry, addAggregateColumn requires that the aggregate operate on a one-to-many relationship property"); } $this->aggregates[] = $aggregate; return $this; }