public function addRepository(\Joseki\LeanMapper\Repository $repository) { $class = get_class($repository); $table = $this->mapper->getTableByRepositoryClass($class); $entity = $this->mapper->getEntityClass($table); $this->entities[] = new $entity(); }
/** * @param string $definition * @param string $alias * @param string $type * @throws InvalidArgumentException */ public function addJoinByType($definition, $alias, $type) { list($fromAlias, $viaProperty) = $this->parseDotNotation($definition); $entityReflection = $this->getReflection($fromEntity = $this->aliases->getEntityClass($fromAlias)); $property = $entityReflection->getEntityProperty($viaProperty); if (!$property->hasRelationship()) { throw new InvalidArgumentException(); } $relationship = $property->getRelationship(); if ($relationship instanceof HasMany) { $this->clauses->join[] = array('type' => $type, 'joinParameters' => array($relationshipTable = $relationship->getRelationshipTable(), $relTableAlias = $relationshipTable . $this->indexer), 'onParameters' => array($fromAlias, $primaryKey = $this->mapper->getPrimaryKey($fromTable = $this->mapper->getTable($fromEntity)), $relTableAlias, $columnReferencingSourceTable = $relationship->getColumnReferencingSourceTable())); $this->hydratorMeta->addTablePrefix($relTableAlias, $relationshipTable); $this->hydratorMeta->addPrimaryKey($relationshipTable, $relTablePrimaryKey = $this->mapper->getPrimaryKey($relationshipTable)); $this->hydratorMeta->addRelationship($alias, new Relationship($fromAlias, $fromTable, $columnReferencingSourceTable, Relationship::DIRECTION_REFERENCING, $relTableAlias, $relationshipTable, $primaryKey)); $this->clauses->join[] = array('type' => $type, 'joinParameters' => array($targetTable = $relationship->getTargetTable(), $alias), 'onParameters' => array($relTableAlias, $columnReferencingTargetTable = $relationship->getColumnReferencingTargetTable(), $alias, $primaryKey = $this->mapper->getPrimaryKey($targetTable))); $this->aliases->addAlias($alias, $property->getType()); $this->hydratorMeta->addTablePrefix($alias, $targetTable); $this->hydratorMeta->addPrimaryKey($targetTable, $primaryKey); $this->hydratorMeta->addRelationship($relTableAlias, new Relationship($relTableAlias, $relationshipTable, $columnReferencingTargetTable, Relationship::DIRECTION_REFERENCED, $alias, $targetTable, $primaryKey)); $this->relationshipTables[$alias] = array($relTableAlias, $relTablePrimaryKey, $relTableAlias . QueryHelper::PREFIX_SEPARATOR . $relTablePrimaryKey, $relTableAlias, $columnReferencingSourceTable, $relTableAlias . QueryHelper::PREFIX_SEPARATOR . $columnReferencingSourceTable, $relTableAlias, $columnReferencingTargetTable, $relTableAlias . QueryHelper::PREFIX_SEPARATOR . $columnReferencingTargetTable); $this->indexer++; } else { $this->clauses->join[] = array('type' => $type, 'joinParameters' => array($targetTable = $relationship->getTargetTable(), $alias), 'onParameters' => $relationship instanceof HasOne ? array($fromAlias, $relationshipColumn = $relationship->getColumnReferencingTargetTable(), $alias, $primaryKey = $this->mapper->getPrimaryKey($targetTable)) : array($fromAlias, $primaryKey = $this->mapper->getPrimaryKey($fromTable = $this->mapper->getTable($fromEntity)), $alias, $columnReferencingSourceTable = $relationship->getColumnReferencingSourceTable())); $this->aliases->addAlias($alias, $property->getType()); $this->hydratorMeta->addTablePrefix($alias, $targetTable); if ($relationship instanceof HasOne) { $this->hydratorMeta->addPrimaryKey($targetTable, $primaryKey); $this->hydratorMeta->addRelationship($alias, new Relationship($fromAlias, $this->mapper->getTable($fromEntity), $relationshipColumn, Relationship::DIRECTION_REFERENCED, $alias, $targetTable, $primaryKey)); } else { $this->hydratorMeta->addPrimaryKey($targetTable, $targetTablePrimaryKey = $this->mapper->getPrimaryKey($targetTable)); $this->hydratorMeta->addRelationship($fromAlias, new Relationship($fromAlias, $fromTable, $columnReferencingSourceTable, Relationship::DIRECTION_REFERENCING, $alias, $targetTable, $primaryKey)); } } }
/** * Checks whether give entity is instance of required type * * @param Entity $entity * @throws InvalidArgumentException */ protected function checkEntityType(Entity $entity) { $entityClass = $this->mapper->getEntityClass($this->getTable()); if (!$entity instanceof $entityClass) { throw new InvalidArgumentException('Repository ' . get_called_class() . ' can only handle ' . $entityClass . ' entites. Use different repository to handle ' . get_class($entity) . '.'); } }
/** * @param Result $referencingResult * @param string $table * @param string $viaColumn * @param string $strategy */ public function setReferencingResult(self $referencingResult, $table, $viaColumn = null, $strategy = self::STRATEGY_IN) { $strategy = $this->translateStrategy($strategy); if ($viaColumn === null) { $viaColumn = $this->mapper->getRelationshipColumn($table, $this->table); } $this->referencing["{$table}({$viaColumn}){$strategy}#" . self::PRELOADED_KEY] = $referencingResult; unset($this->index[$referencingResult->getOriginKey()]); }
private function replaceEntitiesForItsPrimaryKeyValues(array $entities) { foreach ($entities as &$entity) { if ($entity instanceof LeanMapper\Entity) { $entityTable = $this->mapper->getTable(get_class($entity)); // FIXME: Column name could be specified in the entity instead of mapper provided by 'getEntityField' function. $idField = $this->mapper->getEntityField($entityTable, $this->mapper->getPrimaryKey($entityTable)); $entity = $entity->{$idField}; } } return $entities; }
/** * @param $table * @return Property * @throws \Exception */ private function getRelationshipColumnProperty($table) { $class = $this->mapper->getEntityClass($table); if (!class_exists($class)) { throw new \Exception(); } /** @var Entity $entity */ $entity = new $class(); $primaryKey = $this->mapper->getPrimaryKey($table); $primryKeyField = $this->mapper->getEntityField($table, $primaryKey); return $entity->getReflection($this->mapper)->getEntityProperty($primryKeyField); }
/** * @param Entity $entity * @return mixed */ private function getIdValue(Entity $entity) { $table = $this->getTable(); do { $primaryKey = $this->mapper->getPrimaryKey($table); $idField = $this->mapper->getEntityField($table, $primaryKey); $value = $entity->{$idField}; if (!$value instanceof Entity) { return $value; } $entity = $value; $table = $this->mapper->getTable(get_class($entity)); } while (true); }
/** * @return Entity[] */ public function getEntities($limit = null, $offset = null) { if ($this->entities === NULL) { $entities = array(); $entityClass = $this->clauses->from['entityClass']; $result = $this->getResult($this->clauses->from['alias'], $limit, $offset); $primaryKey = $this->mapper->getPrimaryKey($this->mapper->getTable($entityClass)); foreach ($result as $key => $row) { $entity = $this->entityFactory->createEntity($entityClass, new Row($result, $key)); $entities[$entity->{$primaryKey}] = $entity; $entity->makeAlive($this->entityFactory, $this->connection, $this->mapper); $entity->cleanReferencingRowsCache(); } $this->entities = $this->entityFactory->createCollection($entities); } return $this->entities; }
/** * @param string $action * @param string $name * @param mixed $arg * @throws InvalidMethodCallException * @throws InvalidArgumentException * @throws InvalidValueException */ private function addToOrRemoveFrom($action, $name, $arg) { if ($this->isDetached()) { throw new InvalidMethodCallException('Cannot add or remove related entity to detached entity.'); } if ($arg === null) { throw new InvalidArgumentException('Invalid argument given in entity ' . get_called_class() . '.'); } if (is_array($arg) or $arg instanceof Traversable and !$arg instanceof Entity) { foreach ($arg as $value) { $this->addToOrRemoveFrom($action, $name, $value); } } else { $method = $action === self::ACTION_ADD ? 'addTo' : 'removeFrom'; $property = $this->getCurrentReflection()->getEntityProperty($name); if ($property === null or !$property->hasRelationship() or !$property->getRelationship() instanceof Relationship\HasMany) { throw new InvalidMethodCallException("Cannot call {$method} method with '{$name}' property in entity " . get_called_class() . '. Only properties with m:hasMany relationship can be managed this way.'); } if ($property->getFilters()) { throw new InvalidMethodCallException("Cannot call {$method} method with '{$name}' property in entity " . get_called_class() . '. Only properties without filters can be managed this way.'); // deliberate restriction } $relationship = $property->getRelationship(); if ($arg instanceof Entity) { if ($arg->isDetached()) { throw new InvalidArgumentException('Cannot add or remove detached entity ' . get_class($arg) . " to {$name} in entity " . get_called_class() . '.'); } $type = $property->getType(); if (!$arg instanceof $type) { $type = gettype($arg) !== 'object' ? gettype($arg) : 'instance of ' . get_class($arg); throw new InvalidValueException("Unexpected value type given in property '{$property->getName()}' in entity " . get_called_class() . ", {$property->getType()} expected, {$type} given."); } $data = $arg->getRowData(); $arg = $data[$this->mapper->getPrimaryKey($relationship->getTargetTable())]; } $table = $this->mapper->getTable($this->getCurrentReflection()->getName()); $values = [$relationship->getColumnReferencingSourceTable() => $this->row->{$this->mapper->getPrimaryKey($table)}, $relationship->getColumnReferencingTargetTable() => $arg]; $method .= 'Referencing'; $this->row->{$method}($values, $relationship->getRelationshipTable(), $relationship->getColumnReferencingSourceTable(), null, $relationship->getStrategy()); } }
/** * @param string $sourceClass * @param PropertyType $propertyType * @param string $relationshipType * @param string|null $definition * @param IMapper|null $mapper * @return mixed * @throws InvalidAnnotationException */ private static function createRelationship($sourceClass, PropertyType $propertyType, $relationshipType, $definition = null, IMapper $mapper = null) { if ($relationshipType !== 'hasOne') { $strategy = Result::STRATEGY_IN; // default strategy if ($definition !== null and substr($definition, -6) === '#union') { $strategy = Result::STRATEGY_UNION; $definition = substr($definition, 0, -6); } } $pieces = array_replace(array_fill(0, 6, ''), $definition !== null ? explode(':', $definition) : []); $sourceTable = $mapper !== null ? $mapper->getTable($sourceClass) : null; $targetTable = $mapper !== null ? $mapper->getTable($propertyType->getType()) : null; switch ($relationshipType) { case 'hasOne': $relationshipColumn = $mapper !== null ? $mapper->getRelationshipColumn($sourceTable, $targetTable) : self::getSurrogateRelationshipColumn($propertyType); return new Relationship\HasOne($pieces[0] ?: $relationshipColumn, $pieces[1] ?: $targetTable); case 'hasMany': return new Relationship\HasMany($pieces[0] ?: ($mapper !== null ? $mapper->getRelationshipColumn($mapper->getRelationshipTable($sourceTable, $targetTable), $sourceTable) : null), $pieces[1] ?: ($mapper !== null ? $mapper->getRelationshipTable($sourceTable, $targetTable) : null), $pieces[2] ?: ($mapper !== null ? $mapper->getRelationshipColumn($mapper->getRelationshipTable($sourceTable, $targetTable), $targetTable) : null), $pieces[3] ?: $targetTable, $strategy); case 'belongsToOne': $relationshipColumn = $mapper !== null ? $mapper->getRelationshipColumn($targetTable, $sourceTable) : $sourceTable; return new Relationship\BelongsToOne($pieces[0] ?: $relationshipColumn, $pieces[1] ?: $targetTable, $strategy); case 'belongsToMany': $relationshipColumn = $mapper !== null ? $mapper->getRelationshipColumn($targetTable, $sourceTable) : $sourceTable; return new Relationship\BelongsToMany($pieces[0] ?: $relationshipColumn, $pieces[1] ?: $targetTable, $strategy); } return null; }
/** * @param string $table * @param string $viaColumn * @param Filtering|null $filtering * @param string $strategy * @throws InvalidArgumentException * @throws InvalidStateException * @return self */ private function getReferencingResult($table, $viaColumn = null, Filtering $filtering = null, $strategy = self::STRATEGY_IN) { $strategy = $this->translateStrategy($strategy); if ($this->isDetached) { throw new InvalidStateException('Cannot get referencing Result for detached Result.'); } if ($viaColumn === null) { $viaColumn = $this->mapper->getRelationshipColumn($table, $this->table); } $key = "{$table}({$viaColumn}){$strategy}"; if (isset($this->referencing[$forcedKey = $key . '#' . self::KEY_FORCED])) { $ids = $this->extractIds($this->mapper->getPrimaryKey($this->table)); foreach ($this->referencing[$forcedKey] as $filteringResult) { if ($filteringResult->isValidFor($ids, $filtering->getArgs())) { return $filteringResult->getResult(); } } } if (isset($this->referencing[$preloadedKey = $key . '#' . self::KEY_PRELOADED])) { return $this->referencing[$preloadedKey]; } if ($strategy === self::STRATEGY_IN) { if ($filtering === null) { if (!isset($this->referencing[$key])) { isset($ids) or $ids = $this->extractIds($this->mapper->getPrimaryKey($this->table)); $statement = $this->createTableSelection($table, $ids); if ($this->isAlias($viaColumn)) { $statement->where('%n IN %in', $this->trimAlias($viaColumn), $ids); } else { $statement->where('%n.%n IN %in', $table, $viaColumn, $ids); } $data = $statement->execute()->setRowClass(null)->fetchAll(); $this->referencing[$key] = self::createInstance($data, $table, $this->connection, $this->mapper); } } else { isset($ids) or $ids = $this->extractIds($this->mapper->getPrimaryKey($this->table)); $statement = $this->createTableSelection($table, $ids); if ($this->isAlias($viaColumn)) { $statement->where('%n IN %in', $this->trimAlias($viaColumn), $ids); } else { $statement->where('%n.%n IN %in', $table, $viaColumn, $ids); } $filteringResult = $this->applyFiltering($statement, $filtering); if ($filteringResult instanceof FilteringResultDecorator) { if (!isset($this->referencing[$forcedKey])) { $this->referencing[$forcedKey] = []; } $this->referencing[$forcedKey][] = $filteringResult; return $filteringResult->getResult(); } $args = $statement->_export(); $key .= '#' . $this->calculateArgumentsHash($args); if (!isset($this->referencing[$key])) { $data = $this->connection->query($args)->setRowClass(null)->fetchAll(); $this->referencing[$key] = self::createInstance($data, $table, $this->connection, $this->mapper); } } return $this->referencing[$key]; } // $strategy === self::STRATEGY_UNION if ($filtering === null) { if (!isset($this->referencing[$key])) { isset($ids) or $ids = $this->extractIds($this->mapper->getPrimaryKey($this->table)); if (count($ids) === 0) { $data = []; } else { $data = $this->connection->query($this->buildUnionStrategySql($ids, $table, $viaColumn))->setRowClass(null)->fetchAll(); } $this->referencing[$key] = self::createInstance($data, $table, $this->connection, $this->mapper); } } else { isset($ids) or $ids = $this->extractIds($this->mapper->getPrimaryKey($this->table)); if (count($ids) === 0) { $this->referencing[$key] = self::createInstance([], $table, $this->connection, $this->mapper); } else { $firstStatement = $this->createTableSelection($table, [reset($ids)]); if ($this->isAlias($viaColumn)) { $firstStatement->where('%n = ?', $this->trimAlias($viaColumn), reset($ids)); } else { $firstStatement->where('%n.%n = ?', $table, $viaColumn, reset($ids)); } $filteringResult = $this->applyFiltering($firstStatement, $filtering); if ($filteringResult instanceof FilteringResultDecorator) { if (!isset($this->referencing[$forcedKey])) { $this->referencing[$forcedKey] = []; } $this->referencing[$forcedKey][] = $filteringResult; return $filteringResult->getResult(); } $args = $firstStatement->_export(); $key .= '#' . $this->calculateArgumentsHash($args); if (!isset($this->referencing[$key])) { $sql = $this->buildUnionStrategySql($ids, $table, $viaColumn, $filtering); $data = $this->connection->query($sql)->setRowClass(null)->fetchAll(); $result = self::createInstance($data, $table, $this->connection, $this->mapper); $this->referencing[$key] = $result; } } } return $this->referencing[$key]; }