Skip to content

QueryFactory: Cache compute result for findFrom and findFromRaw #180

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
@@ -34,7 +34,7 @@
"symfony/filesystem": "^2.7 || ^3 || ^4",
"ramsey/uuid": "^3.7",
"doctrine/annotations": "^1.6",
"zendframework/zend-code": "^3.3.1",
"zendframework/zend-code": "^3.4",
"psr/container": "^1",
"ext-PDO": "*",
"ext-json": "*",
20 changes: 11 additions & 9 deletions src/QueryFactory/FindObjectsFromRawSqlQueryFactory.php
Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@

namespace TheCodingMachine\TDBM\QueryFactory;

use Doctrine\Common\Cache\Cache;
use Doctrine\DBAL\Platforms\MySqlPlatform;
use Doctrine\DBAL\Schema\Schema;
use TheCodingMachine\TDBM\TDBMException;
@@ -39,20 +40,17 @@ class FindObjectsFromRawSqlQueryFactory implements QueryFactory
* @var string
*/
private $mainTable;

/**
* FindObjectsFromRawSqlQueryFactory constructor.
* @param TDBMService $tdbmService
* @param Schema $schema
* @param string $mainTable
* @param string $sql
* @param string $sqlCount
* @var Cache
*/
public function __construct(TDBMService $tdbmService, Schema $schema, string $mainTable, string $sql, string $sqlCount = null)
private $cache;

public function __construct(TDBMService $tdbmService, Schema $schema, string $mainTable, string $sql, ?string $sqlCount, Cache $cache)
{
$this->tdbmService = $tdbmService;
$this->schema = $schema;
$this->mainTable = $mainTable;
$this->cache = $cache;

[$this->processedSql, $this->processedSqlCount, $this->columnDescriptors] = $this->compute($sql, $sqlCount);
}
@@ -85,6 +83,10 @@ public function getColumnDescriptors(): array
*/
private function compute(string $sql, ?string $sqlCount): array
{
$key = 'FindObjectsFromRawSqlQueryFactory_' . dechex(crc32(var_export($sqlCount, true) . $sql));
if ($this->cache->contains($key)) {
return $this->cache->fetch($key);
}
$parser = new PHPSQLParser();
$parsedSql = $parser->parse($sql);

@@ -95,7 +97,7 @@ private function compute(string $sql, ?string $sqlCount): array
} else {
throw new TDBMException('Unable to analyze query "'.$sql.'"');
}

$this->cache->save($key, [$processedSql, $processedSqlCount, $columnDescriptors]);
return [$processedSql, $processedSqlCount, $columnDescriptors];
}

26 changes: 21 additions & 5 deletions src/QueryFactory/FindObjectsFromSqlQueryFactory.php
Original file line number Diff line number Diff line change
@@ -36,6 +36,17 @@ public function __construct(string $mainTable, string $from, $filterString, $ord

protected function compute(): void
{
$key = 'FindObjectsFromSqlQueryFactory_' . dechex(crc32($this->mainTable.'__'.$this->from.'__'.$this->filterString.'__'.$this->orderBy));
if ($this->cache->contains($key)) {
[
$this->magicSql,
$this->magicSqlCount,
$this->magicSqlSubQuery,
$this->columnDescList
] = $this->cache->fetch($key);
return;
}

// We quote in MySQL because of MagicQuery that will be applied.
$mySqlPlatform = new MySqlPlatform();

@@ -104,6 +115,13 @@ protected function compute(): void
$this->magicSqlCount = $countSql;
$this->magicSqlSubQuery = $subQuery;
$this->columnDescList = $columnDescList;

$this->cache->save($key, [
$this->magicSql,
$this->magicSqlCount,
$this->magicSqlSubQuery,
$this->columnDescList,
]);
}

/**
@@ -161,12 +179,10 @@ private function getChildrenRelationshipForeignKeysWithoutCache(string $tableNam
return $this->getChildrenRelationshipForeignKeys($fk->getLocalTableName());
}, $children);

$fks = array_merge($children, call_user_func_array('array_merge', $fksTables));

return $fks;
} else {
return [];
return array_merge($children, ...$fksTables);
}

return [];
}

/**
7 changes: 4 additions & 3 deletions src/TDBMService.php
Original file line number Diff line number Diff line change
@@ -23,7 +23,6 @@

use Doctrine\Common\Cache\Cache;
use Doctrine\Common\Cache\ClearableCache;
use Doctrine\Common\Cache\VoidCache;
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\Platforms\AbstractPlatform;
@@ -1083,9 +1082,11 @@ private function exploreChildrenTablesRelationships(SchemaAnalyzer $schemaAnalyz
$tables = [$table];
$keys = $schemaAnalyzer->getChildrenRelationships($table);

$tmpTables = [];
foreach ($keys as $key) {
$tables = array_merge($tables, $this->exploreChildrenTablesRelationships($schemaAnalyzer, $key->getLocalTableName()));
$tmpTables[] = $this->exploreChildrenTablesRelationships($schemaAnalyzer, $key->getLocalTableName());
}
$tables = array_merge($tables, ...$tmpTables);

return $tables;
}
@@ -1349,7 +1350,7 @@ public function findObjectsFromRawSql(string $mainTable, string $sql, array $par

$mode = $mode ?: $this->mode;

$queryFactory = new FindObjectsFromRawSqlQueryFactory($this, $this->tdbmSchemaAnalyzer->getSchema(), $mainTable, $sql, $sqlCount);
$queryFactory = new FindObjectsFromRawSqlQueryFactory($this, $this->tdbmSchemaAnalyzer->getSchema(), $mainTable, $sql, $sqlCount, $this->cache);

return $resultIteratorClass::createResultIterator($queryFactory, $parameters, $this->objectStorage, $className, $this, $this->magicQuery, $mode, $this->logger);
}
44 changes: 29 additions & 15 deletions src/Utils/BeanDescriptor.php
Original file line number Diff line number Diff line change
@@ -361,8 +361,9 @@ private function generateBeanConstructor() : MethodGenerator
$constructorProperties = $this->getConstructorProperties();

$constructor = new MethodGenerator('__construct', [], MethodGenerator::FLAG_PUBLIC);
$constructor->setDocBlock('The constructor takes all compulsory arguments.');
$constructor->getDocBlock()->setWordWrap(false);
$constructorDocBlock = new DocBlockGenerator('The constructor takes all compulsory arguments.');
$constructorDocBlock->setWordWrap(false);
$constructor->setDocBlock($constructorDocBlock);

$assigns = [];
$parentConstructorArguments = [];
@@ -374,7 +375,7 @@ private function generateBeanConstructor() : MethodGenerator
}
$constructor->setParameter($parameter);

$constructor->getDocBlock()->setTag($property->getParamAnnotation());
$constructorDocBlock->setTag($property->getParamAnnotation());

if ($property->getTable()->getName() === $this->table->getName()) {
$assigns[] = $property->getConstructorAssignCode()."\n";
@@ -487,9 +488,14 @@ public function generateJsonSerialize(): MethodGenerator
$parentFk = $this->schemaAnalyzer->getParentRelationship($tableName);

$method = new MethodGenerator('jsonSerialize');
$method->setDocBlock('Serializes the object for JSON encoding.');
$method->getDocBlock()->setTag(new ParamTag('$stopRecursion', ['bool'], 'Parameter used internally by TDBM to stop embedded objects from embedding other objects.'));
$method->getDocBlock()->setTag(new ReturnTag(['array']));
$method->setDocBlock(new DocBlockGenerator(
'Serializes the object for JSON encoding.',
null,
[
new ParamTag('$stopRecursion', ['bool'], 'Parameter used internally by TDBM to stop embedded objects from embedding other objects.'),
new ReturnTag(['array'])
]
));
$method->setParameter(new ParameterGenerator('stopRecursion', 'bool', false));

if ($parentFk !== null) {
@@ -1409,8 +1415,11 @@ private function generateGetUsedTablesCode(): MethodGenerator
}

$method = new MethodGenerator('getUsedTables');
$method->setDocBlock('Returns an array of used tables by this bean (from parent to child relationship).');
$method->getDocBlock()->setTag(new ReturnTag(['string[]']));
$method->setDocBlock(new DocBlockGenerator(
'Returns an array of used tables by this bean (from parent to child relationship).',
null,
[new ReturnTag(['string[]'])]
));
$method->setReturnType('array');
$method->setBody($code);

@@ -1433,7 +1442,7 @@ private function generateOnDeleteCode(): ?MethodGenerator
}

$method = new MethodGenerator('onDelete');
$method->setDocBlock('Method called when the bean is removed from database.');
$method->setDocBlock(new DocBlockGenerator('Method called when the bean is removed from database.'));
$method->setReturnType('void');
$method->setBody('parent::onDelete();
'.$code);
@@ -1453,8 +1462,11 @@ private function generateGetManyToManyRelationshipDescriptorCode(array $pivotTab

$method = new MethodGenerator('_getManyToManyRelationshipDescriptor');
$method->setVisibility(AbstractMemberGenerator::VISIBILITY_PUBLIC);
$method->setDocBlock('Get the paths used for many to many relationships methods.');
$method->getDocBlock()->setTag(new GenericTag('internal'));
$method->setDocBlock(new DocBlockGenerator(
'Get the paths used for many to many relationships methods.',
null,
[new GenericTag('internal')]
));
$method->setReturnType(ManyToManyRelationshipPathDescriptor::class);

$parameter = new ParameterGenerator('pathKey');
@@ -1488,9 +1500,11 @@ private function generateGetManyToManyRelationshipDescriptorKeysCode(array $pivo
$method = new MethodGenerator('_getManyToManyRelationshipDescriptorKeys');
$method->setVisibility(AbstractMemberGenerator::VISIBILITY_PUBLIC);
$method->setReturnType('array');
$method->setDocBlock('Returns the list of keys supported for many to many relationships');
$method->getDocBlock()->setTag(new GenericTag('internal'));
$method->getDocBlock()->setTag(new ReturnTag('string[]'));
$method->setDocBlock(new DocBlockGenerator(
'Returns the list of keys supported for many to many relationships',
null,
[new GenericTag('internal'), new ReturnTag('string[]')]
));

$keys = [];
foreach ($pivotTableMethodsDescriptors as $pivotTableMethodsDescriptor) {
@@ -1664,7 +1678,7 @@ private function generateGetForeignKeys(array $fks): MethodGenerator
$method = new MethodGenerator('getForeignKeys');
$method->setVisibility(AbstractMemberGenerator::VISIBILITY_PROTECTED);
$method->setStatic(true);
$method->setDocBlock('Internal method used to retrieve the list of foreign keys attached to this bean.');
$method->setDocBlock(new DocBlockGenerator('Internal method used to retrieve the list of foreign keys attached to this bean.'));
$method->setReturnType(ForeignKeys::class);

$parameter = new ParameterGenerator('tableName');
16 changes: 9 additions & 7 deletions src/Utils/DirectForeignKeyMethodDescriptor.php
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@
use TheCodingMachine\TDBM\Utils\Annotation;
use Zend\Code\Generator\AbstractMemberGenerator;
use Zend\Code\Generator\DocBlock\Tag\ReturnTag;
use Zend\Code\Generator\DocBlockGenerator;
use Zend\Code\Generator\MethodGenerator;

/**
@@ -127,9 +128,11 @@ public function getCode() : array
$getter = new MethodGenerator($this->getName());

if ($this->hasLocalUniqueIndex()) {
$getter->setDocBlock(sprintf('Returns the %s pointing to this bean via the %s column.', $beanClass, implode(', ', $this->foreignKey->getUnquotedLocalColumns())));
$classType = '\\' . $this->beanNamespace . '\\' . $beanClass;
$getter->getDocBlock()->setTag(new ReturnTag([$classType . '|null']))->setWordWrap(false);
$getterDocBlock = new DocBlockGenerator(sprintf('Returns the %s pointing to this bean via the %s column.', $beanClass, implode(', ', $this->foreignKey->getUnquotedLocalColumns())));
$getterDocBlock->setTag([new ReturnTag([$classType . '|null'])]);
$getterDocBlock->setWordWrap(false);
$getter->setDocBlock($getterDocBlock);
$getter->setReturnType('?' . $classType);

$code = sprintf(
@@ -139,11 +142,10 @@ public function getCode() : array
$this->getFilters($this->foreignKey)
);
} else {
$getter->setDocBlock(sprintf('Returns the list of %s pointing to this bean via the %s column.', $beanClass, implode(', ', $this->foreignKey->getUnquotedLocalColumns())));
$getter->getDocBlock()->setTag(new ReturnTag([
$beanClass . '[]',
'\\' . AlterableResultIterator::class
]))->setWordWrap(false);
$getterDocBlock = new DocBlockGenerator(sprintf('Returns the list of %s pointing to this bean via the %s column.', $beanClass, implode(', ', $this->foreignKey->getUnquotedLocalColumns())));
$getterDocBlock->setTag(new ReturnTag([$beanClass . '[]', '\\' . AlterableResultIterator::class]));
$getterDocBlock->setWordWrap(false);
$getter->setDocBlock($getterDocBlock);
$getter->setReturnType(AlterableResultIterator::class);

$code = sprintf(
5 changes: 3 additions & 2 deletions src/Utils/ObjectBeanPropertyDescriptor.php
Original file line number Diff line number Diff line change
@@ -10,6 +10,7 @@
use TheCodingMachine\TDBM\Utils\Annotation\AnnotationParser;
use TheCodingMachine\TDBM\Utils\Annotation;
use Zend\Code\Generator\AbstractMemberGenerator;
use Zend\Code\Generator\DocBlockGenerator;
use Zend\Code\Generator\MethodGenerator;
use Zend\Code\Generator\ParameterGenerator;

@@ -159,7 +160,7 @@ public function getGetterSetterCode(): array
$referencedBeanName = $this->namingStrategy->getBeanClassName($this->foreignKey->getForeignTableName());

$getter = new MethodGenerator($getterName);
$getter->setDocBlock('Returns the ' . $referencedBeanName . ' object bound to this object via the ' . implode(' and ', $this->foreignKey->getUnquotedLocalColumns()) . ' column.');
$getter->setDocBlock(new DocBlockGenerator('Returns the ' . $referencedBeanName . ' object bound to this object via the ' . implode(' and ', $this->foreignKey->getUnquotedLocalColumns()) . ' column.'));

/*$types = [ $referencedBeanName ];
if ($isNullable) {
@@ -177,7 +178,7 @@ public function getGetterSetterCode(): array
}

$setter = new MethodGenerator($setterName);
$setter->setDocBlock('The setter for the ' . $referencedBeanName . ' object bound to this object via the ' . implode(' and ', $this->foreignKey->getUnquotedLocalColumns()) . ' column.');
$setter->setDocBlock(new DocBlockGenerator('The setter for the ' . $referencedBeanName . ' object bound to this object via the ' . implode(' and ', $this->foreignKey->getUnquotedLocalColumns()) . ' column.'));

$setter->setParameter(new ParameterGenerator('object', ($isNullable ? '?' : '') . $this->beanNamespace . '\\' . $referencedBeanName));

35 changes: 23 additions & 12 deletions src/Utils/PivotTableMethodsDescriptor.php
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@
use Doctrine\DBAL\Schema\Column;
use Doctrine\DBAL\Schema\ForeignKeyConstraint;
use Doctrine\DBAL\Schema\Table;
use Zend\Code\Generator\DocBlockGenerator;
use function implode;
use function sprintf;
use TheCodingMachine\TDBM\Utils\Annotation\AnnotationParser;
@@ -199,39 +200,49 @@ public function getCode() : array
$localTableName = var_export($this->remoteFk->getLocalTableName(), true);

$getter = new MethodGenerator($this->getName());
$getter->setDocBlock(sprintf('Returns the list of %s associated to this bean via the %s pivot table.', $remoteBeanName, $this->pivotTable->getName()));
$getter->getDocBlock()->setTag(new ReturnTag([ $fqcnRemoteBeanName.'[]' ]))->setWordWrap(false);
$getterDocBlock = new DocBlockGenerator(sprintf('Returns the list of %s associated to this bean via the %s pivot table.', $remoteBeanName, $this->pivotTable->getName()));
$getterDocBlock->setTag(new ReturnTag([ $fqcnRemoteBeanName.'[]' ]));
$getterDocBlock->setWordWrap(false);
$getter->setDocBlock($getterDocBlock);
$getter->setReturnType('array');
$getter->setBody(sprintf('return $this->_getRelationships(%s);', $pathKey));


$adder = new MethodGenerator('add'.$singularName);
$adder->setDocBlock(sprintf('Adds a relationship with %s associated to this bean via the %s pivot table.', $remoteBeanName, $this->pivotTable->getName()));
$adder->getDocBlock()->setTag(new ParamTag($variableName, [ $fqcnRemoteBeanName ]))->setWordWrap(false);
$adderDocBlock = new DocBlockGenerator(sprintf('Adds a relationship with %s associated to this bean via the %s pivot table.', $remoteBeanName, $this->pivotTable->getName()));
$adderDocBlock->setTag(new ParamTag($variableName, [ $fqcnRemoteBeanName ]));
$adderDocBlock->setWordWrap(false);
$adder->setDocBlock($adderDocBlock);
$adder->setReturnType('void');
$adder->setParameter(new ParameterGenerator($variableName, $fqcnRemoteBeanName));
$adder->setBody(sprintf('$this->addRelationship(%s, $%s);', $localTableName, $variableName));

$remover = new MethodGenerator('remove'.$singularName);
$remover->setDocBlock(sprintf('Deletes the relationship with %s associated to this bean via the %s pivot table.', $remoteBeanName, $this->pivotTable->getName()));
$remover->getDocBlock()->setTag(new ParamTag($variableName, [ $fqcnRemoteBeanName ]))->setWordWrap(false);
$removerDocBlock = new DocBlockGenerator(sprintf('Deletes the relationship with %s associated to this bean via the %s pivot table.', $remoteBeanName, $this->pivotTable->getName()));
$removerDocBlock->setTag(new ParamTag($variableName, [ $fqcnRemoteBeanName ]));
$removerDocBlock->setWordWrap(false);
$remover->setDocBlock($removerDocBlock);
$remover->setReturnType('void');
$remover->setParameter(new ParameterGenerator($variableName, $fqcnRemoteBeanName));
$remover->setBody(sprintf('$this->_removeRelationship(%s, $%s);', $localTableName, $variableName));

$has = new MethodGenerator('has'.$singularName);
$has->setDocBlock(sprintf('Returns whether this bean is associated with %s via the %s pivot table.', $remoteBeanName, $this->pivotTable->getName()));
$has->getDocBlock()->setTag(new ParamTag($variableName, [ $fqcnRemoteBeanName ]))->setWordWrap(false);
$has->getDocBlock()->setTag(new ReturnTag([ 'bool' ]));
$hasDocBlock = new DocBlockGenerator(sprintf('Returns whether this bean is associated with %s via the %s pivot table.', $remoteBeanName, $this->pivotTable->getName()));
$hasDocBlock->setTag(new ParamTag($variableName, [ $fqcnRemoteBeanName ]));
$hasDocBlock->setTag(new ReturnTag([ 'bool' ]));
$hasDocBlock->setWordWrap(false);
$has->setDocBlock($hasDocBlock);
$has->setReturnType('bool');
$has->setParameter(new ParameterGenerator($variableName, $fqcnRemoteBeanName));
$has->setBody(sprintf('return $this->hasRelationship(%s, $%s);', $pathKey, $variableName));

$setter = new MethodGenerator('set'.$pluralName);
$setter->setDocBlock(sprintf('Sets all relationships with %s associated to this bean via the %s pivot table.
$setterDocBlock = new DocBlockGenerator(sprintf('Sets all relationships with %s associated to this bean via the %s pivot table.
Exiting relationships will be removed and replaced by the provided relationships.', $remoteBeanName, $this->pivotTable->getName()));
$setter->getDocBlock()->setTag(new ParamTag($pluralVariableName, [ $fqcnRemoteBeanName.'[]' ]))->setWordWrap(false)->setWordWrap(false);
$setter->getDocBlock()->setTag(new ReturnTag([ 'void' ]));
$setterDocBlock->setTag(new ParamTag($pluralVariableName, [ $fqcnRemoteBeanName.'[]' ]));
$setterDocBlock->setTag(new ReturnTag([ 'void' ]));
$setterDocBlock->setWordWrap(false);
$setter->setDocBlock($setterDocBlock);
$setter->setReturnType('void');
$setter->setParameter(new ParameterGenerator($pluralVariableName, 'array'));
$setter->setBody(sprintf('$this->setRelationships(%s, $%s);', $pathKey, $pluralVariableName));
Loading