From cbffb53f77e2be034d4d1b31a59e8ed6f5458b06 Mon Sep 17 00:00:00 2001 From: Andreas Leathley Date: Wed, 15 Dec 2021 16:21:45 +0100 Subject: [PATCH] Refactor + upgrade dependencies - Improve repositories generation - Upgrade to squirrelphp/queries 1.2 - Upgrade squirrelphp/debug usage --- bin/squirrel_repositories_generate | 24 +- composer.json | 8 +- src/Builder/DeleteEntries.php | 2 +- src/Builder/MultiSelectEntriesFreeform.php | 2 +- src/Builder/MultiUpdateEntriesFreeform.php | 2 +- src/Builder/SelectEntries.php | 36 +- src/Builder/UpdateEntries.php | 2 +- src/Generate/FindClassesWithAttribute.php | 89 ++--- src/Generate/RepositoriesGenerateCommand.php | 317 ++++++------------ src/MultiRepositoryReadOnly.php | 86 ++--- src/MultiRepositoryWriteable.php | 4 +- src/RepositoryReadOnly.php | 56 ++-- src/RepositoryWriteable.php | 26 +- src/Transaction.php | 10 +- tests/RepositoriesGenerateCommandTest.php | 97 +++++- .../TicketRepositoryBuilderReadOnly.php | 8 +- .../TicketRepositoryBuilderWriteable.php | 8 +- tests/TestEntities/NonRepository.php | 4 - .../NonRepositoryWithAttributeInUse.php | 4 - tests/TestEntities/UserAddress.php | 3 - tests/TestEntities/UserName.php | 23 ++ tests/TestEntities/UserNickname.php | 25 ++ .../UserAddressInvalid.php | 3 - .../UserAddressInvalid.php | 3 - .../UserAddressInvalid.php | 3 - .../UserAddressInvalid.php | 3 - .../TestEntitiesNoType/UserAddressInvalid.php | 3 - tests/TestEntitiesWithConflict/.gitignore | 3 + tests/TestEntitiesWithConflict/User.php | 34 ++ 29 files changed, 464 insertions(+), 424 deletions(-) create mode 100644 tests/TestEntities/UserName.php create mode 100644 tests/TestEntities/UserNickname.php create mode 100644 tests/TestEntitiesWithConflict/.gitignore create mode 100644 tests/TestEntitiesWithConflict/User.php diff --git a/bin/squirrel_repositories_generate b/bin/squirrel_repositories_generate index 88c9d5b..0694d02 100755 --- a/bin/squirrel_repositories_generate +++ b/bin/squirrel_repositories_generate @@ -24,7 +24,13 @@ $inputDefinition->addOption(new InputOption( 'verbose', 'v', InputOption::VALUE_NONE, - 'Verbose mode, showing all generated repositories' // Description + 'Verbose mode, showing all generated repositories and possible file conflicts' // Description +)); +$inputDefinition->addOption(new InputOption( + 'force', + 'f', + InputOption::VALUE_NONE, + 'Force creating repositories and gitignore files, even when there are file conflicts' // Description )); $inputDefinition->addOption(new InputOption( 'source-dir', @@ -36,18 +42,28 @@ $inputDefinition->addOption(new InputOption( $input = new ArgvInput(null, $inputDefinition); $srcDirectories = $input->getOption('source-dir'); $isVerbose = $input->getOption('verbose'); +$isForce = $input->getOption('force'); // Execute command to generate repositories $cmd = new \Squirrel\Entities\Generate\RepositoriesGenerateCommand( $srcDirectories, + $isForce, new \Squirrel\Entities\Generate\PHPFilesInDirectoryGetContents() ); -$log = $cmd(); +[$logRepositories, $logConflicts] = $cmd(); // Show detailed log if ($isVerbose === true) { - echo implode("\n", $log); + echo implode("\n", $logRepositories); } // Show summary -echo "\n" . count($log) . ' entities found for which repositories were generated.' . "\n"; \ No newline at end of file +if (\count($logConflicts) > 0 && $isForce === false) { + echo "\n" . count($logConflicts) . ' file conflicts found, where existing files (which seem to not have been generated by this command) would be overwritten. Re-run with --force if you want to overwrite these files, or with --verbose to see which files have conflicts.' . "\n"; +} else { + echo "\n" . count($logRepositories) . ' entities found for which repositories were generated.' . "\n"; + + if (\count($logConflicts) > 0) { + echo count($logConflicts) . ' file conflicts found, those files were overwritten because of --force flag.' . "\n"; + } +} \ No newline at end of file diff --git a/composer.json b/composer.json index 90f4fa1..9722232 100644 --- a/composer.json +++ b/composer.json @@ -23,7 +23,7 @@ "php": ">=8.0", "symfony/console": "^5.0|^6.0", "symfony/finder": "^5.0|^6.0", - "squirrelphp/queries": "^0.13" + "squirrelphp/queries": "^1.2" }, "require-dev": { "bamarni/composer-bin-plugin": "^1.3", @@ -61,9 +61,9 @@ "phpunit": "vendor/bin/phpunit --colors=always", "phpunit_clover": "vendor/bin/phpunit --coverage-text --coverage-clover build/logs/clover.xml", "coverage": "XDEBUG_MODE=coverage vendor/bin/phpunit --coverage-html tests/_reports", - "phpcs": "vendor/bin/phpcs --standard=ruleset.xml --extensions=php --cache=.phpcs-cache --ignore=tests/TestEntities/NonRepositoryWithAttributeInUse.php --colors src bin tests", - "phpcsd": "vendor/bin/phpcs -s --standard=ruleset.xml --extensions=php --cache=.phpcs-cache --ignore=tests/TestEntities/NonRepositoryWithAttributeInUse.php --colors src bin tests", - "phpcsfix": "vendor/bin/phpcbf --standard=ruleset.xml --extensions=php --cache=.phpcs-cache --ignore=tests/TestEntities/NonRepositoryWithAttributeInUse.php src bin tests", + "phpcs": "vendor/bin/phpcs --standard=ruleset.xml --extensions=php --cache=.phpcs-cache --ignore=tests/TestEntities/NonRepositoryWithAttributeInUse.php,tests/TestEntities/UserNickname.php --colors src bin tests", + "phpcsd": "vendor/bin/phpcs -s --standard=ruleset.xml --extensions=php --cache=.phpcs-cache --ignore=tests/TestEntities/NonRepositoryWithAttributeInUse.php,tests/TestEntities/UserNickname.php --colors src bin tests", + "phpcsfix": "vendor/bin/phpcbf --standard=ruleset.xml --extensions=php --cache=.phpcs-cache --ignore=tests/TestEntities/NonRepositoryWithAttributeInUse.php,tests/TestEntities/UserNickname.php src bin tests", "binupdate": "@composer bin all update --ansi", "bininstall": "@composer bin all install --ansi" } diff --git a/src/Builder/DeleteEntries.php b/src/Builder/DeleteEntries.php index 0f5a753..94d9056 100644 --- a/src/Builder/DeleteEntries.php +++ b/src/Builder/DeleteEntries.php @@ -73,9 +73,9 @@ private function accidentalDeleteAllCheck(): void if (\count($this->where) === 0 && $this->confirmNoWhere !== true) { throw Debug::createException( DBInvalidOptionException::class, - [BuilderInterface::class], 'No restricting "where" arguments defined for DELETE ' . 'and no override confirmation with "confirmNoWhereRestrictions" call', + ignoreClasses: [BuilderInterface::class], ); } } diff --git a/src/Builder/MultiSelectEntriesFreeform.php b/src/Builder/MultiSelectEntriesFreeform.php index b1a2802..edc73eb 100644 --- a/src/Builder/MultiSelectEntriesFreeform.php +++ b/src/Builder/MultiSelectEntriesFreeform.php @@ -166,9 +166,9 @@ private function makeSureBadPracticeWasConfirmed(): void if ($this->confirmBadPractice !== true) { throw Debug::createException( DBInvalidOptionException::class, - [BuilderInterface::class], 'No confirmation that freeform queries are bad practice - ' . 'call "confirmFreeformQueriesAreNotRecommended" with "OK" to confirm the freeform query', + ignoreClasses: [BuilderInterface::class], ); } } diff --git a/src/Builder/MultiUpdateEntriesFreeform.php b/src/Builder/MultiUpdateEntriesFreeform.php index 14e1b41..1c0502a 100644 --- a/src/Builder/MultiUpdateEntriesFreeform.php +++ b/src/Builder/MultiUpdateEntriesFreeform.php @@ -102,9 +102,9 @@ private function makeSureBadPracticeWasConfirmed(): void if ($this->confirmBadPractice !== true) { throw Debug::createException( DBInvalidOptionException::class, - [BuilderInterface::class], 'No confirmation that freeform queries are bad practice - ' . 'call "confirmFreeformQueriesAreNotRecommended" with "OK" to confirm the freeform query', + ignoreClasses: [BuilderInterface::class], ); } } diff --git a/src/Builder/SelectEntries.php b/src/Builder/SelectEntries.php index 780251e..42f55c0 100644 --- a/src/Builder/SelectEntries.php +++ b/src/Builder/SelectEntries.php @@ -9,50 +9,50 @@ /** * Select query builder as a fluent object - build query and return object(s) or flattened fields * + * Properties are only protected so we can extend it with generated repositories + * * @implements \IteratorAggregate */ class SelectEntries implements BuilderInterface, \IteratorAggregate { use FlattenedFieldsWithTypeTrait; - private RepositoryReadOnlyInterface $repository; - /** * @var array WHERE restrictions in query */ - private array $where = []; + protected array $where = []; /** * @var array ORDER BY sorting in query */ - private array $orderBy = []; + protected array $orderBy = []; /** * @var int How many results should be returned */ - private int $limitTo = 0; + protected int $limitTo = 0; /** * @var int Where in the result set to start (so many entries are skipped) */ - private int $startAt = 0; + protected int $startAt = 0; /** * @var bool Whether the SELECT query should block the scanned entries */ - private bool $blocking = false; + protected bool $blocking = false; /** * @var string[] Only retrieve some of the fields of the objects, default is to return all */ - private array $fields = []; + protected array $fields = []; - public function __construct(RepositoryReadOnlyInterface $repository) - { - $this->repository = $repository; + public function __construct( + protected RepositoryReadOnlyInterface $repository, + ) { } - public function field(string $onlyGetThisField): self + public function field(string $onlyGetThisField): static { $this->fields = [$onlyGetThisField]; return $this; @@ -61,7 +61,7 @@ public function field(string $onlyGetThisField): self /** * @param string[] $onlyGetTheseFields */ - public function fields(array $onlyGetTheseFields): self + public function fields(array $onlyGetTheseFields): static { $this->fields = $onlyGetTheseFields; return $this; @@ -70,7 +70,7 @@ public function fields(array $onlyGetTheseFields): self /** * @param array $whereClauses */ - public function where(array $whereClauses): self + public function where(array $whereClauses): static { $this->where = $whereClauses; return $this; @@ -79,7 +79,7 @@ public function where(array $whereClauses): self /** * @param array|string $orderByClauses */ - public function orderBy($orderByClauses): self + public function orderBy(array|string $orderByClauses): static { if (\is_string($orderByClauses)) { $orderByClauses = [$orderByClauses]; @@ -89,19 +89,19 @@ public function orderBy($orderByClauses): self return $this; } - public function startAt(int $startAtNumber): self + public function startAt(int $startAtNumber): static { $this->startAt = $startAtNumber; return $this; } - public function limitTo(int $numberOfEntries): self + public function limitTo(int $numberOfEntries): static { $this->limitTo = $numberOfEntries; return $this; } - public function blocking(bool $active = true): self + public function blocking(bool $active = true): static { $this->blocking = $active; return $this; diff --git a/src/Builder/UpdateEntries.php b/src/Builder/UpdateEntries.php index ee035ff..c10a8a5 100644 --- a/src/Builder/UpdateEntries.php +++ b/src/Builder/UpdateEntries.php @@ -77,9 +77,9 @@ public function writeAndReturnAffectedNumber(): int if (\count($this->where) === 0 && $this->confirmNoWhere !== true) { throw Debug::createException( DBInvalidOptionException::class, - [BuilderInterface::class], 'No restricting "where" arguments defined for UPDATE ' . 'and no override confirmation with "confirmNoWhereRestrictions" call', + ignoreClasses: [BuilderInterface::class], ); } diff --git a/src/Generate/FindClassesWithAttribute.php b/src/Generate/FindClassesWithAttribute.php index 16c0731..4356f0b 100644 --- a/src/Generate/FindClassesWithAttribute.php +++ b/src/Generate/FindClassesWithAttribute.php @@ -15,7 +15,6 @@ public function __invoke(string $fileContents): array // Stores the most recent namespace, import class name and classname in loop $namespace = ''; - $importClassName = ''; // Get all PHP tokens from a file $tokens = \token_get_all($fileContents); @@ -24,70 +23,61 @@ public function __invoke(string $fileContents): array $namespaceStarted = false; $classNameStarted = false; $useImportStarted = false; + $attributeStarted = false; $attributeUseFound = false; - // Go through all PHP tokens + // Go through all PHP tokens in the file foreach ($tokens as $key => $token) { - // "use" name started, so collect all parts until we reach the end of the name + // Skip all whitespace tokens + if ($token[0] === T_WHITESPACE) { + continue; + } + + // Look for usage of our attributes in use imports if ($useImportStarted === true) { - // @codeCoverageIgnoreStart - // In PHP8 the whole class namespace is its own token - if (PHP_VERSION_ID >= 80000 && $token[0] === T_NAME_QUALIFIED) { - $importClassName = $token[1]; - } elseif (PHP_VERSION_ID < 80000 && \in_array($token[0], [T_STRING, T_NS_SEPARATOR], true)) { - // In PHP 7.4 the namespace is made of string and namespace separators - $importClassName .= $token[1]; - } elseif ($token[0] === T_WHITESPACE) { // Ignore whitespace, can be before or after the class name - } else { // Every other token indicates that we have reached the end of the name - $namespaceStarted = false; - - // We have found the attribute namespace - so there can be entities in this file - if ( - $importClassName === 'Squirrel\\Entities\\Attribute' - || $importClassName === 'Squirrel\\Entities\\Attribute\\Entity' - || $importClassName === 'Squirrel\\Entities\\Attribute\\Field' - ) { + if ($token[0] === T_NAME_QUALIFIED) { + if ($this->isAttributeUsage($token[1])) { + $attributeUseFound = true; + } + } + + $useImportStarted = false; + } + + // Look for usage of our attributes in attributes (usually those would be fully qualified, unusual but possible) + if ($attributeStarted === true) { + if ($token[0] === T_NAME_FULLY_QUALIFIED || $token[0] === T_NAME_QUALIFIED) { + if ($this->isAttributeUsage($token[1])) { $attributeUseFound = true; } } - // @codeCoverageIgnoreEnd + + $attributeStarted = false; } - // "namespace" name started, so collect all parts until we reach the end of the name + // Record a new namespace to correctly assign the namespace for found classes if ($namespaceStarted === true) { - // @codeCoverageIgnoreStart - // In PHP8 the whole class namespace can be its own token - if (PHP_VERSION_ID >= 80000 && $token[0] === T_NAME_QUALIFIED) { + if ($token[0] === T_NAME_QUALIFIED || $token[0] === T_STRING) { $namespace = $token[1]; - $namespaceStarted = false; - } elseif (\in_array($token[0], [T_STRING, T_NS_SEPARATOR], true)) { - // In PHP 7.4 the namespace is made of string and namespace separators - // In PHP8 the namespace can be one string - $namespace .= $token[1]; - } elseif ($token[0] === T_WHITESPACE) { // Ignore whitespace, can be before or after the namespace - } else { // Every other token indicates that we have reached the end of the name - $namespaceStarted = false; } - // @codeCoverageIgnoreEnd + + $namespaceStarted = false; } - // "class" name started, so collect all parts until we reach the end of the name + // Record any classes if we have found attributes if ($classNameStarted === true) { - // Only a string is expected for the class name if ($token[0] === T_STRING) { if (\strlen($token[1]) > 0 && $attributeUseFound === true) { $classes[] = [$namespace, $token[1]]; } - } elseif ($token[0] === T_WHITESPACE) { // Ignore whitespace, can be before or after the class name - } else { // Every other token indicates that we have reached the end of the name - $classNameStarted = false; } + + $classNameStarted = false; } // "use" token - everything coming after this has to be checked for the attribute classes if ($token[0] === T_USE) { $useImportStarted = true; - $importClassName = ''; } // "namespace" token - start collecting the namespace name @@ -96,13 +86,30 @@ public function __invoke(string $fileContents): array $namespace = ''; } - // "class" token - start collecting the class name which is being defined + // "class" token - collect the class name if attributes were found earlier if ($token[0] === T_CLASS) { $classNameStarted = true; } + + // "attribute" token - look for fully qualified attribute + if ($token[0] === T_ATTRIBUTE) { + $attributeStarted = true; + } } // Return list of the classes found return $classes; } + + private function isAttributeUsage(string $class): bool + { + // Remove any preceding slashes + $class = \ltrim($class, '\\'); + + if (\str_starts_with($class, 'Squirrel\\Entities\\Attribute')) { + return true; + } + + return false; + } } diff --git a/src/Generate/RepositoriesGenerateCommand.php b/src/Generate/RepositoriesGenerateCommand.php index 15bd78c..14312b0 100644 --- a/src/Generate/RepositoriesGenerateCommand.php +++ b/src/Generate/RepositoriesGenerateCommand.php @@ -10,8 +10,6 @@ class RepositoriesGenerateCommand { private FindClassesWithAttribute $findClassesWithAttribute; - /** @var string[] */ - private array $sourceCodeDirectories; private array $repositoryPhpFileBlueprint = [ 'ReadOnly' => <<<'EOD' */ - class SelectIterator implements \Squirrel\Queries\Builder\BuilderInterface, \Iterator + class SelectIterator extends \Squirrel\Entities\Builder\SelectIterator { - private \Squirrel\Entities\Builder\SelectIterator $iteratorInstance; - - public function __construct(\Squirrel\Entities\Builder\SelectIterator $iterator) - { - $this->iteratorInstance = $iterator; - } - public function current(): \{namespaceOfEntity}\{classOfEntity} { - $entry = $this->iteratorInstance->current(); + $entry = parent::current(); if ($entry instanceof \{namespaceOfEntity}\{classOfEntity}) { return $entry; @@ -73,110 +64,24 @@ public function current(): \{namespaceOfEntity}\{classOfEntity} throw new \LogicException('Unexpected type encountered - wrong repository might be configured: ' . \get_class($entry)); } - - public function next(): void - { - $this->iteratorInstance->next(); - } - - public function key(): int - { - return $this->iteratorInstance->key(); - } - - public function valid(): bool - { - return $this->iteratorInstance->valid(); - } - - public function rewind(): void - { - $this->iteratorInstance->rewind(); - } - - public function clear(): void - { - $this->iteratorInstance->clear(); - } } /** * This class exists to have proper type hints about the object(s) returned in the - * getEntries and getOneEntry functions. All calls are delegated to the - * SelectEntries class - because of the builder pattern we cannot extend SelectEntries - * (because then returning self would return that class instead of this extended class) - * so we instead imitate it. This way the implementation in SelectEntries can change - * and this generated class has no ties to how it "works" or how the repository is used. + * getEntries and getOneEntry functions. The heavy lifting is done by the + * SelectEntries class * * @implements \IteratorAggregate */ - class SelectEntries implements \Squirrel\Queries\Builder\BuilderInterface, \IteratorAggregate + class SelectEntries extends \Squirrel\Entities\Builder\SelectEntries { - private \Squirrel\Entities\Builder\SelectEntries $selectImplementation; - - public function __construct(\Squirrel\Entities\RepositoryReadOnlyInterface $repository) - { - $this->selectImplementation = new \Squirrel\Entities\Builder\SelectEntries($repository); - } - - public function field(string $onlyGetThisField): self - { - $this->selectImplementation->field($onlyGetThisField); - return $this; - } - - /** - * @param string[] $onlyGetTheseFields - */ - public function fields(array $onlyGetTheseFields): self - { - $this->selectImplementation->fields($onlyGetTheseFields); - return $this; - } - - /** - * @param array $whereClauses - */ - public function where(array $whereClauses): self - { - $this->selectImplementation->where($whereClauses); - return $this; - } - - /** - * @param array|string $orderByClauses - */ - public function orderBy(array|string $orderByClauses): self - { - $this->selectImplementation->orderBy($orderByClauses); - return $this; - } - - public function startAt(int $startAtNumber): self - { - $this->selectImplementation->startAt($startAtNumber); - return $this; - } - - public function limitTo(int $numberOfEntries): self - { - $this->selectImplementation->limitTo($numberOfEntries); - return $this; - } - - public function blocking(bool $active = true): self - { - $this->selectImplementation->blocking($active); - return $this; - } - /** * @return \{namespaceOfEntity}\{classOfEntity}[] */ public function getAllEntries(): array { /** @var \{namespaceOfEntity}\{classOfEntity}[] $entries */ - $entries = $this->selectImplementation->getAllEntries(); + $entries = parent::getAllEntries(); foreach ($entries as $entry) { if (!($entry instanceof \{namespaceOfEntity}\{classOfEntity})) { @@ -189,7 +94,7 @@ public function getAllEntries(): array public function getOneEntry(): ?\{namespaceOfEntity}\{classOfEntity} { - $entry = $this->selectImplementation->getOneEntry(); + $entry = parent::getOneEntry(); if ($entry instanceof \{namespaceOfEntity}\{classOfEntity} || $entry === null) { return $entry; @@ -198,49 +103,16 @@ public function getOneEntry(): ?\{namespaceOfEntity}\{classOfEntity} throw new \LogicException('Unexpected type encountered - wrong repository might be configured: ' . \get_class($entry)); } - /** - * @return array - */ - public function getFlattenedFields(): array - { - return $this->selectImplementation->getFlattenedFields(); - } - - /** - * @return int[] - */ - public function getFlattenedIntegerFields(): array - { - return $this->selectImplementation->getFlattenedIntegerFields(); - } - - /** - * @return float[] - */ - public function getFlattenedFloatFields(): array - { - return $this->selectImplementation->getFlattenedFloatFields(); - } - - /** - * @return string[] - */ - public function getFlattenedStringFields(): array - { - return $this->selectImplementation->getFlattenedStringFields(); - } - - /** - * @return bool[] - */ - public function getFlattenedBooleanFields(): array - { - return $this->selectImplementation->getFlattenedBooleanFields(); - } - public function getIterator(): SelectIterator { - return new SelectIterator($this->selectImplementation->getIterator()); + return new SelectIterator($this->repository, [ + 'where' => $this->where, + 'order' => $this->orderBy, + 'fields' => $this->fields, + 'limit' => $this->limitTo, + 'offset' => $this->startAt, + 'lock' => $this->blocking, + ]); } } } @@ -302,30 +174,31 @@ public function delete(): \Squirrel\Entities\Builder\DeleteEntries , ]; - private PHPFilesInDirectoryGetContents $PHPFilesInDirectoryGetContents; - - /** - * @param string[] $sourceCodeDirectories - */ public function __construct( - array $sourceCodeDirectories, - PHPFilesInDirectoryGetContents $PHPFilesInDirectoryGetContents, + /** @var string[] */ + private array $sourceCodeDirectories, + private bool $forceFileCreation, + private PHPFilesInDirectoryGetContents $PHPFilesInDirectoryGetContents, ) { $this->findClassesWithAttribute = new FindClassesWithAttribute(); - $this->sourceCodeDirectories = $sourceCodeDirectories; - $this->PHPFilesInDirectoryGetContents = $PHPFilesInDirectoryGetContents; } /** - * @return string[] + * @return array{list, list} */ public function __invoke(): array { - $log = []; + /** @var list $logRepositories */ + $logRepositories = []; + /** @var list $logConflicts */ + $logConflicts = []; // Initialize entity processor to find repository config $entityProcessor = new EntityProcessor(); + /** @var array $filesToCreate */ + $filesToCreate = []; + // Saves the files per path for which to create a .gitignore file $gitignoreFilesForPaths = []; @@ -337,14 +210,8 @@ public function __invoke(): array $classes = $this->findClassesWithAttribute->__invoke($fileData['contents']); // Go through the possible entity classes - foreach ($classes as $class) { - // Divvy up the namespace and the class name - $namespace = $class[0]; - $className = $class[1]; - - /** - * @psalm-var class-string $fullClassName - */ + foreach ($classes as [$namespace, $className]) { + /** @var class-string $fullClassName */ $fullClassName = $namespace . '\\' . $className; // Get repository config as object from attributes @@ -352,86 +219,90 @@ public function __invoke(): array // Repository config found - this is definitely an entity if (isset($repositoryConfig)) { - $log[] = 'Entity found: ' . $fullClassName; - - $gitignoreFilesForPaths[$fileData['path']][] = $this->generateRepositoryFile( - $namespace, - $className, - $fileData, - 'ReadOnly', - ); - - $gitignoreFilesForPaths[$fileData['path']][] = $this->generateRepositoryFile( - $namespace, - $className, - $fileData, - 'Writeable', - ); + $logRepositories[] = 'Entity found: ' . $fullClassName; + + foreach (['ReadOnly', 'Writeable'] as $type) { + $phpFilename = $this->generateRepositoryFilename($fileData['filename'], $type); + + $filesToCreate[$fileData['path'] . '/' . $phpFilename] = $this->repositoryFileContentsFillInBlueprint( + $this->repositoryPhpFileBlueprint[$type], + $namespace, + $className, + ); + + $gitignoreFilesForPaths[$fileData['path']][] = $phpFilename; + } } } } } - // Go through all paths where we created repository files - $this->createGitignoreFiles($gitignoreFilesForPaths); + if (\count($gitignoreFilesForPaths) > 0) { + foreach ($gitignoreFilesForPaths as $path => $files) { + $filesToCreate[$path . '/.gitignore'] = $this->generateGitignoreContents($files); + } + } - return $log; - } + $conflictDetected = false; - private function createGitignoreFiles(array $gitignoreFilesForPaths): void - { - // Go through all paths where we created repository files - foreach ($gitignoreFilesForPaths as $path => $files) { - // Make sure all files are unique / no duplicates - $files = \array_unique($files); - - if (\count($files) > 0) { - // Ignore the .gitignore file in entity directories, that way both the gitignore - // and the repositories will be ignored by VCS - $gitignoreLines = [ - '.gitignore', - ]; - - // Add each repository file to .gitignore - foreach ($files as $filename) { - $gitignoreLines[] = $filename; + foreach ($filesToCreate as $fullFilename => $fileContents) { + if (\file_exists($fullFilename)) { + $currentContents = \file_get_contents($fullFilename); + + // @codeCoverageIgnoreStart + if ($currentContents === false) { + throw new \RuntimeException('Cannot read file: ' . $fullFilename); } + // @codeCoverageIgnoreEnd + + // Only assume no conflict if we have an explicit generated by string in the file + if (!\str_contains($currentContents, 'Generated by Squirrel\Entities\Generate\RepositoriesGenerateCommand')) { + $conflictDetected = true; - $gitignoreFileContents = \implode("\n", $gitignoreLines); - - // Save .gitignore file in the appropriate path if there was a change - if ( - !\file_exists($path . '/.gitignore') - || \file_get_contents($path . '/.gitignore') !== $gitignoreFileContents - ) { - \file_put_contents( - $path . '/.gitignore', - $gitignoreFileContents, - ); + $logConflicts[] = 'Possible conflict detected for: ' . $fullFilename; } + + // Remove files which exist in the exact same way already + if ($currentContents === $fileContents) { + unset($filesToCreate[$fullFilename]); + } + } + } + + if ($conflictDetected === false || $this->forceFileCreation === true) { + foreach ($filesToCreate as $fullFilename => $fileContents) { + \file_put_contents($fullFilename, $fileContents); } } + + return [$logRepositories, $logConflicts]; } - private function generateRepositoryFile(string $namespace, string $className, array $fileData, string $type): string + private function generateGitignoreContents(array $files): string { - $phpFilename = \str_replace('.php', '', $fileData['filename']) . 'Repository' . $type . '.php'; + // Make sure all files are unique / no duplicates + $files = \array_unique($files); - // Compile file name and file contents for repository - $fullPhpFilename = $fileData['path'] . '/' . $phpFilename; - $fileContents = $this->repositoryFileContentsFillInBlueprint( - $this->repositoryPhpFileBlueprint[$type], - $namespace, - $className, - ); + // Ignore the .gitignore file in entity directories, that way both the gitignore + // and the repositories will be ignored by VCS + $gitignoreLines = [ + '# Generated by Squirrel\Entities\Generate\RepositoriesGenerateCommand', + '# Any changes will be overwritten when running that command again', + '# => DO NOT EDIT, DO NOT COMMIT TO VCS', + '.gitignore', + ]; - // Save repository PHP file - only if it changed or doesn't exist yet - if (!\file_exists($fullPhpFilename) || \file_get_contents($fullPhpFilename) !== $fileContents) { - \file_put_contents($fullPhpFilename, $fileContents); + // Add each repository file to .gitignore + foreach ($files as $filename) { + $gitignoreLines[] = $filename; } - // Add PHP file to list for which we want to create a .gitignore file - return $phpFilename; + return \implode("\n", $gitignoreLines); + } + + private function generateRepositoryFilename(string $filename, string $type): string + { + return \str_replace('.php', '', $filename) . 'Repository' . $type . '.php'; } private function repositoryFileContentsFillInBlueprint( diff --git a/src/MultiRepositoryReadOnly.php b/src/MultiRepositoryReadOnly.php index 4b32e0a..e4507ae 100644 --- a/src/MultiRepositoryReadOnly.php +++ b/src/MultiRepositoryReadOnly.php @@ -64,9 +64,9 @@ public function select(array $query): MultiRepositorySelectQueryInterface } catch (DBException $e) { throw Debug::createException( \get_class($e), - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], $e->getMessage(), - $e->getPrevious(), + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], + previousException: $e->getPrevious(), ); } } @@ -78,9 +78,9 @@ public function fetch(MultiRepositorySelectQueryInterface $selectQuery): ?array } catch (DBException $e) { throw Debug::createException( \get_class($e), - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], $e->getMessage(), - $e->getPrevious(), + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], + previousException: $e->getPrevious(), ); } @@ -98,9 +98,9 @@ public function clear(MultiRepositorySelectQueryInterface $selectQuery): void } catch (DBException $e) { throw Debug::createException( \get_class($e), - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], $e->getMessage(), - $e->getPrevious(), + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], + previousException: $e->getPrevious(), ); } } @@ -110,8 +110,8 @@ public function fetchOne(array $query): ?array if (isset($query['limit']) && $query['limit'] !== 1) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Row limit cannot be set for fetchOne query: ' . Debug::sanitizeData($query), + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -139,9 +139,9 @@ public function fetchAll(array $query): array } catch (DBException $e) { throw Debug::createException( \get_class($e), - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], $e->getMessage(), - $e->getPrevious(), + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], + previousException: $e->getPrevious(), ); } @@ -187,8 +187,8 @@ protected function processOptions(array $validOptions, array $options, bool $wri if (!isset($validOptions[$optKey])) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Unknown option key ' . Debug::sanitizeData($optKey), + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -206,9 +206,9 @@ protected function processOptions(array $validOptions, array $options, bool $wri if (!\is_array($optVal)) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Option key ' . Debug::sanitizeData($optKey) . ' had a non-array value: ' . Debug::sanitizeData($optVal), + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } break; @@ -221,8 +221,8 @@ protected function processOptions(array $validOptions, array $options, bool $wri if (!isset($sanitizedOptions['repositories']) || \count($sanitizedOptions['repositories']) === 0) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'No repositories specified', + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -239,8 +239,8 @@ protected function processOptions(array $validOptions, array $options, bool $wri ) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'No "where" definitions', + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -248,8 +248,8 @@ protected function processOptions(array $validOptions, array $options, bool $wri if (isset($validOptions['fields']) && \count($sanitizedOptions['fields']) === 0) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'No "fields" definition', + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -257,8 +257,8 @@ protected function processOptions(array $validOptions, array $options, bool $wri if (isset($validOptions['query']) && \strlen($sanitizedOptions['query']) === 0) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'No "query" definition', + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -273,8 +273,8 @@ protected function processOptions(array $validOptions, array $options, bool $wri if (!\is_scalar($value)) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Non-scalar "parameters" definition', + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -321,16 +321,16 @@ protected function processOptions(array $validOptions, array $options, bool $wri if (!\is_string($name) || \strpos($name, '.') !== false) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Invalid "repositories" key definition: ' . Debug::sanitizeData($name), + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } elseif ($class instanceof RepositoryBuilderReadOnlyInterface) { // Make sure the repository is writeable if we are doing a writing query if ($writing === true && !($class instanceof RepositoryBuilderWriteableInterface)) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Non-writeable "repositories" object definition for writing operation', + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -356,8 +356,8 @@ protected function processOptions(array $validOptions, array $options, bool $wri if (isset($dbInstance) && $dbClass !== $dbInstance) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Repositories have different database connections, combined query is not possible', + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -365,9 +365,9 @@ protected function processOptions(array $validOptions, array $options, bool $wri } catch (\ReflectionException $e) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Repository configuration could not be retrieved through reflection, ' . 'repository class not as expected: ' . $e->getMessage(), + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } } elseif ($class instanceof RepositoryReadOnlyInterface) { @@ -375,8 +375,8 @@ protected function processOptions(array $validOptions, array $options, bool $wri if ($writing === true && !($class instanceof RepositoryWriteableInterface)) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Non-writeable "repositories" object definition for writing operation', + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -397,8 +397,8 @@ protected function processOptions(array $validOptions, array $options, bool $wri if (isset($dbInstance) && $dbClass !== $dbInstance) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Repositories have different database connections, combined query is not possible', + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -406,18 +406,18 @@ protected function processOptions(array $validOptions, array $options, bool $wri } catch (\ReflectionException $e) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Repository configuration could not be retrieved through reflection, ' . 'repository class not as expected: ' . $e->getMessage(), + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } } else { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Invalid repository specified, does not implement ' . 'RepositoryReadOnlyInterface or RepositoryBuilderReadOnlyInterface: ' . Debug::sanitizeData($sanitizedOptions['repositories']), + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -439,9 +439,9 @@ protected function processOptions(array $validOptions, array $options, bool $wri } else { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Repositories did not contain a valid database connection' . Debug::sanitizeData($sanitizedOptions['repositories']), + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -590,8 +590,8 @@ protected function buildFreeform(string $query, array $tableName, array $objectT if (\strpos($query, ':') !== false) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Invalid "query" definition, unresolved colons remain', + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -625,8 +625,8 @@ private function buildFieldSelection( if (!\is_string($name)) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Invalid "fields" definition, key is not a string: ' . Debug::sanitizeData($name), + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -634,9 +634,9 @@ private function buildFieldSelection( if (!\is_string($field)) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Invalid "fields" definition, value for ' . Debug::sanitizeData($name) . ' is not a string: ' . Debug::sanitizeData($field), + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -644,9 +644,9 @@ private function buildFieldSelection( if (\strpos($name, ':') !== false) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Invalid "fields" definition, name ' . Debug::sanitizeData($name) . ' contains a colon', + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -662,9 +662,9 @@ private function buildFieldSelection( if (!isset($fieldParts[1]) || !isset($objectToTableFields[$fieldParts[0]][$fieldParts[1]])) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Invalid "fields" definition, unknown field name: ' . Debug::sanitizeData($field), + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -724,9 +724,9 @@ private function buildFieldSelection( if (\strpos($field, ':') !== false) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Invalid "fields" definition, unresolved colons: ' . Debug::sanitizeData($field), + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -789,8 +789,8 @@ private function processSelectResult( default: throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Unknown casting for object variable ' . Debug::sanitizeData($key), + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } } @@ -818,9 +818,9 @@ protected function preprocessJoins(array $tables, array $tableNames, array $obje if (!\is_string($expression)) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Invalid "tables" / table join definition, expression is not a string: ' . Debug::sanitizeData($expression), + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -830,9 +830,9 @@ protected function preprocessJoins(array $tables, array $tableNames, array $obje if (!isset($tableNames[$expression])) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Invalid "tables" / table join definition, alias not found: ' . Debug::sanitizeData($expression), + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -864,10 +864,10 @@ protected function preprocessJoins(array $tables, array $tableNames, array $obje if (\strpos($expression, ':') !== false) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Invalid "tables" / table join definition, ' . 'unconverted objects/table names found in expression: ' . Debug::sanitizeData($expression), + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -903,9 +903,9 @@ protected function preprocessWhere(array $whereOptions, array $objectToTableFiel if (!\is_string($expression)) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Invalid "where" definition, expression is not a string: ' . Debug::sanitizeData($expression), + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -920,9 +920,9 @@ protected function preprocessWhere(array $whereOptions, array $objectToTableFiel if (!isset($fieldParts[1]) || !isset($objectToTableFields[$fieldParts[0]][$fieldParts[1]])) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Invalid "where" definition, field name not found: ' . Debug::sanitizeData($expression), + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -946,9 +946,9 @@ protected function preprocessWhere(array $whereOptions, array $objectToTableFiel if (\strpos($expression, ':') !== false) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Invalid "where" definition, unresolved colons remain in expression: ' . Debug::sanitizeData($expression), + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } } @@ -984,9 +984,9 @@ private function preprocessGroup(array $groupByOptions, array $objectToTableFiel if (!\is_string($expression)) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Invalid "group" / group by definition, expression is not a string: ' . Debug::sanitizeData($expression), + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -999,17 +999,17 @@ private function preprocessGroup(array $groupByOptions, array $objectToTableFiel if (!isset($objectToTableFields[$fieldParts[0]][$fieldParts[1]])) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Invalid "group" / group by definition, field name not found in any repository: ' . Debug::sanitizeData($expression) . ' within ' . Debug::sanitizeData($groupByOptions), + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } } else { // Freestyle expression - not allowed throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Invalid "group" / group by definition, no variables are allowed in expression: ' . Debug::sanitizeData($expression), + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -1040,9 +1040,9 @@ protected function preprocessOrder(array $orderOptions, array $objectToTableFiel if (!\is_string($expression)) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Invalid "order" / order by definition, expression is not a string: ' . Debug::sanitizeData($expression), + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -1072,9 +1072,9 @@ protected function preprocessOrder(array $orderOptions, array $objectToTableFiel if (\strpos($expression, ':') !== false) { throw Debug::createException( DBInvalidOptionException::class, - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], 'Invalid "order" / order by definition, unconverted object names found in expression: ' . Debug::sanitizeData($expression), + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], ); } diff --git a/src/MultiRepositoryWriteable.php b/src/MultiRepositoryWriteable.php index b5af275..5b7a464 100644 --- a/src/MultiRepositoryWriteable.php +++ b/src/MultiRepositoryWriteable.php @@ -35,9 +35,9 @@ public function update(array $repositories, string $query, array $parameters = [ } catch (DBException $e) { throw Debug::createException( \get_class($e), - [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], $e->getMessage(), - $e->getPrevious(), + ignoreClasses: [MultiRepositoryReadOnlyInterface::class, BuilderInterface::class], + previousException: $e->getPrevious(), ); } } diff --git a/src/RepositoryReadOnly.php b/src/RepositoryReadOnly.php index cfe16ff..c94f48d 100644 --- a/src/RepositoryReadOnly.php +++ b/src/RepositoryReadOnly.php @@ -69,9 +69,9 @@ public function count(array $query): int } catch (DBException $e) { throw Debug::createException( \get_class($e), - [RepositoryReadOnlyInterface::class, BuilderInterface::class], $e->getMessage(), - $e->getPrevious(), + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], + previousException: $e->getPrevious(), ); } @@ -96,9 +96,9 @@ public function select(array $query): RepositorySelectQueryInterface } catch (DBException $e) { throw Debug::createException( \get_class($e), - [RepositoryReadOnlyInterface::class, BuilderInterface::class], $e->getMessage(), - $e->getPrevious(), + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], + previousException: $e->getPrevious(), ); } } @@ -114,9 +114,9 @@ public function fetch(RepositorySelectQueryInterface $selectQuery): ?object } catch (DBException $e) { throw Debug::createException( \get_class($e), - [RepositoryReadOnlyInterface::class, BuilderInterface::class], $e->getMessage(), - $e->getPrevious(), + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], + previousException: $e->getPrevious(), ); } } @@ -131,9 +131,9 @@ public function clear(RepositorySelectQueryInterface $selectQuery): void } catch (DBException $e) { throw Debug::createException( \get_class($e), - [RepositoryReadOnlyInterface::class, BuilderInterface::class], $e->getMessage(), - $e->getPrevious(), + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], + previousException: $e->getPrevious(), ); } } @@ -143,8 +143,8 @@ public function fetchOne(array $query): ?object if (isset($query['limit']) && $query['limit'] !== 1) { throw Debug::createException( DBInvalidOptionException::class, - [RepositoryReadOnlyInterface::class, BuilderInterface::class], 'Row limit cannot be set for fetchOne query: ' . Debug::sanitizeData($query), + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -177,9 +177,9 @@ public function fetchAll(array $query): array } catch (DBException $e) { throw Debug::createException( \get_class($e), - [RepositoryReadOnlyInterface::class, BuilderInterface::class], $e->getMessage(), - $e->getPrevious(), + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], + previousException: $e->getPrevious(), ); } @@ -204,9 +204,9 @@ public function fetchAllAndFlatten(array $query): array } catch (DBException $e) { throw Debug::createException( \get_class($e), - [RepositoryReadOnlyInterface::class, BuilderInterface::class], $e->getMessage(), - $e->getPrevious(), + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], + previousException: $e->getPrevious(), ); } @@ -235,8 +235,8 @@ protected function validateQueryOptions(array $validOptions, array $options): ar if (!isset($validOptions[$optKey])) { throw Debug::createException( DBInvalidOptionException::class, - [RepositoryReadOnlyInterface::class, BuilderInterface::class], 'Unknown option key ' . Debug::sanitizeData($optKey), + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -251,9 +251,9 @@ protected function validateQueryOptions(array $validOptions, array $options): ar if (!\is_array($optVal)) { throw Debug::createException( DBInvalidOptionException::class, - [RepositoryReadOnlyInterface::class, BuilderInterface::class], 'Option key ' . Debug::sanitizeData($optKey) . ' had a non-array value: ' . Debug::sanitizeData($optVal), + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], ); } break; @@ -283,8 +283,8 @@ private function prepareSelectQueryForLowerLayer(array $query): array if (!\is_string($fieldName)) { throw Debug::createException( DBInvalidOptionException::class, - [RepositoryReadOnlyInterface::class, BuilderInterface::class], 'Field name is not a string: ' . Debug::sanitizeData($fieldName), + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -344,8 +344,8 @@ private function booleanSettingValidation($shouldBeBoolean, string $settingName) ) { throw Debug::createException( DBInvalidOptionException::class, - [RepositoryReadOnlyInterface::class, BuilderInterface::class], $settingName . ' set to a non-boolean value: ' . Debug::sanitizeData($shouldBeBoolean), + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -357,8 +357,8 @@ private function compareRepositoryConfigMustBeEqual(RepositoryConfigInterface $c if ($config !== $this->config) { throw Debug::createException( DBInvalidOptionException::class, - [RepositoryReadOnlyInterface::class, BuilderInterface::class], 'Different repository used to fetch result than to do the query!', + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], ); } } @@ -454,9 +454,9 @@ protected function preprocessWhere(array $where): array if (!\is_string($whereName)) { throw Debug::createException( DBInvalidOptionException::class, - [RepositoryReadOnlyInterface::class, BuilderInterface::class], 'Invalid "where" definition, expression is not a string: ' . Debug::sanitizeData($whereName), + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -472,9 +472,9 @@ protected function preprocessWhere(array $where): array if (\strpos($whereName, ':') !== false) { throw Debug::createException( DBInvalidOptionException::class, - [RepositoryReadOnlyInterface::class, BuilderInterface::class], 'Unresolved colons in "where" clause: ' . Debug::sanitizeData($whereName), + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], ); } } else { // Key is a string, meaning normal field - value entry @@ -536,9 +536,9 @@ protected function castOneTableVariable($value, ?string $fieldName = null, bool if (!\is_null($value) && !\is_scalar($value)) { throw Debug::createException( DBInvalidOptionException::class, - [RepositoryReadOnlyInterface::class, BuilderInterface::class], 'Invalid value for field name: ' . Debug::sanitizeData($fieldName) . ' => ' . Debug::sanitizeData($value), + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -562,8 +562,8 @@ protected function castOneTableVariable($value, ?string $fieldName = null, bool if (!isset($this->objectTypes[$fieldName])) { throw Debug::createException( DBInvalidOptionException::class, - [RepositoryReadOnlyInterface::class, BuilderInterface::class], 'Unknown field name: ' . Debug::sanitizeData($fieldName), + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -573,9 +573,9 @@ protected function castOneTableVariable($value, ?string $fieldName = null, bool if ($this->objectTypesNullable[$fieldName] !== true) { throw Debug::createException( DBInvalidOptionException::class, - [RepositoryReadOnlyInterface::class, BuilderInterface::class], 'NULL value for non-nullable field name: ' . Debug::sanitizeData($fieldName), + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -609,8 +609,8 @@ protected function castOneTableVariable($value, ?string $fieldName = null, bool // Always throw an exception we if hit unchartered territory throw Debug::createException( DBInvalidOptionException::class, - [RepositoryReadOnlyInterface::class, BuilderInterface::class], 'Unknown casting for object variable: ' . Debug::sanitizeData($fieldName), + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -644,8 +644,8 @@ protected function convertNameToTable(string $fieldName): string if (!isset($this->objectToTableFields[$fieldName])) { throw Debug::createException( DBInvalidOptionException::class, - [RepositoryReadOnlyInterface::class, BuilderInterface::class], 'Unknown field name: ' . Debug::sanitizeData($fieldName), + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -677,9 +677,9 @@ protected function preprocessOrder(array $orderOptions): array if (!\is_string($expression)) { throw Debug::createException( DBInvalidOptionException::class, - [RepositoryReadOnlyInterface::class, BuilderInterface::class], 'Invalid "order" / order by definition, expression is not a string: ' . Debug::sanitizeData($expression), + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -701,9 +701,9 @@ protected function preprocessOrder(array $orderOptions): array if (\strpos($expression, ':') !== false) { throw Debug::createException( DBInvalidOptionException::class, - [RepositoryReadOnlyInterface::class, BuilderInterface::class], 'Unresolved colons in "order" / order by clause: ' . Debug::sanitizeData($expression), + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], ); } } @@ -752,8 +752,8 @@ protected function castObjVariable($value, string $fieldName) throw Debug::createException( DBInvalidOptionException::class, - [RepositoryReadOnlyInterface::class, BuilderInterface::class], 'Unknown casting for object variable: ' . Debug::sanitizeData($fieldName), + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], ); } } diff --git a/src/RepositoryWriteable.php b/src/RepositoryWriteable.php index 3fb0f47..ac1ff83 100644 --- a/src/RepositoryWriteable.php +++ b/src/RepositoryWriteable.php @@ -19,8 +19,8 @@ public function update(array $changes, array $where): int if (\count($changes) === 0) { throw Debug::createException( DBInvalidOptionException::class, - [RepositoryReadOnlyInterface::class, BuilderInterface::class], 'No "changes" / SET clause defined', + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -33,9 +33,9 @@ public function update(array $changes, array $where): int } catch (DBException $e) { throw Debug::createException( \get_class($e), - [RepositoryReadOnlyInterface::class, BuilderInterface::class], $e->getMessage(), - $e->getPrevious(), + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], + previousException: $e->getPrevious(), ); } } @@ -65,9 +65,9 @@ private function preprocessChanges(array $changes): array if (!\is_string($fieldName)) { throw Debug::createException( DBInvalidOptionException::class, - [RepositoryReadOnlyInterface::class, BuilderInterface::class], 'Invalid "changes" / SET definition, expression is not a string: ' . Debug::sanitizeData($fieldName), + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -86,9 +86,9 @@ private function preprocessChanges(array $changes): array if (\strpos($fieldName, ':') !== false) { throw Debug::createException( DBInvalidOptionException::class, - [RepositoryReadOnlyInterface::class, BuilderInterface::class], 'Unresolved colons in "changes" / SET clause: ' . Debug::sanitizeData($fieldName), + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], ); } } @@ -110,9 +110,9 @@ public function insert(array $fields, bool $returnInsertId = false): ?string if ($returnInsertId === true && \strlen($this->config->getAutoincrementField()) === 0) { throw Debug::createException( DBInvalidOptionException::class, - [RepositoryReadOnlyInterface::class, BuilderInterface::class], 'Insert ID requested but no autoincrement ID specified: ' . Debug::sanitizeData($fields), + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -142,9 +142,9 @@ public function insert(array $fields, bool $returnInsertId = false): ?string } catch (DBException $e) { throw Debug::createException( \get_class($e), - [RepositoryReadOnlyInterface::class, BuilderInterface::class], $e->getMessage(), - $e->getPrevious(), + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], + previousException: $e->getPrevious(), ); } @@ -162,9 +162,9 @@ public function insertOrUpdate(array $fields, array $indexFields = [], ?array $u if (!isset($fields[$fieldName])) { throw Debug::createException( DBInvalidOptionException::class, - [RepositoryReadOnlyInterface::class, BuilderInterface::class], 'Index field specified do not occur in data array: ' . Debug::sanitizeData($fieldName), + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], ); } @@ -198,9 +198,9 @@ public function insertOrUpdate(array $fields, array $indexFields = [], ?array $u } catch (DBException $e) { throw Debug::createException( \get_class($e), - [RepositoryReadOnlyInterface::class, BuilderInterface::class], $e->getMessage(), - $e->getPrevious(), + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], + previousException: $e->getPrevious(), ); } } @@ -216,9 +216,9 @@ public function delete(array $where): int } catch (DBException $e) { throw Debug::createException( \get_class($e), - [RepositoryReadOnlyInterface::class, BuilderInterface::class], $e->getMessage(), - $e->getPrevious(), + ignoreClasses: [RepositoryReadOnlyInterface::class, BuilderInterface::class], + previousException: $e->getPrevious(), ); } } diff --git a/src/Transaction.php b/src/Transaction.php index e0d8fda..8e12ee8 100644 --- a/src/Transaction.php +++ b/src/Transaction.php @@ -48,9 +48,9 @@ public static function withRepositories(array $repositories): self } catch (\ReflectionException $e) { throw Debug::createException( DBInvalidOptionException::class, - [Transaction::class], 'Base repository not found in builder repository via reflection. ' . 'Make sure you use officially supported classes', + ignoreClasses: [Transaction::class], ); } } @@ -65,9 +65,9 @@ public static function withRepositories(array $repositories): self } catch (\ReflectionException $e) { throw Debug::createException( DBInvalidOptionException::class, - [Transaction::class], 'Connection not found in base repository via reflection. ' . 'Make sure you use officially supported classes', + ignoreClasses: [Transaction::class], ); } @@ -75,8 +75,8 @@ public static function withRepositories(array $repositories): self if (isset($connection) && $connection !== $foundConnection) { throw Debug::createException( DBInvalidOptionException::class, - [TransactionInterface::class], 'Repositories have different database connections, transaction is not possible', + ignoreClasses: [TransactionInterface::class], ); } @@ -84,8 +84,8 @@ public static function withRepositories(array $repositories): self } else { // No base repository - meaning this class is invalid throw Debug::createException( DBInvalidOptionException::class, - [TransactionInterface::class], 'Invalid class specified to create transaction (not a repository)', + ignoreClasses: [TransactionInterface::class], ); } } @@ -94,8 +94,8 @@ public static function withRepositories(array $repositories): self if (!isset($connection)) { throw Debug::createException( DBInvalidOptionException::class, - [TransactionInterface::class], 'No repositories for transaction defined', + ignoreClasses: [TransactionInterface::class], ); } diff --git a/tests/RepositoriesGenerateCommandTest.php b/tests/RepositoriesGenerateCommandTest.php index 9e83bf3..d8e3e12 100644 --- a/tests/RepositoriesGenerateCommandTest.php +++ b/tests/RepositoriesGenerateCommandTest.php @@ -22,6 +22,8 @@ public function testGenerationAndValidRepositories(): void 'NonRepositoryWithAttributeInUse.php', 'User.php', 'UserAddress.php', + 'UserName.php', + 'UserNickname.php', ]; $sourceFinder = new Finder(); @@ -38,6 +40,7 @@ public function testGenerationAndValidRepositories(): void $repositoriesGenerator = new RepositoriesGenerateCommand( [__DIR__ . '/' . 'TestEntities'], + false, new PHPFilesInDirectoryGetContents(), ); @@ -54,6 +57,12 @@ public function testGenerationAndValidRepositories(): void 'UserAddress.php', 'UserAddressRepositoryReadOnly.php', 'UserAddressRepositoryWriteable.php', + 'UserName.php', + 'UserNameRepositoryReadOnly.php', + 'UserNameRepositoryWriteable.php', + 'UserNickname.php', + 'UserNicknameRepositoryReadOnly.php', + 'UserNicknameRepositoryWriteable.php', ]; $sourceFinder = new Finder(); @@ -68,9 +77,19 @@ public function testGenerationAndValidRepositories(): void // Make sure we have the same array contents / the same files $this->assertEqualsCanonicalizing($validFiles, $foundFiles); - $gitignoreContents = '.gitignore' . "\n" . - 'UserRepositoryReadOnly.php' . "\n" . 'UserRepositoryWriteable.php' . "\n" . - 'UserAddressRepositoryReadOnly.php' . "\n" . 'UserAddressRepositoryWriteable.php'; + $gitignoreContents = + '# Generated by Squirrel\Entities\Generate\RepositoriesGenerateCommand' . "\n" . + '# Any changes will be overwritten when running that command again' . "\n" . + '# => DO NOT EDIT, DO NOT COMMIT TO VCS' . "\n" . + '.gitignore' . "\n" . + 'UserRepositoryReadOnly.php' . "\n" . + 'UserRepositoryWriteable.php' . "\n" . + 'UserAddressRepositoryReadOnly.php' . "\n" . + 'UserAddressRepositoryWriteable.php' . "\n" . + 'UserNameRepositoryReadOnly.php' . "\n" . + 'UserNameRepositoryWriteable.php' . "\n" . + 'UserNicknameRepositoryReadOnly.php' . "\n" . + 'UserNicknameRepositoryWriteable.php'; // Check the .gitignore file contents, that we exclude the right files in the right order $this->assertEquals( @@ -104,6 +123,10 @@ public function testGenerationAndValidRepositories(): void require(__DIR__ . '/' . 'TestEntities/' . 'UserRepositoryWriteable.php'); require(__DIR__ . '/' . 'TestEntities/' . 'UserAddressRepositoryReadOnly.php'); require(__DIR__ . '/' . 'TestEntities/' . 'UserAddressRepositoryWriteable.php'); + require(__DIR__ . '/' . 'TestEntities/' . 'UserNameRepositoryReadOnly.php'); + require(__DIR__ . '/' . 'TestEntities/' . 'UserNameRepositoryWriteable.php'); + require(__DIR__ . '/' . 'TestEntities/' . 'UserNicknameRepositoryReadOnly.php'); + require(__DIR__ . '/' . 'TestEntities/' . 'UserNicknameRepositoryWriteable.php'); // Make sure all repository classes exist if (!\class_exists('Squirrel\Entities\Tests\TestEntities\UserRepositoryReadOnly', false)) { @@ -118,6 +141,18 @@ public function testGenerationAndValidRepositories(): void if (!\class_exists('Squirrel\Entities\Tests\TestEntities\UserAddressRepositoryWriteable', false)) { $this->assertEquals('', 'Squirrel\Entities\Tests\TestEntities\UserAddressRepositoryWriteable'); } + if (!\class_exists('Squirrel\Entities\Tests\TestEntities\UserNameRepositoryReadOnly', false)) { + $this->assertEquals('', 'Squirrel\Entities\Tests\TestEntities\UserNameRepositoryReadOnly'); + } + if (!\class_exists('Squirrel\Entities\Tests\TestEntities\UserNameRepositoryWriteable', false)) { + $this->assertEquals('', 'Squirrel\Entities\Tests\TestEntities\UserNameRepositoryWriteable'); + } + if (!\class_exists('Squirrel\Entities\Tests\TestEntities\UserNicknameRepositoryReadOnly', false)) { + $this->assertEquals('', 'Squirrel\Entities\Tests\TestEntities\UserNicknameRepositoryReadOnly'); + } + if (!\class_exists('Squirrel\Entities\Tests\TestEntities\UserNicknameRepositoryWriteable', false)) { + $this->assertEquals('', 'Squirrel\Entities\Tests\TestEntities\UserNicknameRepositoryWriteable'); + } if (!\class_exists('Squirrel\Entities\Builder\SquirrelEntitiesTestsTestEntitiesUser\SelectEntries', false)) { $this->assertEquals( '', @@ -183,6 +218,10 @@ public function testGenerationAndValidRepositories(): void @\unlink(__DIR__ . '/' . 'TestEntities/' . 'UserRepositoryWriteable.php'); @\unlink(__DIR__ . '/' . 'TestEntities/' . 'UserAddressRepositoryReadOnly.php'); @\unlink(__DIR__ . '/' . 'TestEntities/' . 'UserAddressRepositoryWriteable.php'); + @\unlink(__DIR__ . '/' . 'TestEntities/' . 'UserNameRepositoryReadOnly.php'); + @\unlink(__DIR__ . '/' . 'TestEntities/' . 'UserNameRepositoryWriteable.php'); + @\unlink(__DIR__ . '/' . 'TestEntities/' . 'UserNicknameRepositoryReadOnly.php'); + @\unlink(__DIR__ . '/' . 'TestEntities/' . 'UserNicknameRepositoryWriteable.php'); } public function testGenerationForInvalidBlobRepositories(): void @@ -207,6 +246,7 @@ public function testGenerationForInvalidBlobRepositories(): void $repositoriesGenerator = new RepositoriesGenerateCommand( [__DIR__ . '/' . 'TestEntitiesInvalidBlob'], + false, new PHPFilesInDirectoryGetContents(), ); @@ -236,6 +276,7 @@ public function testGenerationForNoTypeRepositories(): void $repositoriesGenerator = new RepositoriesGenerateCommand( [__DIR__ . '/' . 'TestEntitiesNoType'], + false, new PHPFilesInDirectoryGetContents(), ); @@ -265,6 +306,7 @@ public function testGenerationForInvalidNoEntityNameRepositories(): void $repositoriesGenerator = new RepositoriesGenerateCommand( [__DIR__ . '/' . 'TestEntitiesInvalidNoEntityName'], + false, new PHPFilesInDirectoryGetContents(), ); @@ -294,6 +336,7 @@ public function testGenerationForInvalidNoFieldNameRepositories(): void $repositoriesGenerator = new RepositoriesGenerateCommand( [__DIR__ . '/' . 'TestEntitiesInvalidNoFieldName'], + false, new PHPFilesInDirectoryGetContents(), ); @@ -323,10 +366,58 @@ public function testGenerationForInvalidFieldUnionTypeRepositories(): void $repositoriesGenerator = new RepositoriesGenerateCommand( [__DIR__ . '/' . 'TestEntitiesInvalidFieldUnionType'], + false, new PHPFilesInDirectoryGetContents(), ); // Execute the generator $repositoriesGenerator(); } + + public function testGenerationForConflictFiles(): void + { + $directory = __DIR__ . '/' . 'TestEntitiesWithConflict'; + + $validFiles = [ + '.gitignore', + 'User.php', + ]; + + $sourceFinder = new Finder(); + $sourceFinder->in($directory)->files()->sortByName()->ignoreDotFiles(false); + + $foundFiles = []; + + foreach ($sourceFinder as $file) { + $foundFiles[] = $file->getFilename(); + } + + // Make sure we have the same array contents / the same files + $this->assertEqualsCanonicalizing($validFiles, $foundFiles); + + $repositoriesGenerator = new RepositoriesGenerateCommand( + [$directory], + false, + new PHPFilesInDirectoryGetContents(), + ); + + [$logRepositories, $logConflicts] = $repositoriesGenerator(); + + // One repository found, and one conflict + $this->assertSame(1, \count($logRepositories)); + $this->assertSame(1, \count($logConflicts)); + + // Check to see that there are no additional files in the directory now + $sourceFinder = new Finder(); + $sourceFinder->in($directory)->files()->sortByName()->ignoreDotFiles(false); + + $foundFiles = []; + + foreach ($sourceFinder as $file) { + $foundFiles[] = $file->getFilename(); + } + + // Make sure we have the same array contents / the same files + $this->assertEqualsCanonicalizing($validFiles, $foundFiles); + } } diff --git a/tests/TestClasses/TicketRepositoryBuilderReadOnly.php b/tests/TestClasses/TicketRepositoryBuilderReadOnly.php index 1f3fbb9..7a3461c 100644 --- a/tests/TestClasses/TicketRepositoryBuilderReadOnly.php +++ b/tests/TestClasses/TicketRepositoryBuilderReadOnly.php @@ -9,11 +9,9 @@ class TicketRepositoryBuilderReadOnly implements RepositoryBuilderReadOnlyInterface { - private RepositoryReadOnlyInterface $repository; - - public function __construct(RepositoryReadOnlyInterface $repository) - { - $this->repository = $repository; + public function __construct( + private RepositoryReadOnlyInterface $repository, + ) { } public function select(): SelectEntries diff --git a/tests/TestClasses/TicketRepositoryBuilderWriteable.php b/tests/TestClasses/TicketRepositoryBuilderWriteable.php index 869aa5d..e90ee28 100644 --- a/tests/TestClasses/TicketRepositoryBuilderWriteable.php +++ b/tests/TestClasses/TicketRepositoryBuilderWriteable.php @@ -12,11 +12,9 @@ class TicketRepositoryBuilderWriteable extends TicketRepositoryBuilderReadOnly implements RepositoryBuilderWriteableInterface { - private RepositoryWriteableInterface $repository; - - public function __construct(RepositoryWriteableInterface $repository) - { - $this->repository = $repository; + public function __construct( + private RepositoryWriteableInterface $repository, + ) { parent::__construct($repository); } diff --git a/tests/TestEntities/NonRepository.php b/tests/TestEntities/NonRepository.php index e6fce98..0b966e2 100644 --- a/tests/TestEntities/NonRepository.php +++ b/tests/TestEntities/NonRepository.php @@ -2,12 +2,8 @@ namespace Squirrel\Entities\Tests\TestEntities; -use Squirrel\Entities\PopulatePropertiesWithIterableTrait; - class NonRepository { - use PopulatePropertiesWithIterableTrait; - private int $userId = 0; public function getUserId(): int diff --git a/tests/TestEntities/NonRepositoryWithAttributeInUse.php b/tests/TestEntities/NonRepositoryWithAttributeInUse.php index 8b9b59f..71ae027 100644 --- a/tests/TestEntities/NonRepositoryWithAttributeInUse.php +++ b/tests/TestEntities/NonRepositoryWithAttributeInUse.php @@ -2,15 +2,11 @@ namespace Squirrel\Entities\Tests\TestEntities; -use Squirrel\Entities\Attribute as SQL; use Squirrel\Entities\Attribute\Entity; use Squirrel\Entities\Attribute\Field; -use Squirrel\Entities\PopulatePropertiesWithIterableTrait; class NonRepositoryWithAttributeInUse { - use PopulatePropertiesWithIterableTrait; - private int $userId = 0; public function getUserId(): int diff --git a/tests/TestEntities/UserAddress.php b/tests/TestEntities/UserAddress.php index 0197f75..eda0f07 100644 --- a/tests/TestEntities/UserAddress.php +++ b/tests/TestEntities/UserAddress.php @@ -4,13 +4,10 @@ use Squirrel\Entities\Attribute\Entity; use Squirrel\Entities\Attribute\Field; -use Squirrel\Entities\PopulatePropertiesWithIterableTrait; #[Entity("users_address")] class UserAddress { - use PopulatePropertiesWithIterableTrait; - #[Field("user_id")] private int $userId = 0; diff --git a/tests/TestEntities/UserName.php b/tests/TestEntities/UserName.php new file mode 100644 index 0000000..a5a9c00 --- /dev/null +++ b/tests/TestEntities/UserName.php @@ -0,0 +1,23 @@ +userId; + } + + public function getUserName(): string + { + return $this->userName; + } +} diff --git a/tests/TestEntities/UserNickname.php b/tests/TestEntities/UserNickname.php new file mode 100644 index 0000000..d67ff71 --- /dev/null +++ b/tests/TestEntities/UserNickname.php @@ -0,0 +1,25 @@ +userId; + } + + public function getUserName(): string + { + return $this->userName; + } +} diff --git a/tests/TestEntitiesInvalidBlob/UserAddressInvalid.php b/tests/TestEntitiesInvalidBlob/UserAddressInvalid.php index 5734769..e474975 100644 --- a/tests/TestEntitiesInvalidBlob/UserAddressInvalid.php +++ b/tests/TestEntitiesInvalidBlob/UserAddressInvalid.php @@ -4,13 +4,10 @@ use Squirrel\Entities\Attribute\Entity; use Squirrel\Entities\Attribute\Field; -use Squirrel\Entities\PopulatePropertiesWithIterableTrait; #[Entity("users_address")] class UserAddressInvalid { - use PopulatePropertiesWithIterableTrait; - /** * An integer field cannot be a blob: (only strings can be a blob) */ diff --git a/tests/TestEntitiesInvalidFieldUnionType/UserAddressInvalid.php b/tests/TestEntitiesInvalidFieldUnionType/UserAddressInvalid.php index 7d83a9b..88eb00b 100644 --- a/tests/TestEntitiesInvalidFieldUnionType/UserAddressInvalid.php +++ b/tests/TestEntitiesInvalidFieldUnionType/UserAddressInvalid.php @@ -4,13 +4,10 @@ use Squirrel\Entities\Attribute\Entity; use Squirrel\Entities\Attribute\Field; -use Squirrel\Entities\PopulatePropertiesWithIterableTrait; #[Entity("dada")] class UserAddressInvalid { - use PopulatePropertiesWithIterableTrait; - /** * PHP8 union types are not allowed */ diff --git a/tests/TestEntitiesInvalidNoEntityName/UserAddressInvalid.php b/tests/TestEntitiesInvalidNoEntityName/UserAddressInvalid.php index c94b89d..d7c66da 100644 --- a/tests/TestEntitiesInvalidNoEntityName/UserAddressInvalid.php +++ b/tests/TestEntitiesInvalidNoEntityName/UserAddressInvalid.php @@ -4,7 +4,6 @@ use Squirrel\Entities\Attribute\Entity; use Squirrel\Entities\Attribute\Field; -use Squirrel\Entities\PopulatePropertiesWithIterableTrait; /** * An empty name is not allowed @@ -12,8 +11,6 @@ #[Entity("")] class UserAddressInvalid { - use PopulatePropertiesWithIterableTrait; - #[Field("user_id")] private int $userId = 0; diff --git a/tests/TestEntitiesInvalidNoFieldName/UserAddressInvalid.php b/tests/TestEntitiesInvalidNoFieldName/UserAddressInvalid.php index faa95eb..238847d 100644 --- a/tests/TestEntitiesInvalidNoFieldName/UserAddressInvalid.php +++ b/tests/TestEntitiesInvalidNoFieldName/UserAddressInvalid.php @@ -4,13 +4,10 @@ use Squirrel\Entities\Attribute\Entity; use Squirrel\Entities\Attribute\Field; -use Squirrel\Entities\PopulatePropertiesWithIterableTrait; #[Entity("users_address")] class UserAddressInvalid { - use PopulatePropertiesWithIterableTrait; - /** * An empty name is not allowed */ diff --git a/tests/TestEntitiesNoType/UserAddressInvalid.php b/tests/TestEntitiesNoType/UserAddressInvalid.php index a5fb0e4..d7cadef 100644 --- a/tests/TestEntitiesNoType/UserAddressInvalid.php +++ b/tests/TestEntitiesNoType/UserAddressInvalid.php @@ -4,13 +4,10 @@ use Squirrel\Entities\Attribute\Entity; use Squirrel\Entities\Attribute\Field; -use Squirrel\Entities\PopulatePropertiesWithIterableTrait; #[Entity("users_address")] class UserAddressInvalid { - use PopulatePropertiesWithIterableTrait; - /** * No property type defined - we need this */ diff --git a/tests/TestEntitiesWithConflict/.gitignore b/tests/TestEntitiesWithConflict/.gitignore new file mode 100644 index 0000000..1d4364f --- /dev/null +++ b/tests/TestEntitiesWithConflict/.gitignore @@ -0,0 +1,3 @@ +# Because this file already exists without an explicit message that it +# was generated by RepositoriesGenerateCommand, it creates a conflict +# when RepositoriesGenerateCommand is executed \ No newline at end of file diff --git a/tests/TestEntitiesWithConflict/User.php b/tests/TestEntitiesWithConflict/User.php new file mode 100644 index 0000000..46e6337 --- /dev/null +++ b/tests/TestEntitiesWithConflict/User.php @@ -0,0 +1,34 @@ +userId; + } + + public function isActive(): bool + { + return $this->active; + } + + public function getUserName(): string + { + return $this->userName; + } +}