diff --git a/src/AbstractTDBMObject.php b/src/AbstractTDBMObject.php
index 10df8c56..b1082d79 100644
--- a/src/AbstractTDBMObject.php
+++ b/src/AbstractTDBMObject.php
@@ -110,18 +110,26 @@ public function __construct(?string $tableName = null, array $primaryKeys = [],
/**
* Alternative constructor called when data is fetched from database via a SELECT.
*
- * @param array[] $beanData array
>
+ * @param array> $beanData array>
* @param TDBMService $tdbmService
*/
- public function _constructFromData(array $beanData, TDBMService $tdbmService): void
+ public function _constructFromData(array $beanData, TDBMService $tdbmService, bool $isFullyLoaced): void
{
$this->tdbmService = $tdbmService;
foreach ($beanData as $table => $columns) {
- $this->dbRows[$table] = new DbRow($this, $table, static::getForeignKeys($table), $tdbmService->_getPrimaryKeysFromObjectData($table, $columns), $tdbmService, $columns);
+ $this->dbRows[$table] = new DbRow(
+ $this,
+ $table,
+ static::getForeignKeys($table),
+ $tdbmService->_getPrimaryKeysFromObjectData($table, $columns),
+ $tdbmService,
+ $columns,
+ $isFullyLoaced
+ );
}
- $this->status = TDBMObjectStateEnum::STATE_LOADED;
+ $this->status = $isFullyLoaced ? TDBMObjectStateEnum::STATE_LOADED : TDBMObjectStateEnum::STATE_PARTIALLY_LOADED;
}
/**
@@ -251,27 +259,14 @@ 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);
}
@@ -291,7 +286,7 @@ 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);
@@ -299,11 +294,11 @@ protected function getRef(string $foreignKeyName, ?string $tableName = null) : ?
return null;
}
- return $this->dbRows[$tableName]->getRef($foreignKeyName);
+ return $this->dbRows[$tableName]->getRef($foreignKeyName, $className, $resultIteratorClass);
}
/**
- * Adds a many to many relationship to this bean.
+ * Adds a many to many$table relationship to this bean.
*
* @param string $pivotTableName
* @param AbstractTDBMObject $remoteBean
@@ -525,15 +520,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());
diff --git a/src/AlterableResultIterator.php b/src/AlterableResultIterator.php
index f02ffb4a..3ed53efc 100644
--- a/src/AlterableResultIterator.php
+++ b/src/AlterableResultIterator.php
@@ -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;
}
}
diff --git a/src/DbRow.php b/src/DbRow.php
index 84d2780e..d0cc3290 100644
--- a/src/DbRow.php
+++ b/src/DbRow.php
@@ -21,6 +21,7 @@
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
+use TheCodingMachine\TDBM\Exception\TDBMPartialQueryException;
use TheCodingMachine\TDBM\Schema\ForeignKeys;
/**
@@ -86,14 +87,14 @@ class DbRow
/**
* A list of modified columns, indexed by column name. Value is always true.
*
- * @var array
+ * @var array
*/
private $modifiedColumns = [];
/**
* A list of modified references, indexed by foreign key name. Value is always true.
*
- * @var array
+ * @var array
*/
private $modifiedReferences = [];
/**
@@ -105,18 +106,23 @@ class DbRow
* You should never call the constructor directly. Instead, you should use the
* TDBMService class that will create TDBMObjects for you.
*
- * Used with id!=false when we want to retrieve an existing object
- * and id==false if we want a new object
- *
* @param AbstractTDBMObject $object The object containing this db row
* @param string $tableName
+ * @param ForeignKeys $foreignKeys
* @param mixed[] $primaryKeys
* @param TDBMService $tdbmService
* @param mixed[] $dbRow
* @throws TDBMException
*/
- public function __construct(AbstractTDBMObject $object, string $tableName, ForeignKeys $foreignKeys, array $primaryKeys = array(), TDBMService $tdbmService = null, array $dbRow = [])
- {
+ public function __construct(
+ AbstractTDBMObject $object,
+ string $tableName,
+ ForeignKeys $foreignKeys,
+ array $primaryKeys = array(),
+ TDBMService $tdbmService = null,
+ array $dbRow = [],
+ bool $isFullyLoaced = null
+ ) {
$this->object = $object;
$this->dbTableName = $tableName;
$this->foreignKeys = $foreignKeys;
@@ -133,8 +139,11 @@ public function __construct(AbstractTDBMObject $object, string $tableName, Forei
if (!empty($primaryKeys)) {
$this->_setPrimaryKeys($primaryKeys);
if (!empty($dbRow)) {
+ if ($isFullyLoaced === null) {
+ throw new TDBMInvalidArgumentException('$isFullyLoaced need to be provided if the DbRow is not empty.');
+ }
$this->dbRow = $dbRow;
- $this->status = TDBMObjectStateEnum::STATE_LOADED;
+ $this->status = $isFullyLoaced ? TDBMObjectStateEnum::STATE_LOADED : TDBMObjectStateEnum::STATE_PARTIALLY_LOADED;
} else {
$this->status = TDBMObjectStateEnum::STATE_NOT_LOADED;
}
@@ -172,6 +181,8 @@ public function _setStatus(string $state) : void
// after saving we are back to a loaded state, hence unmodified.
$this->modifiedColumns = [];
$this->modifiedReferences = [];
+ } elseif ($state === TDBMObjectStateEnum::STATE_NOT_LOADED) {
+ $this->dbRow = [];
}
}
@@ -219,7 +230,10 @@ public function _dbLoadIfNotLoaded(): void
*/
public function get(string $var)
{
- if (!isset($this->primaryKeys[$var])) {
+ if (!array_key_exists($var, $this->dbRow)) {
+ if ($this->_getStatus() === TDBMObjectStateEnum::STATE_PARTIALLY_LOADED) {
+ throw new TDBMPartialQueryException($var, $this->dbRow);
+ }
$this->_dbLoadIfNotLoaded();
}
@@ -235,16 +249,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;
@@ -275,7 +279,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];
@@ -303,9 +307,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);
}
}
}
diff --git a/src/InnerResultIterator.php b/src/InnerResultIterator.php
index 86e75006..c7c66988 100644
--- a/src/InnerResultIterator.php
+++ b/src/InnerResultIterator.php
@@ -42,12 +42,14 @@ class InnerResultIterator implements \Iterator, InnerResultIteratorInterface
private $objectStorage;
private $className;
+ /** @var TDBMService */
private $tdbmService;
private $magicSql;
private $parameters;
private $limit;
private $offset;
private $columnDescriptors;
+ /** @var MagicQuery */
private $magicQuery;
/**
@@ -65,6 +67,8 @@ class InnerResultIterator implements \Iterator, InnerResultIteratorInterface
* @var LoggerInterface
*/
private $logger;
+ /** @var bool */
+ private $hasExcludedColumns;
protected $count = null;
@@ -76,8 +80,19 @@ private function __construct()
* @param mixed[] $parameters
* @param array[] $columnDescriptors
*/
- public static function createInnerResultIterator(string $magicSql, array $parameters, ?int $limit, ?int $offset, array $columnDescriptors, ObjectStorageInterface $objectStorage, ?string $className, TDBMService $tdbmService, MagicQuery $magicQuery, LoggerInterface $logger): self
- {
+ public static function createInnerResultIterator(
+ string $magicSql,
+ array $parameters,
+ ?int $limit,
+ ?int $offset,
+ array $columnDescriptors,
+ ObjectStorageInterface $objectStorage,
+ ?string $className,
+ TDBMService $tdbmService,
+ MagicQuery $magicQuery,
+ LoggerInterface $logger,
+ bool $hasExcludedColumns
+ ): self {
$iterator = new static();
$iterator->magicSql = $magicSql;
$iterator->objectStorage = $objectStorage;
@@ -90,6 +105,7 @@ public static function createInnerResultIterator(string $magicSql, array $parame
$iterator->magicQuery = $magicQuery;
$iterator->databasePlatform = $iterator->tdbmService->getConnection()->getDatabasePlatform();
$iterator->logger = $logger;
+ $iterator->hasExcludedColumns = $hasExcludedColumns;
return $iterator;
}
@@ -170,10 +186,12 @@ public function key()
*/
public function next()
{
+ /** @var array $row */
$row = $this->statement->fetch(\PDO::FETCH_ASSOC);
if ($row) {
// array>>
+ /** @var array>> $beansData */
$beansData = [];
foreach ($row as $key => $value) {
if (!isset($this->columnDescriptors[$key])) {
@@ -201,13 +219,15 @@ public function next()
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...
+ // ClassName could be used to override $actualClassName
+ if ($this->className !== null && is_a($this->className, TDBMObject::class, true)) {
$actualClassName = $this->className;
}
// Let's filter out the beanData that is not used (because it belongs to a part of the hierarchy that is not fetched:
foreach ($beanData as $tableName => $descriptors) {
- if (!in_array($tableName, $tablesUsed)) {
+ if (!in_array($tableName, $tablesUsed, true)) {
unset($beanData[$tableName]);
}
}
@@ -219,6 +239,7 @@ public function next()
$primaryKeys = $this->tdbmService->_getPrimaryKeysFromObjectData($mainBeanTableName, $beanData[$mainBeanTableName]);
$hash = $this->tdbmService->getObjectHash($primaryKeys);
+ /** @var DbRow|null $dbRow */
$dbRow = $this->objectStorage->get($mainBeanTableName, $hash);
if ($dbRow !== null) {
$bean = $dbRow->getTDBMObject();
@@ -228,8 +249,9 @@ public function next()
$reflectionClassCache[$actualClassName] = new \ReflectionClass($actualClassName);
}
// Let's bypass the constructor when creating the bean!
+ /** @var AbstractTDBMObject $bean */
$bean = $reflectionClassCache[$actualClassName]->newInstanceWithoutConstructor();
- $bean->_constructFromData($beanData, $this->tdbmService);
+ $bean->_constructFromData($beanData, $this->tdbmService, !$this->hasExcludedColumns);
}
// The first bean is the one containing the main table.
diff --git a/src/OrderByAnalyzer.php b/src/OrderByAnalyzer.php
index 6055baec..c104a507 100644
--- a/src/OrderByAnalyzer.php
+++ b/src/OrderByAnalyzer.php
@@ -5,6 +5,7 @@
use Doctrine\Common\Cache\Cache;
use PHPSQLParser\PHPSQLParser;
+use PHPSQLParser\utils\ExpressionType;
/**
* Class in charge of analyzing order by clauses.
@@ -85,7 +86,7 @@ private function analyzeOrderByNoCache(string $orderBy) : array
for ($i = 0, $count = count($parsed['ORDER']); $i < $count; ++$i) {
$orderItem = $parsed['ORDER'][$i];
- if ($orderItem['expr_type'] === 'colref') {
+ if ($orderItem['expr_type'] === ExpressionType::COLREF) {
$parts = $orderItem['no_quotes']['parts'];
$columnName = array_pop($parts);
if (!empty($parts)) {
@@ -95,7 +96,7 @@ private function analyzeOrderByNoCache(string $orderBy) : array
}
$results[] = [
- 'type' => 'colref',
+ 'type' => ExpressionType::COLREF,
'table' => $tableName,
'column' => $columnName,
'direction' => $orderItem['direction'],
diff --git a/src/PageIterator.php b/src/PageIterator.php
index f25c516e..08d8a4b0 100644
--- a/src/PageIterator.php
+++ b/src/PageIterator.php
@@ -67,6 +67,8 @@ class PageIterator implements Page, \ArrayAccess, \JsonSerializable
* @var LoggerInterface
*/
private $logger;
+ /** @var bool */
+ private $hasExcludedColumns;
private function __construct()
{
@@ -76,8 +78,21 @@ private function __construct()
* @param mixed[] $parameters
* @param array[] $columnDescriptors
*/
- public static function createResultIterator(ResultIterator $parentResult, string $magicSql, array $parameters, int $limit, int $offset, array $columnDescriptors, ObjectStorageInterface $objectStorage, ?string $className, TDBMService $tdbmService, MagicQuery $magicQuery, int $mode, LoggerInterface $logger): self
- {
+ public static function createResultIterator(
+ ResultIterator $parentResult,
+ string $magicSql,
+ array $parameters,
+ int $limit,
+ int $offset,
+ array $columnDescriptors,
+ ObjectStorageInterface $objectStorage,
+ ?string $className,
+ TDBMService $tdbmService,
+ MagicQuery $magicQuery,
+ int $mode,
+ LoggerInterface $logger,
+ bool $hasExcludedColumns
+ ): self {
$iterator = new self();
$iterator->parentResult = $parentResult;
$iterator->magicSql = $magicSql;
@@ -91,6 +106,7 @@ public static function createResultIterator(ResultIterator $parentResult, string
$iterator->magicQuery = $magicQuery;
$iterator->mode = $mode;
$iterator->logger = $logger;
+ $iterator->hasExcludedColumns = $hasExcludedColumns;
return $iterator;
}
@@ -113,9 +129,9 @@ public function getIterator()
if ($this->parentResult->count() === 0) {
$this->innerResultIterator = new EmptyInnerResultIterator();
} elseif ($this->mode === TDBMService::MODE_CURSOR) {
- $this->innerResultIterator = InnerResultIterator::createInnerResultIterator($this->magicSql, $this->parameters, $this->limit, $this->offset, $this->columnDescriptors, $this->objectStorage, $this->className, $this->tdbmService, $this->magicQuery, $this->logger);
+ $this->innerResultIterator = InnerResultIterator::createInnerResultIterator($this->magicSql, $this->parameters, $this->limit, $this->offset, $this->columnDescriptors, $this->objectStorage, $this->className, $this->tdbmService, $this->magicQuery, $this->logger, $this->hasExcludedColumns);
} else {
- $this->innerResultIterator = InnerResultArray::createInnerResultIterator($this->magicSql, $this->parameters, $this->limit, $this->offset, $this->columnDescriptors, $this->objectStorage, $this->className, $this->tdbmService, $this->magicQuery, $this->logger);
+ $this->innerResultIterator = InnerResultArray::createInnerResultIterator($this->magicSql, $this->parameters, $this->limit, $this->offset, $this->columnDescriptors, $this->objectStorage, $this->className, $this->tdbmService, $this->magicQuery, $this->logger, $this->hasExcludedColumns);
}
}
diff --git a/src/QueryFactory/AbstractQueryFactory.php b/src/QueryFactory/AbstractQueryFactory.php
index d78da3fc..95e0f87a 100644
--- a/src/QueryFactory/AbstractQueryFactory.php
+++ b/src/QueryFactory/AbstractQueryFactory.php
@@ -3,6 +3,8 @@
namespace TheCodingMachine\TDBM\QueryFactory;
+use PHPSQLParser\utils\ExpressionType;
+use TheCodingMachine\TDBM\ResultIterator;
use function array_unique;
use Doctrine\DBAL\Platforms\MySqlPlatform;
use Doctrine\DBAL\Schema\Schema;
@@ -52,6 +54,8 @@ abstract class AbstractQueryFactory implements QueryFactory
* @var string
*/
protected $mainTable;
+ /** @var null|ResultIterator */
+ protected $resultIterator;
/**
* @param TDBMService $tdbmService
@@ -68,6 +72,11 @@ public function __construct(TDBMService $tdbmService, Schema $schema, OrderByAna
$this->mainTable = $mainTable;
}
+ public function setResultIterator(ResultIterator $resultIterator): void
+ {
+ $this->resultIterator = $resultIterator;
+ }
+
/**
* Returns the column list that must be fetched for the SQL request.
*
@@ -78,7 +87,7 @@ public function __construct(TDBMService $tdbmService, Schema $schema, OrderByAna
* @param string|UncheckedOrderBy|null $orderBy
*
* @param bool $canAddAdditionalTablesFetch Set to true if the function can add additional tables to fetch (so if the factory generates its own FROM clause)
- * @return mixed[] A 3 elements array: [$columnDescList, $columnsList, $reconstructedOrderBy]
+ * @return mixed[] A 4 elements array: [$columnDescList, $columnsList, $reconstructedOrderBy, $hasExcludedColumns]
*/
protected function getColumnsList(string $mainTable, array $additionalTablesFetch = array(), $orderBy = null, bool $canAddAdditionalTablesFetch = false): array
{
@@ -120,7 +129,7 @@ protected function getColumnsList(string $mainTable, array $additionalTablesFetc
// If we sort by a column, there is a high chance we will fetch the bean containing this column.
// Hence, we should add the table to the $additionalTablesFetch
foreach ($orderByColumns as $orderByColumn) {
- if ($orderByColumn['type'] === 'colref') {
+ if ($orderByColumn['type'] === ExpressionType::COLREF) {
if ($orderByColumn['table'] !== null) {
if ($canAddAdditionalTablesFetch) {
$additionalTablesFetch[] = $orderByColumn['table'];
@@ -140,16 +149,16 @@ protected function getColumnsList(string $mainTable, array $additionalTablesFetc
$reconstructedOrderBys[] = ($orderByColumn['table'] !== null ? $mysqlPlatform->quoteIdentifier($orderByColumn['table']).'.' : '').$mysqlPlatform->quoteIdentifier($orderByColumn['column']).' '.$orderByColumn['direction'];
}
} elseif ($orderByColumn['type'] === 'expr') {
+ if ($securedOrderBy) {
+ throw new TDBMInvalidArgumentException('Invalid ORDER BY column: "'.$orderByColumn['expr'].'". If you want to use expression in your ORDER BY clause, you must wrap them in a UncheckedOrderBy object. For instance: new UncheckedOrderBy("col1 + col2 DESC")');
+ }
+
$sortColumnName = 'sort_column_'.$sortColumn;
$columnsList[] = $orderByColumn['expr'].' as '.$sortColumnName;
$columnDescList[$sortColumnName] = [
'tableGroup' => null,
];
++$sortColumn;
-
- if ($securedOrderBy) {
- throw new TDBMInvalidArgumentException('Invalid ORDER BY column: "'.$orderByColumn['expr'].'". If you want to use expression in your ORDER BY clause, you must wrap them in a UncheckedOrderBy object. For instance: new UncheckedOrderBy("col1 + col2 DESC")');
- }
}
}
@@ -178,22 +187,30 @@ protected function getColumnsList(string $mainTable, array $additionalTablesFetc
$mysqlPlatform = new MySqlPlatform();
// Now, let's build the column list
+ $hasExcludedColumns = false;
foreach ($allFetchedTables as $table) {
foreach ($this->schema->getTable($table)->getColumns() as $column) {
$columnName = $column->getName();
- $columnDescList[$table.'____'.$columnName] = [
- 'as' => $table.'____'.$columnName,
- 'table' => $table,
- 'column' => $columnName,
- 'type' => $column->getType(),
- 'tableGroup' => $tableGroups[$table],
- ];
- $columnsList[] = $mysqlPlatform->quoteIdentifier($table).'.'.$mysqlPlatform->quoteIdentifier($columnName).' as '.
- $connection->quoteIdentifier($table.'____'.$columnName);
+ if ($this->resultIterator === null // @TODO (gua) don't take care of whitelist in case of LIMIT below 2
+ || $table !== $mainTable // @TODO (gua) Inheritance
+ || $this->resultIterator->isInWhitelist($columnName, $table)
+ ) {
+ $columnDescList[$table . '____' . $columnName] = [
+ 'as' => $table . '____' . $columnName,
+ 'table' => $table,
+ 'column' => $columnName,
+ 'type' => $column->getType(),
+ 'tableGroup' => $tableGroups[$table],
+ ];
+ $columnsList[] = $mysqlPlatform->quoteIdentifier($table) . '.' . $mysqlPlatform->quoteIdentifier($columnName) . ' as ' .
+ $connection->quoteIdentifier($table . '____' . $columnName);
+ } else {
+ $hasExcludedColumns = true;
+ }
}
}
- return [$columnDescList, $columnsList, $reconstructedOrderBy];
+ return [$columnDescList, $columnsList, $reconstructedOrderBy, $hasExcludedColumns];
}
abstract protected function compute(): void;
diff --git a/src/QueryFactory/FindObjectsFromRawSqlQueryFactory.php b/src/QueryFactory/FindObjectsFromRawSqlQueryFactory.php
index 71c0c508..f1f8aded 100644
--- a/src/QueryFactory/FindObjectsFromRawSqlQueryFactory.php
+++ b/src/QueryFactory/FindObjectsFromRawSqlQueryFactory.php
@@ -5,6 +5,8 @@
use Doctrine\DBAL\Platforms\MySqlPlatform;
use Doctrine\DBAL\Schema\Schema;
+use PHPSQLParser\utils\ExpressionType;
+use TheCodingMachine\TDBM\ResultIterator;
use TheCodingMachine\TDBM\TDBMException;
use TheCodingMachine\TDBM\TDBMService;
use PHPSQLParser\PHPSQLCreator;
@@ -77,6 +79,11 @@ public function getColumnDescriptors(): array
return $this->columnDescriptors;
}
+ public function setResultIterator(ResultIterator $resultIterator): void
+ {
+ // We do not need to know the result iterator here
+ }
+
/**
* @param string $sql
* @param null|string $sqlCount
@@ -177,7 +184,7 @@ private function formatSelect(array $baseSelect): array
$fetchedTables = [];
foreach ($baseSelect as $entry) {
- if ($entry['expr_type'] !== 'colref') {
+ if ($entry['expr_type'] !== ExpressionType::COLREF) {
$formattedSelect[] = $entry;
continue;
}
@@ -205,7 +212,7 @@ private function formatSelect(array $baseSelect): array
$columnName = $column->getName();
$alias = "{$tableName}____{$columnName}";
$formattedSelect[] = [
- 'expr_type' => 'colref',
+ 'expr_type' => ExpressionType::COLREF,
'base_expr' => $connection->quoteIdentifier($tableName).'.'.$connection->quoteIdentifier($columnName),
'no_quotes' => [
'delim' => '.',
@@ -292,7 +299,7 @@ private function generateSimpleSqlCount(array $parsedSql): array
} else {
$countSubExpr = [
[
- 'expr_type' => 'colref',
+ 'expr_type' => ExpressionType::COLREF,
'base_expr' => '*',
'sub_tree' => false
]
@@ -368,7 +375,7 @@ private function generateWrappedSqlCount(array $parsedSql): array
'base_expr' => 'COUNT',
'sub_tree' => [
[
- 'expr_type' => 'colref',
+ 'expr_type' => ExpressionType::COLREF,
'base_expr' => '*',
'sub_tree' => false
]
@@ -414,4 +421,12 @@ public function getSubQueryColumnDescriptors(): array
{
throw new TDBMException('Using resultset generated from findFromRawSql as subqueries is unsupported for now.');
}
+
+ /**
+ * @return bool Whether it has or no excluded columns.
+ */
+ public function hasExcludedColumns(): bool
+ {
+ return false;
+ }
}
diff --git a/src/QueryFactory/FindObjectsFromSqlQueryFactory.php b/src/QueryFactory/FindObjectsFromSqlQueryFactory.php
index f178da1e..e3c8d598 100644
--- a/src/QueryFactory/FindObjectsFromSqlQueryFactory.php
+++ b/src/QueryFactory/FindObjectsFromSqlQueryFactory.php
@@ -23,6 +23,8 @@ class FindObjectsFromSqlQueryFactory extends AbstractQueryFactory
private $cache;
private $cachePrefix;
private $schemaAnalyzer;
+ /** @var bool */
+ private $hasExcludedColumns;
public function __construct(string $mainTable, string $from, $filterString, $orderBy, TDBMService $tdbmService, Schema $schema, OrderByAnalyzer $orderByAnalyzer, SchemaAnalyzer $schemaAnalyzer, Cache $cache, string $cachePrefix)
{
@@ -43,7 +45,7 @@ protected function compute(): void
$allFetchedTables = $this->tdbmService->_getRelatedTablesByInheritance($this->mainTable);
- list($columnDescList, $columnsList, $orderString) = $this->getColumnsList($this->mainTable, [], $this->orderBy, false);
+ list($columnDescList, $columnsList, $orderString, $hasExcludedColumns) = $this->getColumnsList($this->mainTable, [], $this->orderBy, false);
$sql = 'SELECT DISTINCT '.implode(', ', $columnsList).' FROM '.$this->from;
@@ -104,6 +106,7 @@ protected function compute(): void
$this->magicSqlCount = $countSql;
$this->magicSqlSubQuery = $subQuery;
$this->columnDescList = $columnDescList;
+ $this->hasExcludedColumns = $hasExcludedColumns;
}
/**
@@ -169,6 +172,11 @@ private function getChildrenRelationshipForeignKeysWithoutCache(string $tableNam
}
}
+ public function hasExcludedColumns(): bool
+ {
+ return $this->hasExcludedColumns;
+ }
+
/**
* Returns an item from cache or computes it using $closure and puts it in cache.
*
diff --git a/src/QueryFactory/FindObjectsQueryFactory.php b/src/QueryFactory/FindObjectsQueryFactory.php
index fbaf4c44..3e187f66 100644
--- a/src/QueryFactory/FindObjectsQueryFactory.php
+++ b/src/QueryFactory/FindObjectsQueryFactory.php
@@ -21,6 +21,8 @@ class FindObjectsQueryFactory extends AbstractQueryFactory
* @var Cache
*/
private $cache;
+ /** @var bool */
+ private $hasExcludedColumns;
public function __construct(string $mainTable, array $additionalTablesFetch, $filterString, $orderBy, TDBMService $tdbmService, Schema $schema, OrderByAnalyzer $orderByAnalyzer, Cache $cache)
{
@@ -37,12 +39,13 @@ protected function compute(): void
[
$this->magicSql,
$this->magicSqlCount,
- $this->columnDescList
+ $this->columnDescList,
+ $this->hasExcludedColumns
] = $this->cache->fetch($key);
return;
}
- list($columnDescList, $columnsList, $orderString) = $this->getColumnsList($this->mainTable, $this->additionalTablesFetch, $this->orderBy, true);
+ list($columnDescList, $columnsList, $orderString, $hasExcludedColumns) = $this->getColumnsList($this->mainTable, $this->additionalTablesFetch, $this->orderBy, true);
$sql = 'SELECT DISTINCT '.implode(', ', $columnsList).' FROM MAGICJOIN('.$this->mainTable.')';
@@ -74,11 +77,18 @@ protected function compute(): void
$this->magicSqlCount = $countSql;
$this->magicSqlSubQuery = $subQuery;
$this->columnDescList = $columnDescList;
+ $this->hasExcludedColumns = $hasExcludedColumns;
$this->cache->save($key, [
$this->magicSql,
$this->magicSqlCount,
$this->columnDescList,
+ $this->hasExcludedColumns
]);
}
+
+ public function hasExcludedColumns(): bool
+ {
+ return $this->hasExcludedColumns;
+ }
}
diff --git a/src/QueryFactory/QueryFactory.php b/src/QueryFactory/QueryFactory.php
index dcd2f5bf..a0232477 100644
--- a/src/QueryFactory/QueryFactory.php
+++ b/src/QueryFactory/QueryFactory.php
@@ -3,6 +3,7 @@
namespace TheCodingMachine\TDBM\QueryFactory;
+use TheCodingMachine\TDBM\ResultIterator;
use TheCodingMachine\TDBM\UncheckedOrderBy;
/**
@@ -47,4 +48,11 @@ public function getColumnDescriptors() : array;
* @return string[][] An array of column descriptors. Value is an array with those keys: table, column
*/
public function getSubQueryColumnDescriptors() : array;
+
+ public function setResultIterator(ResultIterator $resultIterator) : void;
+
+ /**
+ * @return bool Whether it has or no excluded columns.
+ */
+ public function hasExcludedColumns() : bool;
}
diff --git a/src/ResultIterator.php b/src/ResultIterator.php
index df7368c4..f50d3200 100644
--- a/src/ResultIterator.php
+++ b/src/ResultIterator.php
@@ -82,13 +82,24 @@ private function __construct()
/**
* @param mixed[] $parameters
*/
- public static function createResultIterator(QueryFactory $queryFactory, array $parameters, ObjectStorageInterface $objectStorage, ?string $className, TDBMService $tdbmService, MagicQuery $magicQuery, int $mode, LoggerInterface $logger): self
- {
+ public static function createResultIterator(
+ QueryFactory $queryFactory,
+ array $parameters,
+ ObjectStorageInterface $objectStorage,
+ ?string $className,
+ TDBMService $tdbmService,
+ MagicQuery $magicQuery,
+ int $mode,
+ LoggerInterface $logger
+ ): self {
$iterator = new static();
if ($mode !== TDBMService::MODE_CURSOR && $mode !== TDBMService::MODE_ARRAY) {
throw new TDBMException("Unknown fetch mode: '".$mode."'");
}
+ if (is_subclass_of($iterator, self::class, false)) { // We only add iterator if it's specified
+ $queryFactory->setResultIterator($iterator);
+ }
$iterator->queryFactory = $queryFactory;
$iterator->objectStorage = $objectStorage;
$iterator->className = $className;
@@ -100,7 +111,7 @@ public static function createResultIterator(QueryFactory $queryFactory, array $p
return $iterator;
}
- public static function createEmpyIterator(): self
+ public static function createEmptyIterator(): self
{
$iterator = new self();
$iterator->totalCount = 0;
@@ -168,9 +179,9 @@ public function getIterator()
if ($this->totalCount === 0) {
$this->innerResultIterator = new EmptyInnerResultIterator();
} elseif ($this->mode === TDBMService::MODE_CURSOR) {
- $this->innerResultIterator = InnerResultIterator::createInnerResultIterator($this->queryFactory->getMagicSql(), $this->parameters, null, null, $this->queryFactory->getColumnDescriptors(), $this->objectStorage, $this->className, $this->tdbmService, $this->magicQuery, $this->logger);
+ $this->innerResultIterator = InnerResultIterator::createInnerResultIterator($this->queryFactory->getMagicSql(), $this->parameters, null, null, $this->queryFactory->getColumnDescriptors(), $this->objectStorage, $this->className, $this->tdbmService, $this->magicQuery, $this->logger, $this->queryFactory->hasExcludedColumns());
} else {
- $this->innerResultIterator = InnerResultArray::createInnerResultIterator($this->queryFactory->getMagicSql(), $this->parameters, null, null, $this->queryFactory->getColumnDescriptors(), $this->objectStorage, $this->className, $this->tdbmService, $this->magicQuery, $this->logger);
+ $this->innerResultIterator = InnerResultArray::createInnerResultIterator($this->queryFactory->getMagicSql(), $this->parameters, null, null, $this->queryFactory->getColumnDescriptors(), $this->objectStorage, $this->className, $this->tdbmService, $this->magicQuery, $this->logger, $this->queryFactory->hasExcludedColumns());
}
}
@@ -188,7 +199,8 @@ public function take($offset, $limit)
if ($this->totalCount === 0) {
return PageIterator::createEmpyIterator($this);
}
- return PageIterator::createResultIterator($this, $this->queryFactory->getMagicSql(), $this->parameters, $limit, $offset, $this->queryFactory->getColumnDescriptors(), $this->objectStorage, $this->className, $this->tdbmService, $this->magicQuery, $this->mode, $this->logger);
+ // @TODO (gua) check offset < 2 to get full RI
+ return PageIterator::createResultIterator($this, $this->queryFactory->getMagicSql(), $this->parameters, $limit, $offset, $this->queryFactory->getColumnDescriptors(), $this->objectStorage, $this->className, $this->tdbmService, $this->magicQuery, $this->mode, $this->logger, $this->queryFactory->hasExcludedColumns());
}
/**
@@ -383,4 +395,26 @@ public function _getSubQuery(): string
return $sql;
}
+
+ /**
+ * @param array $columns The columns names to includes
+ * @param string $table The concerned table, for use in the case of inheritance
+ *
+ * @throws TDBMException If the table is not found in the inheritance schema or If the column do not exist in the table
+ */
+ protected function setWhitelist(string $columns, string $table) : void
+ {
+ throw new TDBMException('Table `' . $table . '` not found in inheritance Schema.');
+ }
+
+ /**
+ * @param string $column The column names to check
+ * @param string $table The concerned table, for use in the case of inheritance
+ *
+ * @throws TDBMException If the table is not found in the inheritance schema or If the column do not exist in the table
+ */
+ public function isInWhitelist(string $column, string $table) : bool
+ {
+ throw new TDBMException('Table `' . $table . '` not found in inheritance Schema');
+ }
}
diff --git a/src/TDBMObjectStateEnum.php b/src/TDBMObjectStateEnum.php
index c13fd664..6008db07 100644
--- a/src/TDBMObjectStateEnum.php
+++ b/src/TDBMObjectStateEnum.php
@@ -28,11 +28,12 @@
*/
final class TDBMObjectStateEnum
{
- const STATE_DETACHED = 'detached';
- const STATE_NEW = 'new';
- const STATE_SAVING = 'saving';
- const STATE_NOT_LOADED = 'not loaded';
- const STATE_LOADED = 'loaded';
- const STATE_DIRTY = 'dirty';
- const STATE_DELETED = 'deleted';
+ public const STATE_DETACHED = 'detached';
+ public const STATE_NEW = 'new';
+ public const STATE_SAVING = 'saving';
+ public const STATE_NOT_LOADED = 'not loaded';
+ public const STATE_LOADED = 'loaded';
+ public const STATE_DIRTY = 'dirty';
+ public const STATE_DELETED = 'deleted';
+ public const STATE_PARTIALLY_LOADED = 'partially_loaded';
}
diff --git a/src/TDBMService.php b/src/TDBMService.php
index dbf387ab..df1c894e 100644
--- a/src/TDBMService.php
+++ b/src/TDBMService.php
@@ -160,18 +160,13 @@ class TDBMService
*/
private $orderByAnalyzer;
- /**
- * @var string
- */
+ /** @var string */
private $beanNamespace;
-
- /**
- * @var NamingStrategyInterface
- */
+ /** @var string */
+ private $resultIteratorNamespace;
+ /** @var NamingStrategyInterface */
private $namingStrategy;
- /**
- * @var ConfigurationInterface
- */
+ /** @var ConfigurationInterface */
private $configuration;
/**
@@ -205,6 +200,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;
}
@@ -356,7 +352,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);
@@ -1126,7 +1132,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.');
@@ -1163,7 +1169,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.');
@@ -1192,20 +1198,23 @@ public function findObjectsFromSql(string $mainTable, string $from, $filter = nu
* @param string[] $additionalTablesFetch
* @param bool $lazy Whether to perform lazy loading on this object or not
* @param string $className
+ * @param string $resultIteratorClass
*
* @return AbstractTDBMObject
*
* @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)."'");
}
@@ -1218,20 +1227,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);
@@ -1241,7 +1242,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) {
$primaryKeysStringified = implode(' and ', array_map(function ($key, $value) {
return "'".$key."' = ".$value;
@@ -1257,15 +1258,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);
}
@@ -1318,9 +1320,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);
}
@@ -1337,7 +1340,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.');
@@ -1368,9 +1371,10 @@ public function findObjectsFromRawSql(string $mainTable, string $sql, array $par
*
* @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 = null, array $parameters = array(), array $additionalTablesFetch = array(), string $className = null, string $resultIteratorClass = ResultIterator::class): 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 new NoBeanFoundException("No result found for query on table '".$mainTable."'");
}
@@ -1453,7 +1457,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()
+ );
}
/**
@@ -1488,7 +1501,7 @@ private function getPivotTableForeignKeys(string $pivotTableName, AbstractTDBMOb
* Key: table name
* Value: array of types indexed by column.
*
- * @var array[]
+ * @var array>
*/
private $typesForTable = [];
@@ -1497,7 +1510,7 @@ private function getPivotTableForeignKeys(string $pivotTableName, AbstractTDBMOb
*
* @param string $tableName
*
- * @return Type[]
+ * @return array
*/
public function _getColumnTypesForTable(string $tableName): array
{
diff --git a/src/Utils/BeanDescriptor.php b/src/Utils/BeanDescriptor.php
index abbd1f79..ac253d93 100644
--- a/src/Utils/BeanDescriptor.php
+++ b/src/Utils/BeanDescriptor.php
@@ -3,6 +3,7 @@
namespace TheCodingMachine\TDBM\Utils;
+use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\Index;
use Doctrine\DBAL\Schema\Schema;
@@ -42,6 +43,7 @@
use Zend\Code\Generator\MethodGenerator;
use Zend\Code\Generator\ParameterGenerator;
use Zend\Code\Generator\PropertyGenerator;
+use Zend\Code\Generator\PropertyValueGenerator;
use function implode;
use function var_export;
@@ -299,7 +301,7 @@ private function getPropertiesForTable(Table $table): array
$beanPropertyDescriptors = [];
foreach ($table->getColumns() as $column) {
- if (array_search($column->getName(), $ignoreColumns) !== false) {
+ if (in_array($column->getName(), $ignoreColumns, true)) {
continue;
}
@@ -317,7 +319,7 @@ private function getPropertiesForTable(Table $table): array
continue;
}
- $beanPropertyDescriptors[] = new ObjectBeanPropertyDescriptor($table, $fk, $this->namingStrategy, $this->beanNamespace, $this->annotationParser, $this->registry->getBeanForTableName($fk->getForeignTableName()));
+ $beanPropertyDescriptors[] = new ObjectBeanPropertyDescriptor($table, $fk, $this->namingStrategy, $this->beanNamespace, $this->annotationParser, $this->registry->getBeanForTableName($fk->getForeignTableName()), $this->resultIteratorNamespace);
} else {
$beanPropertyDescriptors[] = new ScalarBeanPropertyDescriptor($table, $column, $this->namingStrategy, $this->annotationParser);
}
@@ -411,7 +413,7 @@ private function getDirectForeignKeysDescriptors(): array
$descriptors = [];
foreach ($fks as $fk) {
- $desc = new DirectForeignKeyMethodDescriptor($fk, $this->table, $this->namingStrategy, $this->annotationParser, $this->beanNamespace);
+ $desc = new DirectForeignKeyMethodDescriptor($fk, $this->table, $this->namingStrategy, $this->annotationParser, $this->beanNamespace, $this->resultIteratorNamespace);
$this->checkForDuplicate($desc);
$descriptors[] = $desc;
}
@@ -435,13 +437,13 @@ private function getPivotTableDescriptors(): array
if ($fks[0]->getForeignTableName() === $this->table->getName()) {
list($localFk, $remoteFk) = $fks;
- $desc = new PivotTableMethodsDescriptor($table, $localFk, $remoteFk, $this->namingStrategy, $this->beanNamespace, $this->annotationParser);
+ $desc = new PivotTableMethodsDescriptor($table, $localFk, $remoteFk, $this->namingStrategy, $this->annotationParser, $this->beanNamespace, $this->resultIteratorNamespace);
$this->checkForDuplicate($desc);
$descs[] = $desc;
}
if ($fks[1]->getForeignTableName() === $this->table->getName()) {
list($remoteFk, $localFk) = $fks;
- $desc = new PivotTableMethodsDescriptor($table, $localFk, $remoteFk, $this->namingStrategy, $this->beanNamespace, $this->annotationParser);
+ $desc = new PivotTableMethodsDescriptor($table, $localFk, $remoteFk, $this->namingStrategy, $this->annotationParser, $this->beanNamespace, $this->resultIteratorNamespace);
$this->checkForDuplicate($desc);
$descs[] = $desc;
}
@@ -742,6 +744,8 @@ public function generateDaoPhpCode(): ?FileGenerator
$baseClassName = $this->namingStrategy->getBaseDaoClassName($tableName);
$beanClassWithoutNameSpace = $this->namingStrategy->getBeanClassName($tableName);
$beanClassName = $this->beanNamespace.'\\'.$beanClassWithoutNameSpace;
+ $resultIteratorClassWithoutNameSpace = $this->getResultIteratorClassName();
+ $resultIteratorClass = $this->resultIteratorNamespace.'\\'.$resultIteratorClassWithoutNameSpace;
$findByDaoCodeMethods = $this->generateFindByDaoCode($this->beanNamespace, $beanClassWithoutNameSpace, $class);
@@ -832,7 +836,7 @@ public function generateDaoPhpCode(): ?FileGenerator
} else {
\$orderBy = null;
}
-return \$this->tdbmService->findObjects('$tableName', null, [], \$orderBy, [], null, null, \\$this->resultIteratorNamespace\\{$this->getResultIteratorClassName()}::class);
+return \$this->tdbmService->findObjects('$tableName', null, [], \$orderBy, [], null, \\$beanClassName::class, \\$resultIteratorClass::class);
EOF;
$findAllMethod = new MethodGenerator(
@@ -842,7 +846,7 @@ public function generateDaoPhpCode(): ?FileGenerator
$findAllBody,
(new DocBlockGenerator("Get all $beanClassWithoutNameSpace records."))->setWordWrap(false)
);
- $findAllMethod->setReturnType($this->resultIteratorNamespace . '\\' . $this->getResultIteratorClassName());
+ $findAllMethod->setReturnType($resultIteratorClass);
$findAllMethod = $this->codeGeneratorListener->onBaseDaoFindAllGenerated($findAllMethod, $this, $this->configuration, $class);
if ($findAllMethod !== null) {
$class->addMethodFromGenerator($findAllMethod);
@@ -872,7 +876,7 @@ public function generateDaoPhpCode(): ?FileGenerator
'getById',
$parameters,
MethodGenerator::FLAG_PUBLIC,
- "return \$this->tdbmService->findObjectByPk('$tableName', [" . implode(', ', $primaryKeyFilter) . "], [], \$$lazyLoadingParameterName);",
+ "return \$this->tdbmService->findObjectByPk('$tableName', [" . implode(', ', $primaryKeyFilter) . "], [], \$$lazyLoadingParameterName, \\$beanClassName::class, \\$resultIteratorClass::class);",
(new DocBlockGenerator(
"Get $beanClassWithoutNameSpace specified by its ID (its primary key).",
'If the primary key does not exist, an exception is thrown.',
@@ -922,7 +926,7 @@ public function generateDaoPhpCode(): ?FileGenerator
if (\$this->defaultSort && \$orderBy == null) {
\$orderBy = '$tableName.'.\$this->defaultSort.' '.\$this->defaultDirection;
}
-return \$this->tdbmService->findObjects('$tableName', \$filter, \$parameters, \$orderBy, \$additionalTablesFetch, \$mode, null, \\$this->resultIteratorNamespace\\{$this->getResultIteratorClassName()}::class);
+return \$this->tdbmService->findObjects('$tableName', \$filter, \$parameters, \$orderBy, \$additionalTablesFetch, \$mode, \\$beanClassName::class, \\$resultIteratorClass::class);
EOF;
@@ -949,7 +953,7 @@ public function generateDaoPhpCode(): ?FileGenerator
]
))->setWordWrap(false)
);
- $findMethod->setReturnType($this->resultIteratorNamespace . '\\' . $this->getResultIteratorClassName());
+ $findMethod->setReturnType($resultIteratorClass);
$findMethod = $this->codeGeneratorListener->onBaseDaoFindGenerated($findMethod, $this, $this->configuration, $class);
if ($findMethod !== null) {
$class->addMethodFromGenerator($findMethod);
@@ -959,7 +963,7 @@ public function generateDaoPhpCode(): ?FileGenerator
if (\$this->defaultSort && \$orderBy == null) {
\$orderBy = '$tableName.'.\$this->defaultSort.' '.\$this->defaultDirection;
}
-return \$this->tdbmService->findObjectsFromSql('$tableName', \$from, \$filter, \$parameters, \$orderBy, \$mode, null, \\$this->resultIteratorNamespace\\{$this->getResultIteratorClassName()}::class);
+return \$this->tdbmService->findObjectsFromSql('$tableName', \$from, \$filter, \$parameters, \$orderBy, \$mode, \\$beanClassName::class, \\$resultIteratorClass::class);
EOF;
$findFromSqlMethod = new MethodGenerator(
@@ -991,14 +995,14 @@ public function generateDaoPhpCode(): ?FileGenerator
]
))->setWordWrap(false)
);
- $findFromSqlMethod->setReturnType($this->resultIteratorNamespace . '\\' . $this->getResultIteratorClassName());
+ $findFromSqlMethod->setReturnType($resultIteratorClass);
$findFromSqlMethod = $this->codeGeneratorListener->onBaseDaoFindFromSqlGenerated($findFromSqlMethod, $this, $this->configuration, $class);
if ($findFromSqlMethod !== null) {
$class->addMethodFromGenerator($findFromSqlMethod);
}
$findFromRawSqlMethodBody = <<tdbmService->findObjectsFromRawSql('$tableName', \$sql, \$parameters, \$mode, null, \$countSql, \\$this->resultIteratorNamespace\\{$this->getResultIteratorClassName()}::class);
+return \$this->tdbmService->findObjectsFromRawSql('$tableName', \$sql, \$parameters, \$mode, \\$beanClassName::class, \$countSql, \\$resultIteratorClass::class);
EOF;
$findFromRawSqlMethod = new MethodGenerator(
@@ -1026,14 +1030,14 @@ public function generateDaoPhpCode(): ?FileGenerator
]
))->setWordWrap(false)
);
- $findFromRawSqlMethod->setReturnType($this->resultIteratorNamespace . '\\' . $this->getResultIteratorClassName());
+ $findFromRawSqlMethod->setReturnType($resultIteratorClass);
$findFromRawSqlMethod = $this->codeGeneratorListener->onBaseDaoFindFromRawSqlGenerated($findFromRawSqlMethod, $this, $this->configuration, $class);
if ($findFromRawSqlMethod !== null) {
$class->addMethodFromGenerator($findFromRawSqlMethod);
}
$findOneMethodBody = <<tdbmService->findObject('$tableName', \$filter, \$parameters, \$additionalTablesFetch);
+return \$this->tdbmService->findObject('$tableName', \$filter, \$parameters, \$additionalTablesFetch, \\$beanClassName::class, \\$resultIteratorClass::class);
EOF;
@@ -1064,7 +1068,7 @@ public function generateDaoPhpCode(): ?FileGenerator
}
$findOneFromSqlMethodBody = <<tdbmService->findObjectFromSql('$tableName', \$from, \$filter, \$parameters);
+return \$this->tdbmService->findObjectFromSql('$tableName', \$from, \$filter, \$parameters, \\$beanClassName::class, \\$resultIteratorClass::class);
EOF;
$findOneFromSqlMethod = new MethodGenerator(
@@ -1141,7 +1145,7 @@ public function generateResultIteratorPhpCode(): ?FileGenerator
$tableName = $this->table->getName();
- $className = $this->namingStrategy->getResultIteratorClassName($tableName);
+ $classNameWithoutNamespace = $this->namingStrategy->getResultIteratorClassName($tableName);
$baseClassName = $this->namingStrategy->getBaseResultIteratorClassName($tableName);
$beanClassWithoutNameSpace = $this->namingStrategy->getBeanClassName($tableName);
$beanClassName = $this->beanNamespace.'\\'.$beanClassWithoutNameSpace;
@@ -1150,12 +1154,18 @@ public function generateResultIteratorPhpCode(): ?FileGenerator
<<addUse(ResultIterator::class);
$class->setName($baseClassName);
- $class->setExtendedClass(ResultIterator::class);
+ $extends = $this->getExtendedResultIteratorClassName();
+ if ($extends === null) {
+ $class->addUse(ResultIterator::class);
+ $class->setExtendedClass(ResultIterator::class);
+ } else {
+ $class->addUse($this->resultIteratorNamespace . '\\' . $extends);
+ $class->setExtendedClass($extends);
+ }
$class->setDocBlock((new DocBlockGenerator(
"The $baseClassName class will iterate over results of $beanClassWithoutNameSpace class.",
@@ -1163,6 +1173,58 @@ public function generateResultIteratorPhpCode(): ?FileGenerator
[new Tag\MethodTag('getIterator', ['\\' . $beanClassName . '[]'])]
))->setWordWrap(false));
+ $columnsWithWhitelist = [];
+ foreach ($this->table->getColumns() as $column) {
+ $columnsWithWhitelist[$column->getName()] = $column->getType()->getBindingType() !== ParameterType::LARGE_OBJECT; // Should "not null" values be included too ?
+ }
+ $whitelistProperty = new PropertyGenerator('whitelist');
+ $whitelistProperty->setVisibility(AbstractMemberGenerator::VISIBILITY_PRIVATE);
+ $whitelistProperty->setDocBlock(new DocBlockGenerator(
+ 'Columns to fetch from db',
+ null,
+ [new VarTag(null, 'array', 'Associative array indexed by columns')]
+ ));
+ $whitelistProperty->setDefaultValue(new PropertyValueGenerator($columnsWithWhitelist));
+ $class->addPropertyFromGenerator($whitelistProperty);
+
+ $whitelistSetMethod = new MethodGenerator(
+ 'setWhitelist',
+ [new ParameterGenerator('column', 'string') ,new ParameterGenerator('table', 'string', $tableName)],
+ MethodGenerator::FLAG_PROTECTED,
+ <<whitelist, function (bool &\$value) {
+ \$value = false;
+ });
+ // Set wanted columns to true
+ foreach (\$columns as \$column) {
+ if (!array_key_exists(\$column, \$this->whitelist)) {
+ throw new \TheCodingMachine\TDBM\TDBMException('Column `' . \$column . '` does not exist on table `$tableName`');
+ }
+ \$this->whitelist[\$column] = true;
+ }
+ return;
+}
+parent::emptyWhitelist(\$table);
+PHP
+ );
+ $whitelistSetMethod->setReturnType('void');
+ $whitelistHasMethod = new MethodGenerator(
+ 'isInWhitelist',
+ [new ParameterGenerator('column', 'string'), new ParameterGenerator('table', 'string', $tableName)],
+ MethodGenerator::FLAG_PUBLIC,
+ <<whitelist[\$column];
+}
+return parent::isInWhitelist(\$column, \$table);
+PHP
+ );
+ $whitelistHasMethod->setReturnType('bool');
+ $class->addMethodFromGenerator($whitelistSetMethod);
+ $class->addMethodFromGenerator($whitelistHasMethod);
+
$file = $this->codeGeneratorListener->onBaseResultIteratorGenerated($file, $this, $this->configuration);
return $file;
@@ -1265,7 +1327,7 @@ private function generateFindByDaoCodeForIndex(Index $index, string $beanNamespa
$fk = $this->isPartOfForeignKey($this->table, $this->table->getColumn($column));
if ($fk !== null) {
if (!isset($elements[$fk->getName()])) {
- $elements[$fk->getName()] = new ObjectBeanPropertyDescriptor($this->table, $fk, $this->namingStrategy, $this->beanNamespace, $this->annotationParser, $this->registry->getBeanForTableName($fk->getForeignTableName()));
+ $elements[$fk->getName()] = new ObjectBeanPropertyDescriptor($this->table, $fk, $this->namingStrategy, $this->beanNamespace, $this->annotationParser, $this->registry->getBeanForTableName($fk->getForeignTableName()), $this->resultIteratorNamespace);
}
} else {
$elements[] = new ScalarBeanPropertyDescriptor($this->table, $this->table->getColumn($column), $this->namingStrategy, $this->annotationParser);
@@ -1419,24 +1481,31 @@ private function generateGetUsedTablesCode(): MethodGenerator
private function generateOnDeleteCode(): ?MethodGenerator
{
- $code = '';
+ $setRefsToNullCode = ['parent::onDelete();'];
$relationships = $this->getPropertiesForTable($this->table);
foreach ($relationships as $relationship) {
if ($relationship instanceof ObjectBeanPropertyDescriptor) {
$tdbmFk = ForeignKey::createFromFk($relationship->getForeignKey());
- $code .= '$this->setRef('.var_export($tdbmFk->getCacheKey(), true).', null, '.var_export($this->table->getName(), true).");\n";
+ $foreignTableName = $tdbmFk->getForeignTableName();
+ $setRefsToNullCode[] = sprintf(
+ '$this->setRef(%s, %s, %s, %s, %s);',
+ var_export($tdbmFk->getCacheKey(), true),
+ 'null',
+ var_export($this->table->getName(), true),
+ '\\' . $this->beanNamespace . '\\' . $this->namingStrategy->getBeanClassName($foreignTableName) . '::class',
+ '\\' . $this->resultIteratorNamespace . '\\' . $this->namingStrategy->getResultIteratorClassName($foreignTableName) . '::class'
+ );
}
}
- if (!$code) {
+ if (count($setRefsToNullCode) === 1) {
return null;
}
$method = new MethodGenerator('onDelete');
$method->setDocBlock('Method called when the bean is removed from database.');
$method->setReturnType('void');
- $method->setBody('parent::onDelete();
-'.$code);
+ $method->setBody(implode("\n", $setRefsToNullCode));
return $method;
}
@@ -1600,17 +1669,26 @@ public function getTable(): Table
/**
* Returns the extended bean class name (without the namespace), or null if the bean is not extended.
- *
- * @return string
*/
public function getExtendedBeanClassName(): ?string
{
$parentFk = $this->schemaAnalyzer->getParentRelationship($this->table->getName());
if ($parentFk !== null) {
return $this->namingStrategy->getBeanClassName($parentFk->getForeignTableName());
- } else {
- return null;
}
+ return null;
+ }
+
+ /**
+ * Returns the extended result iterator class name (without the namespace), or null if the result iterator is not extended.
+ */
+ public function getExtendedResultIteratorClassName(): ?string
+ {
+ $parentFk = $this->schemaAnalyzer->getParentRelationship($this->table->getName());
+ if ($parentFk !== null) {
+ return $this->namingStrategy->getResultIteratorClassName($parentFk->getForeignTableName());
+ }
+ return null;
}
/**
diff --git a/src/Utils/DirectForeignKeyMethodDescriptor.php b/src/Utils/DirectForeignKeyMethodDescriptor.php
index 97c88778..961981ae 100644
--- a/src/Utils/DirectForeignKeyMethodDescriptor.php
+++ b/src/Utils/DirectForeignKeyMethodDescriptor.php
@@ -40,26 +40,25 @@ class DirectForeignKeyMethodDescriptor implements RelationshipMethodDescriptorIn
* @var string
*/
private $beanNamespace;
-
/**
- * @param ForeignKeyConstraint $fk The foreign key pointing to our bean
- * @param Table $mainTable The main table that is pointed to
- * @param NamingStrategyInterface $namingStrategy
- * @param AnnotationParser $annotationParser
- * @param string $beanNamespace
+ * @var string
*/
+ private $resultIteratorNamespace;
+
public function __construct(
ForeignKeyConstraint $fk,
Table $mainTable,
NamingStrategyInterface $namingStrategy,
AnnotationParser $annotationParser,
- string $beanNamespace
+ string $beanNamespace,
+ string $resultIteratorNamespace
) {
$this->foreignKey = $fk;
$this->mainTable = $mainTable;
$this->namingStrategy = $namingStrategy;
$this->annotationParser = $annotationParser;
$this->beanNamespace = $beanNamespace;
+ $this->resultIteratorNamespace = $resultIteratorNamespace;
}
/**
@@ -106,6 +105,16 @@ public function getBeanClassName(): string
return $this->namingStrategy->getBeanClassName($this->foreignKey->getLocalTableName());
}
+ /**
+ * Returns the name of the class that will be returned by the getter (short name).
+ *
+ * @return string
+ */
+ public function getResultIteratorClassName(): string
+ {
+ return $this->namingStrategy->getResultIteratorClassName($this->foreignKey->getLocalTableName());
+ }
+
/**
* Requests the use of an alternative name for this method.
*/
@@ -133,10 +142,11 @@ public function getCode() : array
$getter->setReturnType('?' . $classType);
$code = sprintf(
- 'return $this->retrieveManyToOneRelationshipsStorage(%s, %s, %s)->first();',
+ 'return $this->retrieveManyToOneRelationshipsStorage(%s, %s, %s, null, %s)->first();',
var_export($this->foreignKey->getLocalTableName(), true),
var_export($tdbmFk->getCacheKey(), true),
- $this->getFilters($this->foreignKey)
+ $this->getFilters($this->foreignKey),
+ '\\' . $this->resultIteratorNamespace . '\\' . $this->getResultIteratorClassName() .'::class'
);
} else {
$getter->setDocBlock(sprintf('Returns the list of %s pointing to this bean via the %s column.', $beanClass, implode(', ', $this->foreignKey->getUnquotedLocalColumns())));
@@ -147,10 +157,11 @@ public function getCode() : array
$getter->setReturnType(AlterableResultIterator::class);
$code = sprintf(
- 'return $this->retrieveManyToOneRelationshipsStorage(%s, %s, %s);',
+ 'return $this->retrieveManyToOneRelationshipsStorage(%s, %s, %s, null, %s);',
var_export($this->foreignKey->getLocalTableName(), true),
var_export($tdbmFk->getCacheKey(), true),
- $this->getFilters($this->foreignKey)
+ $this->getFilters($this->foreignKey),
+ '\\' . $this->resultIteratorNamespace . '\\' . $this->getResultIteratorClassName() .'::class'
);
}
diff --git a/src/Utils/ManyToManyRelationshipPathDescriptor.php b/src/Utils/ManyToManyRelationshipPathDescriptor.php
index 224cf9b6..86f9dc89 100644
--- a/src/Utils/ManyToManyRelationshipPathDescriptor.php
+++ b/src/Utils/ManyToManyRelationshipPathDescriptor.php
@@ -3,6 +3,8 @@
namespace TheCodingMachine\TDBM\Utils;
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
+use TheCodingMachine\TDBM\ResultIterator;
+use TheCodingMachine\TDBM\TDBMInvalidArgumentException;
use function var_export;
class ManyToManyRelationshipPathDescriptor
@@ -28,6 +30,10 @@ class ManyToManyRelationshipPathDescriptor
* @var array
*/
private $whereKeys;
+ /**
+ * @var string
+ */
+ private $resultIteratorClass;
/**
* ManyToManyRelationshipPathDescriptor constructor.
@@ -37,13 +43,15 @@ class ManyToManyRelationshipPathDescriptor
* @param string[] $joinLocalKeys
* @param string[] $whereKeys
*/
- public function __construct(string $targetTable, string $pivotTable, array $joinForeignKeys, array $joinLocalKeys, array $whereKeys)
+ public function __construct(string $targetTable, string $pivotTable, array $joinForeignKeys, array $joinLocalKeys, array $whereKeys, string $resultIteratorClass)
{
+ assert(is_a($resultIteratorClass, ResultIterator::class, true), new TDBMInvalidArgumentException('$resultIteratorClass should be a `'. ResultIterator::class. '`. `' . $resultIteratorClass . '` provided.'));
$this->targetTable = $targetTable;
$this->pivotTable = $pivotTable;
$this->joinForeignKeys = $joinForeignKeys;
$this->joinLocalKeys = $joinLocalKeys;
$this->whereKeys = $whereKeys;
+ $this->resultIteratorClass = $resultIteratorClass;
}
public static function generateModelKey(ForeignKeyConstraint $remoteFk, ForeignKeyConstraint $localFk): string
@@ -95,4 +103,9 @@ public function getPivotParams(array $primaryKeys): array
}
return $params;
}
+
+ public function getResultIteratorClass(): string
+ {
+ return $this->resultIteratorClass;
+ }
}
diff --git a/src/Utils/ObjectBeanPropertyDescriptor.php b/src/Utils/ObjectBeanPropertyDescriptor.php
index d9605670..7d437333 100644
--- a/src/Utils/ObjectBeanPropertyDescriptor.php
+++ b/src/Utils/ObjectBeanPropertyDescriptor.php
@@ -32,6 +32,10 @@ class ObjectBeanPropertyDescriptor extends AbstractBeanPropertyDescriptor
* @var BeanDescriptor
*/
private $foreignBeanDescriptor;
+ /**
+ * @var string
+ */
+ private $resultIteratorNamespace;
/**
* ObjectBeanPropertyDescriptor constructor.
@@ -41,6 +45,7 @@ class ObjectBeanPropertyDescriptor extends AbstractBeanPropertyDescriptor
* @param string $beanNamespace
* @param AnnotationParser $annotationParser
* @param BeanDescriptor $foreignBeanDescriptor The BeanDescriptor of FK foreign table
+ * @param string $resultIteratorNamespace
*/
public function __construct(
Table $table,
@@ -48,7 +53,8 @@ public function __construct(
NamingStrategyInterface $namingStrategy,
string $beanNamespace,
AnnotationParser $annotationParser,
- BeanDescriptor $foreignBeanDescriptor
+ BeanDescriptor $foreignBeanDescriptor,
+ string $resultIteratorNamespace
) {
parent::__construct($table, $namingStrategy);
$this->foreignKey = $foreignKey;
@@ -57,6 +63,7 @@ public function __construct(
$this->table = $table;
$this->namingStrategy = $namingStrategy;
$this->foreignBeanDescriptor = $foreignBeanDescriptor;
+ $this->resultIteratorNamespace = $resultIteratorNamespace;
}
/**
@@ -152,25 +159,27 @@ public function isPrimaryKey(): bool
public function getGetterSetterCode(): array
{
$tableName = $this->table->getName();
+ $foreignTableName = $this->foreignKey->getForeignTableName();
$getterName = $this->getGetterName();
$setterName = $this->getSetterName();
$isNullable = !$this->isCompulsory();
- $referencedBeanName = $this->namingStrategy->getBeanClassName($this->foreignKey->getForeignTableName());
+ $referencedBeanName = $this->namingStrategy->getBeanClassName($foreignTableName);
+ $referencedResultIteratorName = $this->namingStrategy->getResultIteratorClassName($foreignTableName);
$getter = new MethodGenerator($getterName);
$getter->setDocBlock('Returns the ' . $referencedBeanName . ' object bound to this object via the ' . implode(' and ', $this->foreignKey->getUnquotedLocalColumns()) . ' column.');
- /*$types = [ $referencedBeanName ];
- if ($isNullable) {
- $types[] = 'null';
- }
- $getter->getDocBlock()->setTag(new ReturnTag($types));*/
-
$getter->setReturnType(($isNullable ? '?' : '') . $this->beanNamespace . '\\' . $referencedBeanName);
$tdbmFk = ForeignKey::createFromFk($this->foreignKey);
- $getter->setBody('return $this->getRef(' . var_export($tdbmFk->getCacheKey(), true) . ', ' . var_export($tableName, true) . ');');
+ $getter->setBody(sprintf(
+ 'return $this->getRef(%s, %s, %s, %s);',
+ var_export($tdbmFk->getCacheKey(), true),
+ var_export($tableName, true),
+ '\\' . $this->beanNamespace . '\\' . $referencedBeanName . '::class',
+ '\\' . $this->resultIteratorNamespace . '\\' . $referencedResultIteratorName . '::class'
+ ));
if ($this->isGetterProtected()) {
$getter->setVisibility(AbstractMemberGenerator::VISIBILITY_PROTECTED);
@@ -183,7 +192,14 @@ public function getGetterSetterCode(): array
$setter->setReturnType('void');
- $setter->setBody('$this->setRef(' . var_export($tdbmFk->getCacheKey(), true) . ', $object, ' . var_export($tableName, true) . ');');
+ $setter->setBody(sprintf(
+ '$this->setRef(%s, %s, %s, %s, %s);',
+ var_export($tdbmFk->getCacheKey(), true),
+ '$object',
+ var_export($tableName, true),
+ '\\' . $this->beanNamespace . '\\' . $referencedBeanName . '::class',
+ '\\' . $this->resultIteratorNamespace . '\\' . $referencedResultIteratorName . '::class'
+ ));
if ($this->isSetterProtected()) {
$setter->setVisibility(AbstractMemberGenerator::VISIBILITY_PROTECTED);
diff --git a/src/Utils/PivotTableMethodsDescriptor.php b/src/Utils/PivotTableMethodsDescriptor.php
index 36dee569..68f97e8a 100644
--- a/src/Utils/PivotTableMethodsDescriptor.php
+++ b/src/Utils/PivotTableMethodsDescriptor.php
@@ -60,6 +60,10 @@ class PivotTableMethodsDescriptor implements RelationshipMethodDescriptorInterfa
* @var string
*/
private $pathKey;
+ /**
+ * @var string
+ */
+ private $resultIteratorNamespace;
/**
* @param Table $pivotTable The pivot table
@@ -67,14 +71,22 @@ class PivotTableMethodsDescriptor implements RelationshipMethodDescriptorInterfa
* @param ForeignKeyConstraint $remoteFk
* @param NamingStrategyInterface $namingStrategy
*/
- public function __construct(Table $pivotTable, ForeignKeyConstraint $localFk, ForeignKeyConstraint $remoteFk, NamingStrategyInterface $namingStrategy, string $beanNamespace, AnnotationParser $annotationParser)
- {
+ public function __construct(
+ Table $pivotTable,
+ ForeignKeyConstraint $localFk,
+ ForeignKeyConstraint $remoteFk,
+ NamingStrategyInterface $namingStrategy,
+ AnnotationParser $annotationParser,
+ string $beanNamespace,
+ string $resultIteratorNamespace
+ ) {
$this->pivotTable = $pivotTable;
$this->localFk = $localFk;
$this->remoteFk = $remoteFk;
$this->namingStrategy = $namingStrategy;
- $this->beanNamespace = $beanNamespace;
$this->annotationParser = $annotationParser;
+ $this->beanNamespace = $beanNamespace;
+ $this->resultIteratorNamespace = $resultIteratorNamespace;
$this->pathKey = ManyToManyRelationshipPathDescriptor::generateModelKey($this->remoteFk, $this->localFk);
}
@@ -107,6 +119,16 @@ public function getBeanClassName(): string
return $this->namingStrategy->getBeanClassName($this->remoteFk->getForeignTableName());
}
+ /**
+ * Returns the name of the class that will be returned by the getter (short name).
+ *
+ * @return string
+ */
+ public function getResultIteratorClassName(): string
+ {
+ return $this->namingStrategy->getResultIteratorClassName($this->remoteFk->getForeignTableName());
+ }
+
/**
* Returns the plural name.
*
@@ -159,6 +181,7 @@ public function getManyToManyRelationshipInstantiationCode(): string
', '.$this->getArrayInlineCode($this->remoteFk->getUnquotedForeignColumns()).
', '.$this->getArrayInlineCode($this->remoteFk->getUnquotedLocalColumns()).
', '.$this->getArrayInlineCode($this->localFk->getUnquotedLocalColumns()).
+ ', \\' . $this->resultIteratorNamespace . '\\' . $this->getResultIteratorClassName() . '::class'.
')';
}
diff --git a/tests/AbstractTDBMObjectTest.php b/tests/AbstractTDBMObjectTest.php
index ff81c211..ac0c4bd4 100644
--- a/tests/AbstractTDBMObjectTest.php
+++ b/tests/AbstractTDBMObjectTest.php
@@ -17,7 +17,7 @@ public function testGetManyToManyRelationshipDescriptor()
public function testEmptyResultIterator()
{
- $a = ResultIterator::createEmpyIterator();
+ $a = ResultIterator::createEmptyIterator();
foreach ($a as $empty) {
throw new \LogicException("Not supposed to iterate on an empty iterator.");
}
@@ -43,7 +43,7 @@ public function testEmptyResultIterator()
public function testEmptyPageIterator()
{
- $a = ResultIterator::createEmpyIterator();
+ $a = ResultIterator::createEmptyIterator();
$b = $a->take(0, 10);
foreach ($b as $empty) {
throw new \LogicException("Not supposed to iterate on an empty page iterator.");
diff --git a/tests/Dao/TestUserDao.php b/tests/Dao/TestUserDao.php
index 754f650d..a526dddd 100644
--- a/tests/Dao/TestUserDao.php
+++ b/tests/Dao/TestUserDao.php
@@ -6,6 +6,7 @@
use TheCodingMachine\TDBM\Test\Dao\Bean\CountryBean;
use TheCodingMachine\TDBM\Test\Dao\Bean\UserBean;
use TheCodingMachine\TDBM\Test\Dao\Generated\UserBaseDao;
+use TheCodingMachine\TDBM\Test\ResultIterator\UserResultIterator;
use TheCodingMachine\TDBM\UncheckedOrderBy;
/**
@@ -15,20 +16,16 @@ class TestUserDao extends UserBaseDao
{
/**
* Returns the list of users by alphabetical order.
- *
- * @return UserBean[]
*/
- public function getUsersByAlphabeticalOrder()
+ public function getUsersByAlphabeticalOrder(): UserResultIterator
{
// The third parameter will be used in the "ORDER BY" clause of the SQL query.
return $this->find(null, [], 'login ASC');
}
/**
* Returns the list of users by alphabetical order.
- *
- * @return UserBean[]
*/
- public function getUsersFromSqlByAlphabeticalOrder()
+ public function getUsersFromSqlByAlphabeticalOrder(): UserResultIterator
{
// The third parameter will be used in the "ORDER BY" clause of the SQL query.
return $this->findFromSql('users', null, [], 'users.login ASC');
@@ -36,20 +33,16 @@ public function getUsersFromSqlByAlphabeticalOrder()
/**
* Returns the list of users by alphabetical order.
- *
- * @return UserBean[]
*/
- public function getUsersByCountryOrder()
+ public function getUsersByCountryOrder(): UserResultIterator
{
// The third parameter will be used in the "ORDER BY" clause of the SQL query.
return $this->find(null, [], 'country.label ASC', ['country']);
}
/**
* Returns the list of users by alphabetical order.
- *
- * @return UserBean[]
*/
- public function getUsersFromSqlByCountryOrder()
+ public function getUsersFromSqlByCountryOrder(): UserResultIterator
{
// The third parameter will be used in the "ORDER BY" clause of the SQL query.
return $this->findFromSql('users JOIN country ON country.id = users.country_id', null, [], 'country.label ASC');
@@ -60,10 +53,8 @@ public function getUsersFromSqlByCountryOrder()
*
* @param string $login
* @param string $mode
- *
- * @return \TheCodingMachine\TDBM\ResultIterator|UserBean[]
*/
- public function getUsersByLoginStartingWith($login = null, $mode = null)
+ public function getUsersByLoginStartingWith($login = null, $mode = null): UserResultIterator
{
return $this->find('login LIKE :login', ['login' => $login.'%'], null, [], $mode);
}
@@ -87,30 +78,24 @@ public function getUsersByManagerId($managerId)
/**
* Triggers an error because table "contacts" does not exist.
- *
- * @return \TheCodingMachine\TDBM\ResultIterator|UserBean[]
*/
- public function getUsersWrongTableName()
+ public function getUsersWrongTableName(): UserResultIterator
{
return $this->find('contacts.manager_id = 1');
}
/**
* Returns a list of users, sorted by a table on an external column.
- *
- * @return \TheCodingMachine\TDBM\ResultIterator|UserBean[]
*/
- public function getUsersByCountryName()
+ public function getUsersByCountryName(): UserResultIterator
{
return $this->find(null, [], 'country.label DESC');
}
/**
* A test to sort by function.
- *
- * @return \TheCodingMachine\TDBM\ResultIterator|UserBean[]
*/
- public function getUsersByReversedCountryName()
+ public function getUsersByReversedCountryName(): UserResultIterator
{
return $this->find(null, [], new UncheckedOrderBy('REVERSE(country.label) ASC'));
}
@@ -138,4 +123,9 @@ public function getUsersByComplexFilterBag(CountryBean $country, array $names)
$filterBag[] = $country;
return $this->find($filterBag);
}
+
+ public function findForWhitelist()
+ {
+ return $this->tdbmService->findObjects('users', $filter, $parameters, $orderBy, $additionalTablesFetch, $mode, \TheCodingMachine\TDBM\Test\Dao\Bean\UserBean::class, \TheCodingMachine\TDBM\Test\ResultIterator\UserResultIterator::class);
+ }
}
diff --git a/tests/TDBMAbstractServiceTest.php b/tests/TDBMAbstractServiceTest.php
index 79a2c202..268fc527 100644
--- a/tests/TDBMAbstractServiceTest.php
+++ b/tests/TDBMAbstractServiceTest.php
@@ -320,7 +320,7 @@ private static function initSchema(Connection $connection): void
// Tables using @Json annotations
$db->table('accounts')
- ->column('id')->integer()->primaryKey()->autoIncrement()
+ ->column('id')->integer()->primaryKey()->autoIncrement()->notNull()
->column('name')->string();
$db->table('nodes')
diff --git a/tests/TDBMDaoGeneratorTest.php b/tests/TDBMDaoGeneratorTest.php
index 1d923365..49cf5b2b 100644
--- a/tests/TDBMDaoGeneratorTest.php
+++ b/tests/TDBMDaoGeneratorTest.php
@@ -35,8 +35,10 @@
use TheCodingMachine\TDBM\Dao\TestCountryDao;
use TheCodingMachine\TDBM\Dao\TestRoleDao;
use TheCodingMachine\TDBM\Dao\TestUserDao;
+use TheCodingMachine\TDBM\Exception\TDBMPartialQueryException;
use TheCodingMachine\TDBM\Fixtures\Interfaces\TestUserDaoInterface;
use TheCodingMachine\TDBM\Fixtures\Interfaces\TestUserInterface;
+use TheCodingMachine\TDBM\Test\Dao\AccountDao;
use TheCodingMachine\TDBM\Test\Dao\AlbumDao;
use TheCodingMachine\TDBM\Test\Dao\AllNullableDao;
use TheCodingMachine\TDBM\Test\Dao\AnimalDao;
@@ -2236,4 +2238,54 @@ public function testSubQueryExceptionOnPrimaryKeysWithMultipleColumns(): void
$this->expectExceptionMessage('You cannot use in a sub-query a table that has a primary key on more that 1 column.');
$states->_getSubQuery();
}
+
+ // @TODO:
+ // - test can't access partially loaded object
+ // - test can access partially loaded object
+ // - test can refresh partially loaded object
+ // - test can refresh partially loaded RI
+ // What about jsonSerialize ?
+ // test inheritance
+
+ public function testWhitelist(): void
+ {
+ $userDao = new TestUserDao($this->tdbmService);
+ $users = $userDao->findAll();
+
+ $this->assertCount(6, $users);
+ $this->assertEquals('john.smith', $users[0]->getLogin());
+
+// $this
+
+ }
+
+ /**
+ * @depends testDaoGeneration
+ */
+ public function testExceptionOnNotLoadedAccess(): void
+ {
+ $fileDao = new FileDao($this->tdbmService);
+ $this->expectException(TDBMPartialQueryException::class);
+ $files = $fileDao->findAll();
+ // @TODO empty whitelist and add only id
+ foreach ($files as $file) {
+ $this->assertIsInt($file->getId());
+ $file->getFile();
+ }
+ }
+
+ /**
+ * @depends testDaoGeneration
+ */
+ public function testCanAccessWhitelistedProperties(): void
+ {
+ $accountDao = new AccountDao($this->tdbmService);
+ $accounts = $accountDao->findAll();
+ $accounts->liteWhitelist(); // Empty + id / label
+ foreach ($accounts as $account) {
+ $account->getId();
+ $account->getName();
+ }
+ $this->assertTrue(true);
+ }
}
diff --git a/tests/TDBMServiceTest.php b/tests/TDBMServiceTest.php
index 6d83eda1..9f53e26a 100644
--- a/tests/TDBMServiceTest.php
+++ b/tests/TDBMServiceTest.php
@@ -23,6 +23,12 @@
use Psr\Log\LogLevel;
use Psr\Log\NullLogger;
+use TheCodingMachine\TDBM\Test\Dao\Bean\ContactBean;
+use TheCodingMachine\TDBM\Test\ResultIterator\ContactResultIterator;
+use TheCodingMachine\TDBM\Test\ResultIterator\CountryResultIterator;
+use TheCodingMachine\TDBM\Test\ResultIterator\PersonResultIterator;
+use TheCodingMachine\TDBM\Test\ResultIterator\RoleResultIterator;
+use TheCodingMachine\TDBM\Test\ResultIterator\UserResultIterator;
use Wa72\SimpleLogger\ArrayLogger;
class TDBMServiceTest extends TDBMAbstractServiceTest
@@ -126,7 +132,7 @@ public function testInsertMultipleDataAtOnceInInheritance(): void
public function testCompleteSave(): void
{
- $beans = $this->tdbmService->findObjects('users', 'users.login = :login', ['login' => 'jane.doe'], null, [], null, TDBMObject::class);
+ $beans = $this->tdbmService->findObjects('users', 'users.login = :login', ['login' => 'jane.doe'], null, [], null, TDBMObject::class, ResultIterator::class);
$jane = $beans[0];
$jane->setProperty('country_id', 2, 'users');
@@ -136,7 +142,7 @@ public function testCompleteSave(): void
public function testCompleteSave2(): void
{
- $beans = $this->tdbmService->findObjects('users', 'users.login = :login', ['login' => 'jane.doe'], null, [], null, TDBMObject::class);
+ $beans = $this->tdbmService->findObjects('users', 'users.login = :login', ['login' => 'jane.doe'], null, [], null, TDBMObject::class, ResultIterator::class);
$jane = $beans[0];
$this->assertEquals(2, $jane->getProperty('country_id', 'users'));
@@ -209,8 +215,8 @@ public function testFindObjects(): void
$result = $magicQuery->parse("SELECT DISTINCT users.id, users.login FROM users");
var_dump($result);*/
- $beans = $this->tdbmService->findObjects('contact', null, [], 'contact.id ASC', [], null, TDBMObject::class);
- $beans2 = $this->tdbmService->findObjects('contact', 'contact.id = :id', ['id' => 1], null, [], null, TDBMObject::class);
+ $beans = $this->tdbmService->findObjects('contact', null, [], 'contact.id ASC', [], null, TDBMObject::class, ContactResultIterator::class);
+ $beans2 = $this->tdbmService->findObjects('contact', 'contact.id = :id', ['id' => 1], null, [], null, TDBMObject::class, ContactResultIterator::class);
foreach ($beans as $bean) {
$bean1 = $bean;
@@ -242,7 +248,7 @@ public function testRawSqlFilterCountriesByUserCount(): void
HAVING COUNT(users.id) > 1;
SQL;
/** @var Test\Dao\Bean\CountryBean[]|\Porpaginas\Result $beans */
- $beans = $this->tdbmService->findObjectsFromRawSql('country', $sql, [], null, Test\Dao\Bean\CountryBean::class);
+ $beans = $this->tdbmService->findObjectsFromRawSql('country', $sql, [], null, Test\Dao\Bean\CountryBean::class, null, CountryResultIterator::class);
$count = 0;
foreach ($beans as $country) {
@@ -265,7 +271,7 @@ public function testRawSqlOrderCountriesByUserCount(): void
SQL;
/** @var Test\Dao\Bean\CountryBean[]|\Porpaginas\Result $beans */
- $beans = $this->tdbmService->findObjectsFromRawSql('country', $sql, [], null, Test\Dao\Bean\CountryBean::class);
+ $beans = $this->tdbmService->findObjectsFromRawSql('country', $sql, [], null, Test\Dao\Bean\CountryBean::class, null, CountryResultIterator::class);
$count = 0;
foreach ($beans as $country) {
@@ -294,7 +300,7 @@ public function testRawSqlOrderUsersByCustomRoleOrder(): void
SQL;
/** @var Test\Dao\Bean\UserBean[]|\Porpaginas\Result $beans */
- $beans = $this->tdbmService->findObjectsFromRawSql('contact', $sql, [], null, Test\Dao\Bean\UserBean::class);
+ $beans = $this->tdbmService->findObjectsFromRawSql('contact', $sql, [], null, Test\Dao\Bean\UserBean::class, null, UserResultIterator::class);
function getCustomOrder(Test\Dao\Bean\UserBean $contact)
{
@@ -318,7 +324,7 @@ function getCustomOrder(Test\Dao\Bean\UserBean $contact)
public function testArrayAccess(): void
{
- $beans = $this->tdbmService->findObjects('contact', null, [], 'contact.id ASC', [], null, TDBMObject::class);
+ $beans = $this->tdbmService->findObjects('contact', null, [], 'contact.id ASC', [], null, TDBMObject::class, ResultIterator::class);
$this->assertTrue(isset($beans[0]));
$this->assertFalse(isset($beans[42]));
@@ -345,7 +351,7 @@ public function testArrayAccess(): void
public function testArrayAccessException(): void
{
$this->expectException('TheCodingMachine\TDBM\TDBMInvalidOffsetException');
- $beans = $this->tdbmService->findObjects('contact', null, [], 'contact.id ASC', [], null, TDBMObject::class);
+ $beans = $this->tdbmService->findObjects('contact', null, [], 'contact.id ASC', [], null, TDBMObject::class, ContactResultIterator::class);
$beans[-1];
}
@@ -357,7 +363,7 @@ public function testArrayAccessException(): void
public function testArrayAccessException2(): void
{
$this->expectException('TheCodingMachine\TDBM\TDBMInvalidOffsetException');
- $beans = $this->tdbmService->findObjects('contact', null, [], 'contact.id ASC', [], null, TDBMObject::class);
+ $beans = $this->tdbmService->findObjects('contact', null, [], 'contact.id ASC', [], null, TDBMObject::class, ContactResultIterator::class);
$beans['foo'];
}
@@ -369,7 +375,7 @@ public function testArrayAccessException2(): void
public function testBeanGetException(): void
{
$this->expectException('TheCodingMachine\TDBM\TDBMException');
- $beans = $this->tdbmService->findObjects('contact', null, [], 'contact.id ASC', [], null, TDBMObject::class);
+ $beans = $this->tdbmService->findObjects('contact', null, [], 'contact.id ASC', [], null, TDBMObject::class, ResultIterator::class);
$bean = $beans[0];
// we don't specify the table on inheritance table => exception.
@@ -383,7 +389,7 @@ public function testBeanGetException(): void
public function testBeanSetException(): void
{
$this->expectException('TheCodingMachine\TDBM\TDBMException');
- $beans = $this->tdbmService->findObjects('contact', null, [], 'contact.id ASC', [], null, TDBMObject::class);
+ $beans = $this->tdbmService->findObjects('contact', null, [], 'contact.id ASC', [], null, TDBMObject::class, ResultIterator::class);
$bean = $beans[0];
// we don't specify the table on inheritance table => exception.
@@ -392,7 +398,7 @@ public function testBeanSetException(): void
public function testTake(): void
{
- $beans = $this->tdbmService->findObjects('contact', null, [], 'contact.id ASC', [], null, TDBMObject::class);
+ $beans = $this->tdbmService->findObjects('contact', null, [], 'contact.id ASC', [], null, TDBMObject::class, ResultIterator::class);
$page = $beans->take(0, 2);
@@ -419,7 +425,7 @@ public function testTake(): void
public function testTakeInCursorMode(): void
{
- $beans = $this->tdbmService->findObjects('contact', null, [], 'contact.id ASC', [], TDBMService::MODE_CURSOR, TDBMObject::class);
+ $beans = $this->tdbmService->findObjects('contact', null, [], 'contact.id ASC', [], TDBMService::MODE_CURSOR, TDBMObject::class, ContactResultIterator::class);
$page = $beans->take(0, 2);
@@ -446,7 +452,7 @@ public function testTakeInCursorMode(): void
public function testMap(): void
{
- $beans = $this->tdbmService->findObjects('person', null, [], 'person.id ASC', [], null, TDBMObject::class);
+ $beans = $this->tdbmService->findObjects('person', null, [], 'person.id ASC', [], null, TDBMObject::class, ResultIterator::class);
$results = $beans->map(function ($item) {
return $item->getProperty('id', 'person');
@@ -471,7 +477,7 @@ public function testMap(): void
public function testUnsetException(): void
{
$this->expectException('TheCodingMachine\TDBM\TDBMException');
- $beans = $this->tdbmService->findObjects('contact', null, [], 'contact.id ASC', [], null, TDBMObject::class);
+ $beans = $this->tdbmService->findObjects('contact', null, [], 'contact.id ASC', [], null, TDBMObject::class, ContactResultIterator::class);
unset($beans[0]);
}
@@ -483,7 +489,7 @@ public function testUnsetException(): void
public function testSetException(): void
{
$this->expectException('TheCodingMachine\TDBM\TDBMException');
- $beans = $this->tdbmService->findObjects('contact', null, [], 'contact.id ASC', [], null, TDBMObject::class);
+ $beans = $this->tdbmService->findObjects('contact', null, [], 'contact.id ASC', [], null, TDBMObject::class, ContactResultIterator::class);
$beans[0] = 'foo';
}
@@ -495,7 +501,7 @@ public function testSetException(): void
public function testPageUnsetException(): void
{
$this->expectException('TheCodingMachine\TDBM\TDBMException');
- $beans = $this->tdbmService->findObjects('contact', null, [], 'contact.id ASC', [], null, TDBMObject::class);
+ $beans = $this->tdbmService->findObjects('contact', null, [], 'contact.id ASC', [], null, TDBMObject::class, ContactResultIterator::class);
$page = $beans->take(0, 1);
unset($page[0]);
}
@@ -507,14 +513,14 @@ public function testPageUnsetException(): void
public function testPageSetException(): void
{
$this->expectException('TheCodingMachine\TDBM\TDBMException');
- $beans = $this->tdbmService->findObjects('contact', null, [], 'contact.id ASC', [], null, TDBMObject::class);
+ $beans = $this->tdbmService->findObjects('contact', null, [], 'contact.id ASC', [], null, TDBMObject::class, ContactResultIterator::class);
$page = $beans->take(0, 1);
$page[0] = 'foo';
}
public function testToArray(): void
{
- $beans = $this->tdbmService->findObjects('contact', 'contact.id = :id', ['id' => 1], null, [], null, TDBMObject::class);
+ $beans = $this->tdbmService->findObjects('contact', 'contact.id = :id', ['id' => 1], null, [], null, TDBMObject::class, ResultIterator::class);
$beanArray = $beans->toArray();
@@ -524,7 +530,7 @@ public function testToArray(): void
public function testCursorMode(): void
{
- $beans = $this->tdbmService->findObjects('contact', 'contact.id = :id', ['id' => 1], null, [], TDBMService::MODE_CURSOR, TDBMObject::class);
+ $beans = $this->tdbmService->findObjects('contact', 'contact.id = :id', ['id' => 1], null, [], TDBMService::MODE_CURSOR, TDBMObject::class, ContactResultIterator::class);
$this->assertInstanceOf('\\TheCodingMachine\\TDBM\\ResultIterator', $beans);
@@ -556,7 +562,7 @@ public function testCursorMode(): void
public function testSetFetchMode(): void
{
$this->tdbmService->setFetchMode(TDBMService::MODE_CURSOR);
- $beans = $this->tdbmService->findObjects('contact', 'contact.id = :id', ['id' => 1], null, [], null, TDBMObject::class);
+ $beans = $this->tdbmService->findObjects('contact', 'contact.id = :id', ['id' => 1], null, [], null, TDBMObject::class, ContactResultIterator::class);
$this->assertInstanceOf('\\TheCodingMachine\\TDBM\\ResultIterator', $beans);
@@ -587,7 +593,7 @@ public function testInvalidSetFetchMode(): void
public function testCursorModeException(): void
{
$this->expectException('TheCodingMachine\TDBM\TDBMException');
- $beans = $this->tdbmService->findObjects('contact', 'contact.id = :id', ['id' => 1], null, [], 99);
+ $beans = $this->tdbmService->findObjects('contact', 'contact.id = :id', ['id' => 1], null, [], 99, ContactBean::class, ContactResultIterator::class);
}
/**
@@ -597,18 +603,18 @@ public function testCursorModeException(): void
public function testTableNameException(): void
{
$this->expectException('TheCodingMachine\TDBM\TDBMException');
- $beans = $this->tdbmService->findObjects('foo bar');
+ $beans = $this->tdbmService->findObjects('foo bar', null, [], null, [], null, AbstractTDBMObject::class, ResultIterator::class);
}
public function testLinkedTableFetch(): void
{
- $beans = $this->tdbmService->findObjects('contact', 'contact.id = :id', ['id' => 1], null, ['country'], null, TDBMObject::class);
+ $beans = $this->tdbmService->findObjects('contact', 'contact.id = :id', ['id' => 1], null, ['country'], null, TDBMObject::class, ContactResultIterator::class);
$this->assertInstanceOf(ResultIterator::class, $beans);
}
public function testFindObject(): void
{
- $bean = $this->tdbmService->findObject('contact', 'contact.id = :id', ['id' => -42], [], TDBMObject::class);
+ $bean = $this->tdbmService->findObject('contact', 'contact.id = :id', ['id' => -42], [], TDBMObject::class, ContactResultIterator::class);
$this->assertNull($bean);
}
@@ -619,7 +625,7 @@ public function testFindObject(): void
public function testFindObjectOrFail(): void
{
$this->expectException('TheCodingMachine\TDBM\NoBeanFoundException');
- $bean = $this->tdbmService->findObjectOrFail('contact', 'contact.id = :id', ['id' => -42], [], TDBMObject::class);
+ $bean = $this->tdbmService->findObjectOrFail('contact', 'contact.id = :id', ['id' => -42], [], TDBMObject::class, ContactResultIterator::class);
}
/**
@@ -629,7 +635,7 @@ public function testFindObjectByPkException(): void
{
$this->expectException(NoBeanFoundException::class);
$this->expectExceptionMessage("No result found for query on table 'contact' for 'id' = -42");
- $bean = $this->tdbmService->findObjectByPk('contact', ['id' => -42], [], false, TDBMObject::class);
+ $bean = $this->tdbmService->findObjectByPk('contact', ['id' => -42], [], false, TDBMObject::class, ContactResultIterator::class);
}
/**
@@ -639,14 +645,14 @@ public function testFindObjectDuplicateRow(): void
{
$this->expectException(DuplicateRowException::class);
- $bean = $this->tdbmService->findObject('contact');
+ $bean = $this->tdbmService->findObject('contact', null, [], [], TDBMObject::class, ContactResultIterator::class);
}
public function testFindObjectsByBean(): void
{
- $countryBean = $this->tdbmService->findObject('country', 'id = :id', ['id' => 1], [], TDBMObject::class);
+ $countryBean = $this->tdbmService->findObject('country', 'id = :id', ['id' => 1], [], TDBMObject::class, ResultIterator::class);
- $users = $this->tdbmService->findObjects('users', $countryBean, [], null, [], null, TDBMObject::class);
+ $users = $this->tdbmService->findObjects('users', $countryBean, [], null, [], null, TDBMObject::class, ResultIterator::class);
$this->assertCount(1, $users);
$this->assertEquals('jean.dupont', $users[0]->getProperty('login', 'users'));
}
@@ -671,7 +677,10 @@ public function testFindObjectsFromSql(): void
'roles JOIN roles_rights ON roles.id = roles_rights.role_id JOIN rights ON rights.label = roles_rights.right_label',
'rights.label = :right',
array('right' => 'CAN_SING'),
- 'roles.name DESC'
+ 'roles.name DESC',
+ null,
+ null,
+ RoleResultIterator::class
);
$this->assertCount(2, $roles);
$this->assertInstanceOf(AbstractTDBMObject::class, $roles[0]);
@@ -689,7 +698,10 @@ public function testFindObjectsFromSqlBadTableName(): void
'roles JOIN roles_rights ON roles.id = roles_rights.role_id JOIN rights ON rights.label = roles_rights.right_label',
'rights.label = :right',
array('right' => 'CAN_SING'),
- 'name DESC'
+ 'name DESC',
+ null,
+ null,
+ ResultIterator::class
);
}
@@ -705,7 +717,10 @@ public function testFindObjectsFromSqlGroupBy(): void
'roles JOIN roles_rights ON roles.id = roles_rights.role_id JOIN rights ON rights.label = roles_rights.right_label',
'rights.label = :right GROUP BY roles.name',
array('right' => 'CAN_SING'),
- 'name DESC'
+ 'name DESC',
+ null,
+ null,
+ RoleResultIterator::class
);
$role = $roles[0];
}
@@ -720,7 +735,11 @@ public function testFindObjectsFromRawSqlBadTableName(): void
$this->tdbmService->findObjectsFromRawSql(
'#{azerty',
'roles JOIN roles_rights ON roles.id = roles_rights.role_id JOIN rights ON rights.label = roles_rights.right_label WHERE rights.label = :right',
- array('right' => 'CAN_SING')
+ array('right' => 'CAN_SING'),
+ null,
+ TDBMObject::class,
+ null,
+ ResultIterator::class
);
}
@@ -730,7 +749,9 @@ public function testFindObjectFromSql(): void
'roles',
'roles JOIN roles_rights ON roles.id = roles_rights.role_id JOIN rights ON rights.label = roles_rights.right_label',
'rights.label = :right AND name = :name',
- array('right' => 'CAN_SING', 'name' => 'Singers')
+ array('right' => 'CAN_SING', 'name' => 'Singers'),
+ null,
+ RoleResultIterator::class
);
$this->assertInstanceOf(AbstractTDBMObject::class, $role);
}
@@ -746,7 +767,9 @@ public function testFindObjectFromSqlException(): void
'roles',
'roles JOIN roles_rights ON roles.id = roles_rights.role_id JOIN rights ON rights.label = roles_rights.right_label',
'rights.label = :right',
- array('right' => 'CAN_SING')
+ array('right' => 'CAN_SING'),
+ null,
+ RoleResultIterator::class
);
}
@@ -759,7 +782,8 @@ public function testFindObjectsFromSqlHierarchyDown(): void
array('name' => 'Robert Marley', 'name2' => 'Bill Shakespeare'),
null,
null,
- TDBMObject::class
+ TDBMObject::class,
+ PersonResultIterator::class
);
$this->assertCount(2, $users);
$this->assertSame('robert.marley', $users[0]->getProperty('login', 'users'));
@@ -774,7 +798,8 @@ public function testFindObjectsFromSqlHierarchyUp(): void
array('login' => 'robert.marley', 'login2' => 'bill.shakespeare'),
'users.login DESC',
null,
- TDBMObject::class
+ TDBMObject::class,
+ UserResultIterator::class
);
$this->assertCount(2, $users);
$this->assertSame('Robert Marley', $users[0]->getProperty('name', 'person'));
@@ -786,7 +811,7 @@ public function testLogger(): void
$tdbmService = new TDBMService(new Configuration('TheCodingMachine\\TDBM\\Test\\Dao\\Bean', 'TheCodingMachine\\TDBM\\Test\\Dao', self::getConnection(), $this->getNamingStrategy(), null, null, $arrayLogger));
$tdbmService->setLogLevel(LogLevel::DEBUG);
- $beans = $tdbmService->findObjects('contact', null, [], 'contact.id ASC', [], null, TDBMObject::class);
+ $beans = $tdbmService->findObjects('contact', null, [], 'contact.id ASC', [], null, TDBMObject::class, ContactResultIterator::class);
$beans->first();
$this->assertNotEmpty($arrayLogger->get());
@@ -794,14 +819,14 @@ public function testLogger(): void
public function testFindObjectsCountWithOneToManyLink(): void
{
- $countries = $this->tdbmService->findObjects('country', "users.status = 'on' OR users.status = 'off'");
+ $countries = $this->tdbmService->findObjects('country', "users.status = 'on' OR users.status = 'off'", [], null, [], null, null, CountryResultIterator::class);
$this->assertEquals(3, $countries->count());
}
public function testFindObjectsFromSqlCountWithOneToManyLink(): void
{
- $countries = $this->tdbmService->findObjectsFromSql('country', 'country LEFT JOIN users ON country.id = users.country_id', "users.status = 'on' OR users.status = 'off'");
+ $countries = $this->tdbmService->findObjectsFromSql('country', 'country LEFT JOIN users ON country.id = users.country_id', "users.status = 'on' OR users.status = 'off'", [], null, null, null, CountryResultIterator::class);
$this->assertEquals(3, $countries->count());
}
diff --git a/tests/Utils/DirectForeignKeyMethodDescriptorTest.php b/tests/Utils/DirectForeignKeyMethodDescriptorTest.php
index 7b5cb335..51b8e48c 100644
--- a/tests/Utils/DirectForeignKeyMethodDescriptorTest.php
+++ b/tests/Utils/DirectForeignKeyMethodDescriptorTest.php
@@ -15,7 +15,7 @@ public function testGetForeignKey(): void
$table = $this->createMock(Table::class);
$ns = $this->createMock(DefaultNamingStrategy::class);
$ap = $this->createMock(AnnotationParser::class);
- $descriptor = new DirectForeignKeyMethodDescriptor($fk, $table, $ns, $ap, '');
+ $descriptor = new DirectForeignKeyMethodDescriptor($fk, $table, $ns, $ap, '', '');
$this->assertSame($fk, $descriptor->getForeignKey());
$this->assertSame($table, $descriptor->getMainTable());
diff --git a/tests/Utils/PivotTableMethodsDescriptorTest.php b/tests/Utils/PivotTableMethodsDescriptorTest.php
index 434c5124..ad742005 100644
--- a/tests/Utils/PivotTableMethodsDescriptorTest.php
+++ b/tests/Utils/PivotTableMethodsDescriptorTest.php
@@ -17,7 +17,7 @@ public function testGetters(): void
$remoteFk = new ForeignKeyConstraint(['foo2'], new Table('table2'), ['lol2']);
$remoteFk->setLocalTable(new Table('table3'));
$ns = $this->createMock(DefaultNamingStrategy::class);
- $descriptor = new PivotTableMethodsDescriptor($table, $localFk, $remoteFk, $ns, 'Bean\Namespace', AnnotationParser::buildWithDefaultAnnotations([]));
+ $descriptor = new PivotTableMethodsDescriptor($table, $localFk, $remoteFk, $ns, AnnotationParser::buildWithDefaultAnnotations([]), 'Bean\Namespace', 'ResultIterator\Namespace');
$this->assertSame($table, $descriptor->getPivotTable());
$this->assertSame($localFk, $descriptor->getLocalFk());