Skip to content

Commit

Permalink
ResultIterator: Correctly wire classes to iterators (#252)
Browse files Browse the repository at this point in the history
Co-authored-by: Guillaume <[email protected]>
  • Loading branch information
homersimpsons and homersimpsons authored Mar 24, 2021
1 parent c702860 commit 3465791
Show file tree
Hide file tree
Showing 15 changed files with 294 additions and 191 deletions.
27 changes: 8 additions & 19 deletions src/AbstractTDBMObject.php
Original file line number Diff line number Diff line change
Expand Up @@ -251,27 +251,15 @@ protected function set(string $var, $value, ?string $tableName = null): void
}
}

/**
* @param string $foreignKeyName
* @param AbstractTDBMObject $bean
*/
protected function setRef(string $foreignKeyName, AbstractTDBMObject $bean = null, string $tableName = null): void
protected function setRef(string $foreignKeyName, ?AbstractTDBMObject $bean, string $tableName, string $className, string $resultIteratorClass): void
{
if ($tableName === null) {
if (count($this->dbRows) > 1) {
throw new TDBMException('This object is based on several tables. You must specify which table you are retrieving data from.');
} elseif (count($this->dbRows) === 1) {
$tableName = (string) array_keys($this->dbRows)[0];
} else {
throw new TDBMException('Please specify a table for this object.');
}
}
assert($bean === null || is_a($bean, $className), new TDBMInvalidArgumentException('$bean should be `null` or `' . $className . '`. `' . ($bean === null ? 'null' : get_class($bean)) . '` provided.'));

if (!isset($this->dbRows[$tableName])) {
$this->registerTable($tableName);
}

$oldLinkedBean = $this->dbRows[$tableName]->getRef($foreignKeyName);
$oldLinkedBean = $this->dbRows[$tableName]->getRef($foreignKeyName, $className, $resultIteratorClass);
if ($oldLinkedBean !== null) {
$oldLinkedBean->removeManyToOneRelationship($tableName, $foreignKeyName, $this);
}
Expand All @@ -291,15 +279,15 @@ protected function setRef(string $foreignKeyName, AbstractTDBMObject $bean = nul
*
* @return AbstractTDBMObject|null
*/
protected function getRef(string $foreignKeyName, ?string $tableName = null) : ?AbstractTDBMObject
protected function getRef(string $foreignKeyName, string $tableName, string $className, string $resultIteratorClass) : ?AbstractTDBMObject
{
$tableName = $this->checkTableName($tableName);

if (!isset($this->dbRows[$tableName])) {
return null;
}

return $this->dbRows[$tableName]->getRef($foreignKeyName);
return $this->dbRows[$tableName]->getRef($foreignKeyName, $className, $resultIteratorClass);
}

/**
Expand Down Expand Up @@ -525,15 +513,16 @@ private function removeManyToOneRelationship(string $tableName, string $foreignK
*
* @return AlterableResultIterator
*/
protected function retrieveManyToOneRelationshipsStorage(string $tableName, string $foreignKeyName, array $searchFilter, string $orderString = null) : AlterableResultIterator
protected function retrieveManyToOneRelationshipsStorage(string $tableName, string $foreignKeyName, array $searchFilter, ?string $orderString, string $resultIteratorClass) : AlterableResultIterator
{
assert(is_a($resultIteratorClass, ResultIterator::class, true), new TDBMInvalidArgumentException('$resultIteratorClass should be a `'. ResultIterator::class. '`. `' . $resultIteratorClass . '` provided.'));
$key = $tableName.'___'.$foreignKeyName;
$alterableResultIterator = $this->getManyToOneAlterableResultIterator($tableName, $foreignKeyName);
if ($this->status === TDBMObjectStateEnum::STATE_DETACHED || $this->status === TDBMObjectStateEnum::STATE_NEW || (isset($this->manyToOneRelationships[$key]) && $this->manyToOneRelationships[$key]->getUnderlyingResultIterator() !== null)) {
return $alterableResultIterator;
}

$unalteredResultIterator = $this->tdbmService->findObjects($tableName, $searchFilter, [], $orderString);
$unalteredResultIterator = $this->tdbmService->findObjects($tableName, $searchFilter, [], $orderString, [], null, null, $resultIteratorClass);

$alterableResultIterator->setResultIterator($unalteredResultIterator->getIterator());

Expand Down
7 changes: 2 additions & 5 deletions src/AlterableResultIterator.php
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,8 @@ public function add($object): void
{
$this->alterations->attach($object, 'add');

if ($this->resultArray !== null) {
$foundKey = array_search($object, $this->resultArray, true);
if ($foundKey === false) {
$this->resultArray[] = $object;
}
if ($this->resultArray !== null && !in_array($object, $this->resultArray, true)) {
$this->resultArray[] = $object;
}
}

Expand Down
16 changes: 3 additions & 13 deletions src/DbRow.php
Original file line number Diff line number Diff line change
Expand Up @@ -235,16 +235,6 @@ public function set(string $var, $value): void
{
$this->_dbLoadIfNotLoaded();

/*
// Ok, let's start by checking the column type
$type = $this->db_connection->getColumnType($this->dbTableName, $var);
// Throws an exception if the type is not ok.
if (!$this->db_connection->checkType($value, $type)) {
throw new TDBMException("Error! Invalid value passed for attribute '$var' of table '$this->dbTableName'. Passed '$value', but expecting '$type'");
}
*/

/*if ($var == $this->getPrimaryKey() && isset($this->dbRow[$var]))
throw new TDBMException("Error! Changing primary key value is forbidden.");*/
$this->dbRow[$var] = $value;
Expand Down Expand Up @@ -275,7 +265,7 @@ public function setRef(string $foreignKeyName, AbstractTDBMObject $bean = null):
*
* @return AbstractTDBMObject|null
*/
public function getRef(string $foreignKeyName) : ?AbstractTDBMObject
public function getRef(string $foreignKeyName, string $className, string $resultIteratorClass) : ?AbstractTDBMObject
{
if (array_key_exists($foreignKeyName, $this->references)) {
return $this->references[$foreignKeyName];
Expand Down Expand Up @@ -303,9 +293,9 @@ public function getRef(string $foreignKeyName) : ?AbstractTDBMObject

// If the foreign key points to the primary key, let's use findObjectByPk
if ($this->tdbmService->getPrimaryKeyColumns($foreignTableName) === $foreignColumns) {
return $this->tdbmService->findObjectByPk($foreignTableName, $filter, [], true);
return $this->tdbmService->findObjectByPk($foreignTableName, $filter, [], true, $className, $resultIteratorClass);
} else {
return $this->tdbmService->findObject($foreignTableName, $filter);
return $this->tdbmService->findObject($foreignTableName, $filter, [], [], $className, $resultIteratorClass);
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/InnerResultIterator.php
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,9 @@ public function next(): void

list($actualClassName, $mainBeanTableName, $tablesUsed) = $this->tdbmService->_getClassNameFromBeanData($beanData);

if ($this->className !== null) {
// @TODO (gua) this is a weird hack to be able to force a TDBMObject...
// `$this->className` could be used to override `$actualClassName`
if ($this->className !== null && is_a($this->className, TDBMObject::class, true)) {
$actualClassName = $this->className;
}

Expand Down
71 changes: 46 additions & 25 deletions src/TDBMService.php
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,10 @@ class TDBMService
* @var string
*/
private $beanNamespace;
/**
* @var string
*/
private $resultIteratorNamespace;

/**
* @var NamingStrategyInterface
Expand Down Expand Up @@ -213,6 +217,7 @@ public function __construct(ConfigurationInterface $configuration)
}
$this->orderByAnalyzer = new OrderByAnalyzer($this->cache, $this->cachePrefix);
$this->beanNamespace = $configuration->getBeanNamespace();
$this->resultIteratorNamespace = $configuration->getResultIteratorNamespace();
$this->namingStrategy = $configuration->getNamingStrategy();
$this->configuration = $configuration;
}
Expand Down Expand Up @@ -364,7 +369,17 @@ private function deleteAllConstraintWithThisObject(AbstractTDBMObject $obj): voi
foreach ($incomingFks as $incomingFk) {
$filter = SafeFunctions::arrayCombine($incomingFk->getUnquotedLocalColumns(), $pks);

$results = $this->findObjects($incomingFk->getLocalTableName(), $filter);
$localTableName = $incomingFk->getLocalTableName();
$results = $this->findObjects(
$localTableName,
$filter,
[],
null,
[],
null,
$this->beanNamespace . '\\' . $this->namingStrategy->getBeanClassName($localTableName),
$this->resultIteratorNamespace . '\\' . $this->namingStrategy->getResultIteratorClassName($localTableName)
);

foreach ($results as $bean) {
$this->deleteCascade($bean);
Expand Down Expand Up @@ -1134,7 +1149,7 @@ private function exploreChildrenTablesRelationships(SchemaAnalyzer $schemaAnalyz
*
* @throws TDBMException
*/
public function findObjects(string $mainTable, $filter = null, array $parameters = array(), $orderString = null, array $additionalTablesFetch = array(), ?int $mode = null, string $className = null, string $resultIteratorClass = ResultIterator::class): ResultIterator
public function findObjects(string $mainTable, $filter, array $parameters, $orderString, array $additionalTablesFetch, ?int $mode, ?string $className, string $resultIteratorClass): ResultIterator
{
if (!is_a($resultIteratorClass, ResultIterator::class, true)) {
throw new TDBMInvalidArgumentException('$resultIteratorClass should be a `'. ResultIterator::class. '`. `' . $resultIteratorClass . '` provided.');
Expand Down Expand Up @@ -1171,7 +1186,7 @@ public function findObjects(string $mainTable, $filter = null, array $parameters
*
* @throws TDBMException
*/
public function findObjectsFromSql(string $mainTable, string $from, $filter = null, array $parameters = array(), $orderString = null, ?int $mode = null, string $className = null, string $resultIteratorClass = ResultIterator::class): ResultIterator
public function findObjectsFromSql(string $mainTable, string $from, $filter, array $parameters, $orderString, ?int $mode, ?string $className, string $resultIteratorClass): ResultIterator
{
if (!is_a($resultIteratorClass, ResultIterator::class, true)) {
throw new TDBMInvalidArgumentException('$resultIteratorClass should be a `'. ResultIterator::class. '`. `' . $resultIteratorClass . '` provided.');
Expand Down Expand Up @@ -1205,15 +1220,17 @@ public function findObjectsFromSql(string $mainTable, string $from, $filter = nu
*
* @throws TDBMException
*/
public function findObjectByPk(string $table, array $primaryKeys, array $additionalTablesFetch = array(), bool $lazy = false, string $className = null): AbstractTDBMObject
public function findObjectByPk(string $table, array $primaryKeys, array $additionalTablesFetch, bool $lazy, string $className, string $resultIteratorClass): AbstractTDBMObject
{
assert(is_a($resultIteratorClass, ResultIterator::class, true), new TDBMInvalidArgumentException('$resultIteratorClass should be a `'. ResultIterator::class. '`. `' . $resultIteratorClass . '` provided.'));
assert(is_a($className, AbstractTDBMObject::class, true), new TDBMInvalidArgumentException('$className should be a `'. AbstractTDBMObject::class. '`. `' . $className . '` provided.'));
$primaryKeys = $this->_getPrimaryKeysFromObjectData($table, $primaryKeys);
$hash = $this->getObjectHash($primaryKeys);

$dbRow = $this->objectStorage->get($table, $hash);
if ($dbRow !== null) {
$bean = $dbRow->getTDBMObject();
if ($className !== null && !is_a($bean, $className)) {
if (!is_a($bean, $className)) {
throw new TDBMException("TDBM cannot create a bean of class '".$className."'. The requested object was already loaded and its class is '".get_class($bean)."'");
}

Expand All @@ -1226,20 +1243,12 @@ public function findObjectByPk(string $table, array $primaryKeys, array $additio
$tables = $this->_getRelatedTablesByInheritance($table);
// Only allowed if no inheritance.
if (count($tables) === 1) {
if ($className === null) {
try {
$className = $this->getBeanClassName($table);
} catch (TDBMInvalidArgumentException $e) {
$className = TDBMObject::class;
}
}

// Let's construct the bean
if (!isset($this->reflectionClassCache[$className])) {
$this->reflectionClassCache[$className] = new \ReflectionClass($className);
}
// Let's bypass the constructor when creating the bean!
/** @var AbstractTDBMObject */
/** @var AbstractTDBMObject $bean */
$bean = $this->reflectionClassCache[$className]->newInstanceWithoutConstructor();
$bean->_constructLazy($table, $primaryKeys, $this);

Expand All @@ -1249,7 +1258,7 @@ public function findObjectByPk(string $table, array $primaryKeys, array $additio

// Did not find the object in cache? Let's query it!
try {
return $this->findObjectOrFail($table, $primaryKeys, [], $additionalTablesFetch, $className);
return $this->findObjectOrFail($table, $primaryKeys, [], $additionalTablesFetch, $className, $resultIteratorClass);
} catch (NoBeanFoundException $exception) {
throw NoBeanFoundException::missPrimaryKeyRecord($table, $primaryKeys, $this->getBeanClassName($table), $exception);
}
Expand All @@ -1262,15 +1271,16 @@ public function findObjectByPk(string $table, array $primaryKeys, array $additio
* @param string|array|null $filter The SQL filters to apply to the query (the WHERE part). All columns must be prefixed by the table name (in the form: table.column)
* @param mixed[] $parameters
* @param string[] $additionalTablesFetch
* @param string $className Optional: The name of the class to instantiate. This class must extend the TDBMObject class. If none is specified, a TDBMObject instance will be returned
* @param string $className The name of the class to instantiate. This class must extend the TDBMObject class. If none is specified, a TDBMObject instance will be returned
*
* @return AbstractTDBMObject|null The object we want, or null if no object matches the filters
*
* @throws TDBMException
*/
public function findObject(string $mainTable, $filter = null, array $parameters = array(), array $additionalTablesFetch = array(), string $className = null) : ?AbstractTDBMObject
public function findObject(string $mainTable, $filter, array $parameters, array $additionalTablesFetch, string $className, string $resultIteratorClass) : ?AbstractTDBMObject
{
$objects = $this->findObjects($mainTable, $filter, $parameters, null, $additionalTablesFetch, self::MODE_ARRAY, $className);
assert(is_a($resultIteratorClass, ResultIterator::class, true), new TDBMInvalidArgumentException('$resultIteratorClass should be a `'. ResultIterator::class. '`. `' . $resultIteratorClass . '` provided.'));
$objects = $this->findObjects($mainTable, $filter, $parameters, null, $additionalTablesFetch, self::MODE_ARRAY, $className, $resultIteratorClass);
return $this->getAtMostOneObjectOrFail($objects, $mainTable, $filter, $parameters);
}

Expand Down Expand Up @@ -1323,9 +1333,10 @@ private function getAtMostOneObjectOrFail(ResultIterator $objects, string $mainT
*
* @throws TDBMException
*/
public function findObjectFromSql(string $mainTable, string $from, $filter = null, array $parameters = array(), ?string $className = null) : ?AbstractTDBMObject
public function findObjectFromSql(string $mainTable, string $from, $filter, array $parameters, ?string $className, string $resultIteratorClass) : ?AbstractTDBMObject
{
$objects = $this->findObjectsFromSql($mainTable, $from, $filter, $parameters, null, self::MODE_ARRAY, $className);
assert(is_a($resultIteratorClass, ResultIterator::class, true), new TDBMInvalidArgumentException('$resultIteratorClass should be a `'. ResultIterator::class. '`. `' . $resultIteratorClass . '` provided.'));
$objects = $this->findObjectsFromSql($mainTable, $from, $filter, $parameters, null, self::MODE_ARRAY, $className, $resultIteratorClass);
return $this->getAtMostOneObjectOrFail($objects, $mainTable, $filter, $parameters);
}

Expand All @@ -1342,7 +1353,7 @@ public function findObjectFromSql(string $mainTable, string $from, $filter = nul
*
* @throws TDBMException
*/
public function findObjectsFromRawSql(string $mainTable, string $sql, array $parameters = array(), ?int $mode = null, string $className = null, string $sqlCount = null, string $resultIteratorClass = ResultIterator::class): ResultIterator
public function findObjectsFromRawSql(string $mainTable, string $sql, array $parameters, ?int $mode, ?string $className, ?string $sqlCount, string $resultIteratorClass): ResultIterator
{
if (!is_a($resultIteratorClass, ResultIterator::class, true)) {
throw new TDBMInvalidArgumentException('$resultIteratorClass should be a `'. ResultIterator::class. '`. `' . $resultIteratorClass . '` provided.');
Expand All @@ -1367,15 +1378,16 @@ public function findObjectsFromRawSql(string $mainTable, string $sql, array $par
* @param string|array|null $filter The SQL filters to apply to the query (the WHERE part). All columns must be prefixed by the table name (in the form: table.column)
* @param mixed[] $parameters
* @param string[] $additionalTablesFetch
* @param string $className Optional: The name of the class to instantiate. This class must extend the TDBMObject class. If none is specified, a TDBMObject instance will be returned
* @param string $className The name of the class to instantiate. This class must extend the TDBMObject class. If none is specified, a TDBMObject instance will be returned
*
* @return AbstractTDBMObject The object we want
*
* @throws TDBMException
*/
public function findObjectOrFail(string $mainTable, $filter = null, array $parameters = array(), array $additionalTablesFetch = array(), string $className = null): AbstractTDBMObject
public function findObjectOrFail(string $mainTable, $filter, array $parameters, array $additionalTablesFetch, string $className, string $resultIteratorClass): AbstractTDBMObject
{
$bean = $this->findObject($mainTable, $filter, $parameters, $additionalTablesFetch, $className);
assert(is_a($resultIteratorClass, ResultIterator::class, true), new TDBMInvalidArgumentException('$resultIteratorClass should be a `'. ResultIterator::class. '`. `' . $resultIteratorClass . '` provided.'));
$bean = $this->findObject($mainTable, $filter, $parameters, $additionalTablesFetch, $className, $resultIteratorClass);
if ($bean === null) {
throw NoBeanFoundException::missFilterRecord($mainTable);
}
Expand Down Expand Up @@ -1458,7 +1470,16 @@ private function fromCache(string $key, callable $closure)
*/
public function _getRelatedBeans(ManyToManyRelationshipPathDescriptor $pathDescriptor, AbstractTDBMObject $bean): ResultIterator
{
return $this->findObjectsFromSql($pathDescriptor->getTargetName(), $pathDescriptor->getPivotFrom(), $pathDescriptor->getPivotWhere(), $pathDescriptor->getPivotParams($this->getPrimaryKeyValues($bean)));
return $this->findObjectsFromSql(
$pathDescriptor->getTargetName(),
$pathDescriptor->getPivotFrom(),
$pathDescriptor->getPivotWhere(),
$pathDescriptor->getPivotParams($this->getPrimaryKeyValues($bean)),
null,
null,
null,
$pathDescriptor->getResultIteratorClass()
);
}

/**
Expand Down
Loading

0 comments on commit 3465791

Please sign in to comment.