Skip to content

Commit f578b41

Browse files
committed
Fixing merge conflict
2 parents f450476 + b5ff431 commit f578b41

13 files changed

+288
-108
lines changed

.travis.yml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,20 @@ matrix:
88
include:
99
- php: 7.2
1010
env: PREFER_LOWEST="" DB=mysql RUN_PHPSTAN=1 RUN_CSCHECK=1 RUN_REQUIRECHECKER=1
11+
services:
12+
- mysql
1113
- php: 7.3
1214
env: PREFER_LOWEST="" DB=mysql NO_WEAKREF=1
15+
services:
16+
- mysql
1317
- php: 7.1
1418
env: PREFER_LOWEST="" DB=mysql COVERALLS=true
19+
services:
20+
- mysql
1521
- php: 7.1
1622
env: PREFER_LOWEST="--prefer-lowest" DB=mysql
23+
services:
24+
- mysql
1725
- php: 7.1
1826
env: PREFER_LOWEST="" DB=mysql8
1927
sudo: required
@@ -22,11 +30,11 @@ matrix:
2230
- php: 7.1
2331
env: PREFER_LOWEST="" DB=mariadb
2432
addons:
25-
mariadb: '10.0'
33+
mariadb: '10.3'
2634
- php: 7.1
2735
env: PREFER_LOWEST="--prefer-lowest" DB=mariadb
2836
addons:
29-
mariadb: '10.0'
37+
mariadb: '10.3'
3038
- php: 7.1
3139
env: PREFER_LOWEST="--prefer-lowest" DB=oracle PHPUNITFILE="-c phpunit.oracle.xml"
3240
sudo: required

composer.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"ramsey/uuid": "^3.7",
3636
"doctrine/annotations": "^1.6",
3737
"zendframework/zend-code": "^3.3.1",
38+
"psr/container": "^1",
3839
"ext-PDO": "*",
3940
"ext-json": "*",
4041
"ext-hash": "*",

src/AlterableResultIterator.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,9 @@ public function take($offset, $limit)
222222
*/
223223
public function count()
224224
{
225+
if ($this->resultIterator instanceof \Countable && $this->alterations->count() === 0) {
226+
return $this->resultIterator->count();
227+
}
225228
return count($this->toArray());
226229
}
227230

src/InnerResultIterator.php

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ protected function executeQuery(): void
102102
$this->fetchStarted = true;
103103
}
104104

105+
private $count = null;
105106
/**
106107
* Counts found records (this is the number of records fetched, taking into account the LIMIT and OFFSET settings).
107108
*
@@ -113,30 +114,14 @@ public function count()
113114
return $this->count;
114115
}
115116

116-
if ($this->tdbmService->getConnection()->getDatabasePlatform() instanceof MySqlPlatform) {
117+
if ($this->fetchStarted && $this->tdbmService->getConnection()->getDatabasePlatform() instanceof MySqlPlatform) {
117118
// Optimisation: we don't need a separate "count" SQL request in MySQL.
118-
return $this->getRowCountViaRowCountFunction();
119-
} else {
120-
return $this->getRowCountViaSqlQuery();
121-
}
122-
}
123-
124-
private $count = null;
125-
126-
/**
127-
* Get the row count from the rowCount function (only works with MySQL)
128-
*/
129-
private function getRowCountViaRowCountFunction(): int
130-
{
131-
if (!$this->fetchStarted) {
132-
$this->executeQuery();
119+
$this->count = $this->statement->rowCount();
120+
return $this->count;
133121
}
134-
135-
$this->count = $this->statement->rowCount();
136-
return $this->count;
122+
return $this->getRowCountViaSqlQuery();
137123
}
138124

139-
140125
/**
141126
* Makes a separate SQL query to compute the row count.
142127
* (not needed in MySQL)
@@ -147,7 +132,7 @@ private function getRowCountViaSqlQuery(): int
147132

148133
$this->logger->debug('Running count SQL request: '.$countSql);
149134

150-
$this->count = $this->tdbmService->getConnection()->fetchColumn($countSql, $this->parameters);
135+
$this->count = (int) $this->tdbmService->getConnection()->fetchColumn($countSql, $this->parameters);
151136
return $this->count;
152137
}
153138

src/Utils/BeanDescriptor.php

Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,20 @@ class BeanDescriptor implements BeanDescriptorInterface
107107
*/
108108
private $configuration;
109109

110-
public function __construct(Table $table, string $beanNamespace, string $generatedBeanNamespace, string $daoNamespace, string $generatedDaoNamespace, SchemaAnalyzer $schemaAnalyzer, Schema $schema, TDBMSchemaAnalyzer $tdbmSchemaAnalyzer, NamingStrategyInterface $namingStrategy, AnnotationParser $annotationParser, CodeGeneratorListenerInterface $codeGeneratorListener, ConfigurationInterface $configuration)
111-
{
110+
public function __construct(
111+
Table $table,
112+
string $beanNamespace,
113+
string $generatedBeanNamespace,
114+
string $daoNamespace,
115+
string $generatedDaoNamespace,
116+
SchemaAnalyzer $schemaAnalyzer,
117+
Schema $schema,
118+
TDBMSchemaAnalyzer $tdbmSchemaAnalyzer,
119+
NamingStrategyInterface $namingStrategy,
120+
AnnotationParser $annotationParser,
121+
CodeGeneratorListenerInterface $codeGeneratorListener,
122+
ConfigurationInterface $configuration
123+
) {
112124
$this->table = $table;
113125
$this->beanNamespace = $beanNamespace;
114126
$this->generatedBeanNamespace = $generatedBeanNamespace;
@@ -360,7 +372,7 @@ private function getDirectForeignKeysDescriptors(): array
360372
$descriptors = [];
361373

362374
foreach ($fks as $fk) {
363-
$descriptors[] = new DirectForeignKeyMethodDescriptor($fk, $this->table, $this->namingStrategy, $this->annotationParser);
375+
$descriptors[] = new DirectForeignKeyMethodDescriptor($fk, $this->table, $this->namingStrategy, $this->annotationParser, $this->beanNamespace);
364376
}
365377

366378
return $descriptors;
@@ -795,27 +807,35 @@ public function generateDaoPhpCode(): ?FileGenerator
795807
$class->addMethodFromGenerator($findAllMethod);
796808
}
797809

798-
if (count($primaryKeyColumns) === 1) {
799-
$primaryKeyColumn = $primaryKeyColumns[0];
800-
$primaryKeyPhpType = TDBMDaoGenerator::dbalTypeToPhpType($this->table->getColumn($primaryKeyColumn)->getType());
810+
if (count($primaryKeyColumns) > 0) {
811+
$lazyLoadingParameterName = 'lazyLoading';
812+
$parameters = [];
813+
$parametersTag = [];
814+
$primaryKeyFilter = [];
815+
816+
foreach ($primaryKeyColumns as $primaryKeyColumn) {
817+
if ($primaryKeyColumn === $lazyLoadingParameterName) {
818+
throw new TDBMException('Primary Column name `' . $lazyLoadingParameterName . '` is not allowed.');
819+
}
820+
$phpType = TDBMDaoGenerator::dbalTypeToPhpType($this->table->getColumn($primaryKeyColumn)->getType());
821+
$parameters[] = new ParameterGenerator($primaryKeyColumn, $phpType);
822+
$parametersTag[] = new ParamTag($primaryKeyColumn, [$phpType]);
823+
$primaryKeyFilter[] = "'$primaryKeyColumn' => \$$primaryKeyColumn";
824+
}
825+
$parameters[] = new ParameterGenerator($lazyLoadingParameterName, 'bool', false);
826+
$parametersTag[] = new ParamTag($lazyLoadingParameterName, ['bool'], 'If set to true, the object will not be loaded right away. Instead, it will be loaded when you first try to access a method of the object.');
827+
$parametersTag[] = new ReturnTag(['\\'.$beanClassName]);
828+
$parametersTag[] = new ThrowsTag('\\'.TDBMException::class);
801829

802830
$getByIdMethod = new MethodGenerator(
803831
'getById',
804-
[
805-
new ParameterGenerator('id', $primaryKeyPhpType),
806-
new ParameterGenerator('lazyLoading', 'bool', false)
807-
],
832+
$parameters,
808833
MethodGenerator::FLAG_PUBLIC,
809-
"return \$this->tdbmService->findObjectByPk('$tableName', ['$primaryKeyColumn' => \$id], [], \$lazyLoading);",
834+
"return \$this->tdbmService->findObjectByPk('$tableName', [" . implode(', ', $primaryKeyFilter) . "], [], \$$lazyLoadingParameterName);",
810835
(new DocBlockGenerator(
811836
"Get $beanClassWithoutNameSpace specified by its ID (its primary key).",
812837
'If the primary key does not exist, an exception is thrown.',
813-
[
814-
new ParamTag('id', [$primaryKeyPhpType]),
815-
new ParamTag('lazyLoading', ['bool'], 'If set to true, the object will not be loaded right away. Instead, it will be loaded when you first try to access a method of the object.'),
816-
new ReturnTag(['\\'.$beanClassName]),
817-
new ThrowsTag('\\'.TDBMException::class)
818-
]
838+
$parametersTag
819839
))->setWordWrap(false)
820840
);
821841
$getByIdMethod->setReturnType($beanClassName);
@@ -1118,6 +1138,9 @@ private function generateFindByDaoCode(string $beanNamespace, string $beanClassN
11181138
}
11191139
}
11201140
}
1141+
usort($methods, static function (MethodGenerator $methodA, MethodGenerator $methodB) {
1142+
return $methodA->getName() <=> $methodB->getName();
1143+
});
11211144

11221145
return $methods;
11231146
}
@@ -1132,7 +1155,11 @@ private function removeDuplicateIndexes(array $indexes): array
11321155
{
11331156
$indexesByKey = [];
11341157
foreach ($indexes as $index) {
1135-
$indexesByKey[implode('__`__', $index->getUnquotedColumns())] = $index;
1158+
$key = implode('__`__', $index->getUnquotedColumns());
1159+
// Unique Index have precedence over non unique one
1160+
if (!isset($indexesByKey[$key]) || $index->isUnique()) {
1161+
$indexesByKey[$key] = $index;
1162+
}
11361163
}
11371164

11381165
return array_values($indexesByKey);
@@ -1175,9 +1202,10 @@ private function generateFindByDaoCodeForIndex(Index $index, string $beanNamespa
11751202
$parameters = [];
11761203
//$functionParameters = [];
11771204
$first = true;
1205+
/** @var AbstractBeanPropertyDescriptor $element */
11781206
foreach ($elements as $element) {
11791207
$parameter = new ParameterGenerator(ltrim($element->getVariableName(), '$'));
1180-
if (!$first) {
1208+
if (!$first && !($element->isCompulsory() && $index->isUnique())) {
11811209
$parameterType = '?';
11821210
//$functionParameter = '?';
11831211
} else {
@@ -1186,7 +1214,7 @@ private function generateFindByDaoCodeForIndex(Index $index, string $beanNamespa
11861214
}
11871215
$parameterType .= $element->getPhpType();
11881216
$parameter->setType($parameterType);
1189-
if (!$first) {
1217+
if (!$first && !($element->isCompulsory() && $index->isUnique())) {
11901218
$parameter->setDefaultValue(null);
11911219
}
11921220
//$functionParameter .= $element->getPhpType();
@@ -1224,7 +1252,7 @@ private function generateFindByDaoCodeForIndex(Index $index, string $beanNamespa
12241252
foreach ($columns as $localColumn => $foreignColumn) {
12251253
// TODO: a foreign key could point to another foreign key. In this case, there is no getter for the pointed column. We don't support this case.
12261254
$targetedElement = new ScalarBeanPropertyDescriptor($foreignTable, $foreignTable->getColumn($foreignColumn), $this->namingStrategy, $this->annotationParser);
1227-
if ($first) {
1255+
if ($first || $element->isCompulsory() && $index->isUnique()) {
12281256
// First parameter for index is not nullable
12291257
$filterArrayCode .= ' '.var_export($localColumn, true).' => '.$element->getVariableName().'->'.$targetedElement->getGetterName()."(),\n";
12301258
} else {

src/Utils/DirectForeignKeyMethodDescriptor.php

Lines changed: 66 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,30 @@ class DirectForeignKeyMethodDescriptor implements MethodDescriptorInterface
4040
* @var AnnotationParser
4141
*/
4242
private $annotationParser;
43+
/**
44+
* @var string
45+
*/
46+
private $beanNamespace;
4347

4448
/**
4549
* @param ForeignKeyConstraint $fk The foreign key pointing to our bean
4650
* @param Table $mainTable The main table that is pointed to
4751
* @param NamingStrategyInterface $namingStrategy
52+
* @param AnnotationParser $annotationParser
53+
* @param string $beanNamespace
4854
*/
49-
public function __construct(ForeignKeyConstraint $fk, Table $mainTable, NamingStrategyInterface $namingStrategy, AnnotationParser $annotationParser)
50-
{
55+
public function __construct(
56+
ForeignKeyConstraint $fk,
57+
Table $mainTable,
58+
NamingStrategyInterface $namingStrategy,
59+
AnnotationParser $annotationParser,
60+
string $beanNamespace
61+
) {
5162
$this->foreignKey = $fk;
5263
$this->mainTable = $mainTable;
5364
$this->namingStrategy = $namingStrategy;
5465
$this->annotationParser = $annotationParser;
66+
$this->beanNamespace = $beanNamespace;
5567
}
5668

5769
/**
@@ -100,23 +112,37 @@ public function useAlternativeName(): void
100112
public function getCode() : array
101113
{
102114
$beanClass = $this->getBeanClassName();
115+
$tdbmFk = ForeignKey::createFromFk($this->foreignKey);
103116

104117
$getter = new MethodGenerator($this->getName());
105-
$getter->setDocBlock(sprintf('Returns the list of %s pointing to this bean via the %s column.', $beanClass, implode(', ', $this->foreignKey->getUnquotedLocalColumns())));
106-
$getter->getDocBlock()->setTag(new ReturnTag([
107-
$beanClass.'[]',
108-
'\\'.AlterableResultIterator::class
109-
]));
110-
$getter->setReturnType(AlterableResultIterator::class);
111118

112-
$tdbmFk = ForeignKey::createFromFk($this->foreignKey);
119+
if ($this->hasLocalUniqueIndex()) {
120+
$getter->setDocBlock(sprintf('Returns the %s pointing to this bean via the %s column.', $beanClass, implode(', ', $this->foreignKey->getUnquotedLocalColumns())));
121+
$classType = '\\' . $this->beanNamespace . '\\' . $beanClass;
122+
$getter->getDocBlock()->setTag(new ReturnTag([$classType . '|null']));
123+
$getter->setReturnType('?' . $classType);
113124

114-
$code = sprintf(
115-
'return $this->retrieveManyToOneRelationshipsStorage(%s, %s, %s);',
116-
var_export($this->foreignKey->getLocalTableName(), true),
117-
var_export($tdbmFk->getCacheKey(), true),
118-
$this->getFilters($this->foreignKey)
119-
);
125+
$code = sprintf(
126+
'return $this->retrieveManyToOneRelationshipsStorage(%s, %s, %s)->first();',
127+
var_export($this->foreignKey->getLocalTableName(), true),
128+
var_export($tdbmFk->getCacheKey(), true),
129+
$this->getFilters($this->foreignKey)
130+
);
131+
} else {
132+
$getter->setDocBlock(sprintf('Returns the list of %s pointing to this bean via the %s column.', $beanClass, implode(', ', $this->foreignKey->getUnquotedLocalColumns())));
133+
$getter->getDocBlock()->setTag(new ReturnTag([
134+
$beanClass . '[]',
135+
'\\' . AlterableResultIterator::class
136+
]));
137+
$getter->setReturnType(AlterableResultIterator::class);
138+
139+
$code = sprintf(
140+
'return $this->retrieveManyToOneRelationshipsStorage(%s, %s, %s);',
141+
var_export($this->foreignKey->getLocalTableName(), true),
142+
var_export($tdbmFk->getCacheKey(), true),
143+
$this->getFilters($this->foreignKey)
144+
);
145+
}
120146

121147
$getter->setBody($code);
122148

@@ -144,6 +170,25 @@ private function getFilters(ForeignKeyConstraint $fk) : string
144170
return $parametersCode;
145171
}
146172

173+
/**
174+
* Check if the ForeignKey have an unique index
175+
*
176+
* @return bool
177+
*/
178+
private function hasLocalUniqueIndex(): bool
179+
{
180+
foreach ($this->getForeignKey()->getLocalTable()->getIndexes() as $index) {
181+
if (
182+
$index->isUnique()
183+
&& count($index->getUnquotedColumns()) === count($this->getForeignKey()->getUnquotedLocalColumns())
184+
&& !array_diff($index->getUnquotedColumns(), $this->getForeignKey()->getUnquotedLocalColumns()) // Check for permuted columns too
185+
) {
186+
return true;
187+
}
188+
}
189+
return false;
190+
}
191+
147192
/**
148193
* Returns an array of classes that needs a "use" for this method.
149194
*
@@ -181,17 +226,21 @@ public function getJsonSerializeCode() : string
181226
$class = $this->getBeanClassName();
182227
$variableName = '$' . TDBMDaoGenerator::toVariableName($class);
183228
$getter = $this->getName();
184-
$code = <<<PHP
229+
if ($this->hasLocalUniqueIndex()) {
230+
$code = "\$array['$index'] = (\$object = \$this->$getter()) ? \$object->$format : null;";
231+
} else {
232+
$code = <<<PHP
185233
\$array['$index'] = array_map(function ($class $variableName) {
186234
return ${variableName}->$format;
187235
}, \$this->$getter()->toArray());
188236
PHP;
237+
}
189238
if (!$isIncluded) {
190239
$code = preg_replace('(\n)', '\0 ', $code);
191240
$code = <<<PHP
192241
if (!\$stopRecursion) {
193242
$code
194-
};
243+
}
195244
PHP;
196245
}
197246
return $code;

0 commit comments

Comments
 (0)