From 6113c42f081b1be878558ea68211e1067b47c425 Mon Sep 17 00:00:00 2001 From: Alexander Strizhak Date: Sat, 26 Apr 2025 15:00:40 +0300 Subject: [PATCH 01/14] psalm up --- composer.json | 2 +- tests/docker-compose.yml | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 3707ea55..202fa8ff 100644 --- a/composer.json +++ b/composer.json @@ -47,7 +47,7 @@ "require-dev": { "doctrine/annotations": "^1.14.3 || ^2.0.1", "phpunit/phpunit": "^10.1", - "vimeo/psalm": "^5.26", + "vimeo/psalm": "^5.26 || ^6.10", "spiral/code-style": "^2.2", "spiral/dumper": "^3.3" }, diff --git a/tests/docker-compose.yml b/tests/docker-compose.yml index 7bbf8d8b..317ab8ba 100644 --- a/tests/docker-compose.yml +++ b/tests/docker-compose.yml @@ -1,5 +1,3 @@ -version: "3" - services: sqlserver: image: mcr.microsoft.com/mssql/server:2019-latest From f4502e63987e2c34424ae71b22ab6db58ef8ad4c Mon Sep 17 00:00:00 2001 From: Alexander Strizhak Date: Sat, 26 Apr 2025 15:39:45 +0300 Subject: [PATCH 02/14] obsolete annotation --- src/Annotation/Obsolete.php | 20 +++++++++ src/Configurator.php | 23 +++++++++- tests/Annotated/Fixtures/Fixtures26/User.php | 40 +++++++++++++++++ .../Functional/Driver/Common/ObsoleteTest.php | 44 +++++++++++++++++++ .../Functional/Driver/MySQL/ObsoleteTest.php | 17 +++++++ .../Driver/Postgres/ObsoleteTest.php | 17 +++++++ .../Driver/SQLServer/ObsoleteTest.php | 17 +++++++ .../Functional/Driver/SQLite/ObsoleteTest.php | 17 +++++++ tests/generate.php | 24 ++++++++++ 9 files changed, 217 insertions(+), 2 deletions(-) create mode 100644 src/Annotation/Obsolete.php create mode 100644 tests/Annotated/Fixtures/Fixtures26/User.php create mode 100644 tests/Annotated/Functional/Driver/Common/ObsoleteTest.php create mode 100644 tests/Annotated/Functional/Driver/MySQL/ObsoleteTest.php create mode 100644 tests/Annotated/Functional/Driver/Postgres/ObsoleteTest.php create mode 100644 tests/Annotated/Functional/Driver/SQLServer/ObsoleteTest.php create mode 100644 tests/Annotated/Functional/Driver/SQLite/ObsoleteTest.php diff --git a/src/Annotation/Obsolete.php b/src/Annotation/Obsolete.php new file mode 100644 index 00000000..8ecb80a1 --- /dev/null +++ b/src/Annotation/Obsolete.php @@ -0,0 +1,20 @@ +getProperties() as $property) { + foreach ($this->getActualProperties($class) as $property) { try { $column = $this->reader->firstPropertyMetadata($property, Column::class); } catch (\Exception $e) { @@ -119,7 +120,7 @@ public function initFields(EntitySchema $entity, \ReflectionClass $class, string public function initRelations(EntitySchema $entity, \ReflectionClass $class): void { - foreach ($class->getProperties() as $property) { + foreach ($this->getActualProperties($class) as $property) { $metadata = $this->getPropertyMetadata($property, RelationAnnotation\RelationInterface::class); foreach ($metadata as $meta) { @@ -413,4 +414,22 @@ private function isOnInsertGeneratedField(Field $field): bool default => $field->isPrimary(), }; } + + /** + * @return \Generator<\ReflectionProperty> + */ + private function getActualProperties(\ReflectionClass $class): \Generator + { + foreach ($class->getProperties() as $property) { + // Obsolete property must not be included in the scheme. + $metadata = \iterator_to_array( + $this->getPropertyMetadata($property, Obsolete::class), + ); + if ([] !== $metadata) { + continue; + } + + yield $property; + } + } } diff --git a/tests/Annotated/Fixtures/Fixtures26/User.php b/tests/Annotated/Fixtures/Fixtures26/User.php new file mode 100644 index 00000000..1bc24f4d --- /dev/null +++ b/tests/Annotated/Fixtures/Fixtures26/User.php @@ -0,0 +1,40 @@ + [__DIR__ . '/../../../Fixtures/Fixtures26'], + 'exclude' => [], + ]) + ); + + $locator = $tokenizer->classLocator(); + + $r = new Registry($this->dbal); + + $schema = (new Compiler())->compile($r, [ + new Entities(new TokenizerEntityLocator($locator, $reader), $reader), + new RenderTables(), + new SyncTables(), + ]); + + $this->assertArrayNotHasKey('skype', $schema['user'][SchemaInterface::COLUMNS]); + $this->assertArrayNotHasKey('skype', $schema['user'][SchemaInterface::TYPECAST]); + } +} diff --git a/tests/Annotated/Functional/Driver/MySQL/ObsoleteTest.php b/tests/Annotated/Functional/Driver/MySQL/ObsoleteTest.php new file mode 100644 index 00000000..8d4675ee --- /dev/null +++ b/tests/Annotated/Functional/Driver/MySQL/ObsoleteTest.php @@ -0,0 +1,17 @@ + Date: Sat, 26 Apr 2025 13:13:52 +0000 Subject: [PATCH 03/14] style(php-cs-fixer): fix coding standards --- src/Annotation/Obsolete.php | 4 +--- src/Configurator.php | 2 +- tests/Annotated/Functional/Driver/Common/ObsoleteTest.php | 2 +- tests/generate.php | 1 - 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Annotation/Obsolete.php b/src/Annotation/Obsolete.php index 8ecb80a1..9be975a2 100644 --- a/src/Annotation/Obsolete.php +++ b/src/Annotation/Obsolete.php @@ -15,6 +15,4 @@ * @Target({"PROPERTY"}) */ #[\Attribute(\Attribute::TARGET_PROPERTY), NamedArgumentConstructor] -class Obsolete -{ -} +class Obsolete {} diff --git a/src/Configurator.php b/src/Configurator.php index 4490c655..7f9bc9eb 100644 --- a/src/Configurator.php +++ b/src/Configurator.php @@ -425,7 +425,7 @@ private function getActualProperties(\ReflectionClass $class): \Generator $metadata = \iterator_to_array( $this->getPropertyMetadata($property, Obsolete::class), ); - if ([] !== $metadata) { + if ($metadata !== []) { continue; } diff --git a/tests/Annotated/Functional/Driver/Common/ObsoleteTest.php b/tests/Annotated/Functional/Driver/Common/ObsoleteTest.php index 8538de9c..b8640d81 100644 --- a/tests/Annotated/Functional/Driver/Common/ObsoleteTest.php +++ b/tests/Annotated/Functional/Driver/Common/ObsoleteTest.php @@ -25,7 +25,7 @@ public function testObsoleteColumn(ReaderInterface $reader): void new TokenizerConfig([ 'directories' => [__DIR__ . '/../../../Fixtures/Fixtures26'], 'exclude' => [], - ]) + ]), ); $locator = $tokenizer->classLocator(); diff --git a/tests/generate.php b/tests/generate.php index 3042bd88..46576ea7 100644 --- a/tests/generate.php +++ b/tests/generate.php @@ -3,7 +3,6 @@ /** * 1. Create new test case class in folder `tests/Functional/Driver/Common` with content: * - * * namespace Cycle\Annotated\Tests\Functional\Driver\Common; * * abstract class MyTest extends BaseTest {} From 26ef33e44be484462f406bb8aa78a4828e18d0a7 Mon Sep 17 00:00:00 2001 From: Alexander Strizhak Date: Sat, 26 Apr 2025 16:15:06 +0300 Subject: [PATCH 04/14] up --- src/Annotation/Obsolete.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Annotation/Obsolete.php b/src/Annotation/Obsolete.php index 8ecb80a1..f7ed3235 100644 --- a/src/Annotation/Obsolete.php +++ b/src/Annotation/Obsolete.php @@ -7,7 +7,7 @@ use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; /** - * Used to safely drop column from table which used in several services.For safely drop column from table which used in several services. + * For safely drop column from table which used in several services. * The property should not be displayed in schema but should remain in the tableThe property should not be in schema. * * @Annotation From 94d25209e99caa61dfd6ff5ef3c8f2622422cf45 Mon Sep 17 00:00:00 2001 From: Alexander Strizhak Date: Sat, 26 Apr 2025 16:19:57 +0300 Subject: [PATCH 05/14] up --- .../Functional/Driver/Common/ObsoleteTest.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/Annotated/Functional/Driver/Common/ObsoleteTest.php b/tests/Annotated/Functional/Driver/Common/ObsoleteTest.php index b8640d81..cb92b751 100644 --- a/tests/Annotated/Functional/Driver/Common/ObsoleteTest.php +++ b/tests/Annotated/Functional/Driver/Common/ObsoleteTest.php @@ -6,10 +6,17 @@ use Cycle\Annotated\Entities; use Cycle\Annotated\Locator\TokenizerEntityLocator; +use Cycle\Annotated\MergeColumns; +use Cycle\Annotated\MergeIndexes; use Cycle\ORM\SchemaInterface; use Cycle\Schema\Compiler; +use Cycle\Schema\Generator\GenerateRelations; +use Cycle\Schema\Generator\GenerateTypecast; +use Cycle\Schema\Generator\RenderRelations; use Cycle\Schema\Generator\RenderTables; +use Cycle\Schema\Generator\ResetTables; use Cycle\Schema\Generator\SyncTables; +use Cycle\Schema\Generator\ValidateEntities; use Cycle\Schema\Registry; use PHPUnit\Framework\Attributes\DataProvider; use Spiral\Attributes\ReaderInterface; @@ -34,8 +41,15 @@ public function testObsoleteColumn(ReaderInterface $reader): void $schema = (new Compiler())->compile($r, [ new Entities(new TokenizerEntityLocator($locator, $reader), $reader), + new ResetTables(), + new MergeColumns($reader), + new GenerateRelations(), + new ValidateEntities(), new RenderTables(), + new RenderRelations(), + new MergeIndexes($reader), new SyncTables(), + new GenerateTypecast(), ]); $this->assertArrayNotHasKey('skype', $schema['user'][SchemaInterface::COLUMNS]); From b5eed225d228e10f2ae76437c0ad9927c8c5dddf Mon Sep 17 00:00:00 2001 From: Alexander Strizhak Date: Sat, 26 Apr 2025 16:32:16 +0300 Subject: [PATCH 06/14] up --- src/Configurator.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Configurator.php b/src/Configurator.php index 7f9bc9eb..7accf687 100644 --- a/src/Configurator.php +++ b/src/Configurator.php @@ -422,9 +422,10 @@ private function getActualProperties(\ReflectionClass $class): \Generator { foreach ($class->getProperties() as $property) { // Obsolete property must not be included in the scheme. - $metadata = \iterator_to_array( - $this->getPropertyMetadata($property, Obsolete::class), - ); + $metadata = $this->getPropertyMetadata($property, Obsolete::class); + if (!\is_array($metadata)) { + $metadata = \iterator_to_array($metadata); + } if ($metadata !== []) { continue; } From 3a96ecea6314f619363b9f35a886e9e9f653a9dd Mon Sep 17 00:00:00 2001 From: Alexander Strizhak Date: Sun, 27 Apr 2025 00:44:33 +0300 Subject: [PATCH 07/14] up --- src/Configurator.php | 24 ++++---------- .../Annotated/Fixtures/Fixtures26/Address.php | 31 +++++++++++++++++ .../Fixtures/Fixtures26/Passport.php | 33 +++++++++++++++++++ tests/Annotated/Fixtures/Fixtures26/User.php | 26 +++++++++++++-- .../Functional/Driver/Common/ObsoleteTest.php | 9 +++++ 5 files changed, 103 insertions(+), 20 deletions(-) create mode 100644 tests/Annotated/Fixtures/Fixtures26/Address.php create mode 100644 tests/Annotated/Fixtures/Fixtures26/Passport.php diff --git a/src/Configurator.php b/src/Configurator.php index 7accf687..68c5f0f0 100644 --- a/src/Configurator.php +++ b/src/Configurator.php @@ -97,7 +97,7 @@ public function initEmbedding(Embeddable $emb, \ReflectionClass $class): EntityS public function initFields(EntitySchema $entity, \ReflectionClass $class, string $columnPrefix = ''): void { - foreach ($this->getActualProperties($class) as $property) { + foreach ($class->getProperties() as $property) { try { $column = $this->reader->firstPropertyMetadata($property, Column::class); } catch (\Exception $e) { @@ -114,13 +114,14 @@ public function initFields(EntitySchema $entity, \ReflectionClass $class, string $field = $this->initField($property->getName(), $column, $class, $columnPrefix); $field->setEntityClass($property->getDeclaringClass()->getName()); + $field->setObsolete($this->isObsolete($property)); $entity->getFields()->set($property->getName(), $field); } } public function initRelations(EntitySchema $entity, \ReflectionClass $class): void { - foreach ($this->getActualProperties($class) as $property) { + foreach ($class->getProperties() as $property) { $metadata = $this->getPropertyMetadata($property, RelationAnnotation\RelationInterface::class); foreach ($metadata as $meta) { @@ -173,6 +174,7 @@ public function initRelations(EntitySchema $entity, \ReflectionClass $class): vo $relation->getOptions()->set($option, $value); } + $relation->setObsolete($this->isObsolete($property)); // need relation definition $entity->getRelations()->set($property->getName(), $relation); } @@ -415,22 +417,8 @@ private function isOnInsertGeneratedField(Field $field): bool }; } - /** - * @return \Generator<\ReflectionProperty> - */ - private function getActualProperties(\ReflectionClass $class): \Generator + private function isObsolete(\ReflectionProperty $property): bool { - foreach ($class->getProperties() as $property) { - // Obsolete property must not be included in the scheme. - $metadata = $this->getPropertyMetadata($property, Obsolete::class); - if (!\is_array($metadata)) { - $metadata = \iterator_to_array($metadata); - } - if ($metadata !== []) { - continue; - } - - yield $property; - } + return $this->reader->firstPropertyMetadata($property, Obsolete::class) !== null; } } diff --git a/tests/Annotated/Fixtures/Fixtures26/Address.php b/tests/Annotated/Fixtures/Fixtures26/Address.php new file mode 100644 index 00000000..02399cf6 --- /dev/null +++ b/tests/Annotated/Fixtures/Fixtures26/Address.php @@ -0,0 +1,31 @@ +address = new Address(); + } } diff --git a/tests/Annotated/Functional/Driver/Common/ObsoleteTest.php b/tests/Annotated/Functional/Driver/Common/ObsoleteTest.php index cb92b751..b8bdd793 100644 --- a/tests/Annotated/Functional/Driver/Common/ObsoleteTest.php +++ b/tests/Annotated/Functional/Driver/Common/ObsoleteTest.php @@ -4,7 +4,9 @@ namespace Cycle\Annotated\Tests\Functional\Driver\Common; +use Cycle\Annotated\Embeddings; use Cycle\Annotated\Entities; +use Cycle\Annotated\Locator\TokenizerEmbeddingLocator; use Cycle\Annotated\Locator\TokenizerEntityLocator; use Cycle\Annotated\MergeColumns; use Cycle\Annotated\MergeIndexes; @@ -40,6 +42,7 @@ public function testObsoleteColumn(ReaderInterface $reader): void $r = new Registry($this->dbal); $schema = (new Compiler())->compile($r, [ + new Embeddings(new TokenizerEmbeddingLocator($locator, $reader), $reader), new Entities(new TokenizerEntityLocator($locator, $reader), $reader), new ResetTables(), new MergeColumns($reader), @@ -52,7 +55,13 @@ public function testObsoleteColumn(ReaderInterface $reader): void new GenerateTypecast(), ]); + $this->assertArrayNotHasKey('user:address:address', $schema); + // user $this->assertArrayNotHasKey('skype', $schema['user'][SchemaInterface::COLUMNS]); $this->assertArrayNotHasKey('skype', $schema['user'][SchemaInterface::TYPECAST]); + $this->assertArrayNotHasKey('passport', $schema['user'][SchemaInterface::RELATIONS]); + $this->assertArrayNotHasKey('address', $schema['user'][SchemaInterface::RELATIONS]); + // passport + $this->assertArrayNotHasKey('user', $schema['passport'][SchemaInterface::RELATIONS]); } } From 4bc62bb498e69e3e3a9d4b79c3271167ab176ad4 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 26 Apr 2025 21:44:58 +0000 Subject: [PATCH 08/14] style(php-cs-fixer): fix coding standards --- tests/Annotated/Fixtures/Fixtures26/User.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/Annotated/Fixtures/Fixtures26/User.php b/tests/Annotated/Fixtures/Fixtures26/User.php index abf28345..5fd38ced 100644 --- a/tests/Annotated/Fixtures/Fixtures26/User.php +++ b/tests/Annotated/Fixtures/Fixtures26/User.php @@ -56,7 +56,8 @@ class User #[HasOne(target: Passport::class)] protected $passport; - public function __construct() { + public function __construct() + { $this->address = new Address(); } } From 2ef0e6e3890259171107a0037cffa0ccbf928ff6 Mon Sep 17 00:00:00 2001 From: Alexander Strizhak Date: Sun, 27 Apr 2025 14:38:35 +0300 Subject: [PATCH 09/14] attr --- src/Annotation/Column.php | 17 ++++---- src/Annotation/Obsolete.php | 18 -------- src/Annotation/Relation/BelongsTo.php | 4 +- src/Annotation/Relation/Embedded.php | 4 +- src/Annotation/Relation/HasMany.php | 4 +- src/Annotation/Relation/HasOne.php | 4 +- src/Annotation/Relation/ManyToMany.php | 4 +- src/Annotation/Relation/RefersTo.php | 4 +- src/Annotation/Relation/Relation.php | 2 + src/Configurator.php | 10 +---- tests/Annotated/Fixtures/Fixtures26/City.php | 28 ++++++++++++ .../Fixtures/Fixtures26/Passport.php | 7 +-- tests/Annotated/Fixtures/Fixtures26/User.php | 43 +++++++------------ 13 files changed, 76 insertions(+), 73 deletions(-) delete mode 100644 src/Annotation/Obsolete.php create mode 100644 tests/Annotated/Fixtures/Fixtures26/City.php diff --git a/src/Annotation/Column.php b/src/Annotation/Column.php index aec7a99a..45468bd1 100644 --- a/src/Annotation/Column.php +++ b/src/Annotation/Column.php @@ -38,15 +38,8 @@ class Column * @param bool $primary Explicitly set column as a primary key. * @param bool $nullable Set column as nullable. * @param mixed|null $default Default column value. - * @param callable|non-empty-string|null $typecast Typecast rule name. - * Regarding the default Typecast handler {@see Typecast} the value can be `callable` or - * one of ("int"|"float"|"bool"|"datetime") based on column type. - * If you want to use another rule you should add in the `typecast` argument of the {@see Entity} attribute - * a relevant Typecast handler that supports the rule. - * @param bool $readonlySchema Set to true to disable schema synchronization for the assigned column. - * @param mixed ...$attributes Other database specific attributes. Use named notation to define them. - * For example: #[Column('smallInt', unsigned: true, zerofill: true)] - */ + * @param bool $obsolete The property should not be displayed in schema but must remains in the database. + */ public function __construct( #[ExpectedValues(values: ['primary', 'bigPrimary', 'enum', 'boolean', 'integer', 'tinyInteger', 'smallInteger', 'bigInteger', 'string', 'text', 'tinyText', 'longText', 'double', @@ -68,6 +61,7 @@ public function __construct( protected mixed $typecast = null, protected bool $castDefault = false, protected bool $readonlySchema = false, + protected bool $obsolete = false, mixed ...$attributes, ) { if ($default !== null) { @@ -135,6 +129,11 @@ public function isReadonlySchema(): bool return $this->readonlySchema; } + public function isObsolete(): bool + { + return $this->obsolete; + } + /** * @return array */ diff --git a/src/Annotation/Obsolete.php b/src/Annotation/Obsolete.php deleted file mode 100644 index b9c85c74..00000000 --- a/src/Annotation/Obsolete.php +++ /dev/null @@ -1,18 +0,0 @@ -inverse = $inverse; - parent::__construct($target, $load); + parent::__construct($target, $load, $obsolete); } } diff --git a/src/Annotation/Relation/Embedded.php b/src/Annotation/Relation/Embedded.php index 8023865f..f35c697d 100644 --- a/src/Annotation/Relation/Embedded.php +++ b/src/Annotation/Relation/Embedded.php @@ -23,16 +23,18 @@ class Embedded extends Relation * @param non-empty-string $target Entity to embed. * @param 'eager'|'lazy' $load Relation load approach. * @param string|null $prefix Prefix for embedded entity columns. + * @param bool $obsolete The property should not be displayed in schema but must remains in the database. */ public function __construct( string $target, #[ExpectedValues(values: ['lazy', 'eager'])] string $load = 'eager', ?string $prefix = null, + bool $obsolete = false, ) { $this->embeddedPrefix = $prefix; - parent::__construct($target, $load); + parent::__construct($target, $load, $obsolete); } public function getInverse(): ?Inverse diff --git a/src/Annotation/Relation/HasMany.php b/src/Annotation/Relation/HasMany.php index 273aca47..650e79c7 100644 --- a/src/Annotation/Relation/HasMany.php +++ b/src/Annotation/Relation/HasMany.php @@ -35,6 +35,7 @@ class HasMany extends Relation * @param bool $indexCreate Create an index on outerKey. * @param non-empty-string|null $collection Collection that will contain loaded entities. * @param non-empty-string $load Relation load approach. + * @param bool $obsolete The property should not be displayed in schema but must remains in the database. */ public function __construct( string $target, @@ -60,9 +61,10 @@ public function __construct( #[ExpectedValues(values: ['lazy', 'eager'])] string $load = 'lazy', ?Inverse $inverse = null, + bool $obsolete = false, ) { $this->inverse = $inverse; - parent::__construct($target, $load); + parent::__construct($target, $load, $obsolete); } } diff --git a/src/Annotation/Relation/HasOne.php b/src/Annotation/Relation/HasOne.php index 069eb163..93d76c44 100644 --- a/src/Annotation/Relation/HasOne.php +++ b/src/Annotation/Relation/HasOne.php @@ -33,6 +33,7 @@ class HasOne extends Relation * Defaults to {@see $fkAction}. * @param bool $indexCreate Create index on outerKey. * @param non-empty-string $load Relation load approach. + * @param bool $obsolete The property should not be displayed in schema but must remains in the database. */ public function __construct( string $target, @@ -55,9 +56,10 @@ public function __construct( #[ExpectedValues(values: ['lazy', 'eager'])] string $load = 'lazy', ?Inverse $inverse = null, + bool $obsolete = false, ) { $this->inverse = $inverse; - parent::__construct($target, $load); + parent::__construct($target, $load, $obsolete); } } diff --git a/src/Annotation/Relation/ManyToMany.php b/src/Annotation/Relation/ManyToMany.php index 3dd39baf..63309058 100644 --- a/src/Annotation/Relation/ManyToMany.php +++ b/src/Annotation/Relation/ManyToMany.php @@ -42,6 +42,7 @@ class ManyToMany extends Relation * @param bool $indexCreate Create index on [throughInnerKey, throughOuterKey]. * @param non-empty-string|null $collection Collection that will contain loaded entities. * @param non-empty-string $load Relation load approach. + * @param bool $obsolete The property should not be displayed in schema but must remains in the database. */ public function __construct( string $target, @@ -71,9 +72,10 @@ public function __construct( #[ExpectedValues(values: ['lazy', 'eager'])] string $load = 'lazy', ?Inverse $inverse = null, + bool $obsolete = false, ) { $this->inverse = $inverse; - parent::__construct($target, $load); + parent::__construct($target, $load, $obsolete); } } diff --git a/src/Annotation/Relation/RefersTo.php b/src/Annotation/Relation/RefersTo.php index 6a97d97f..5b308885 100644 --- a/src/Annotation/Relation/RefersTo.php +++ b/src/Annotation/Relation/RefersTo.php @@ -33,6 +33,7 @@ class RefersTo extends Relation * Defaults to {@see $fkAction}. * @param bool $indexCreate Create an index on outerKey. * @param non-empty-string $load Relation load approach. + * @param bool $obsolete The property should not be displayed in schema but must remains in the database. */ public function __construct( string $target, @@ -55,8 +56,9 @@ public function __construct( #[ExpectedValues(values: ['lazy', 'eager'])] string $load = 'lazy', ?Inverse $inverse = null, + bool $obsolete = false, ) { $this->inverse = $inverse; - parent::__construct($target, $load); + parent::__construct($target, $load, $obsolete); } } diff --git a/src/Annotation/Relation/Relation.php b/src/Annotation/Relation/Relation.php index b53c1b8d..dfbf9102 100644 --- a/src/Annotation/Relation/Relation.php +++ b/src/Annotation/Relation/Relation.php @@ -12,10 +12,12 @@ abstract class Relation implements RelationInterface /** * @param non-empty-string|null $target * @param non-empty-string $load + * @param bool $obsolete The property should not be displayed in schema but must remains in the database. Useful for the further safe DROP COLUMN. */ public function __construct( protected ?string $target, protected string $load = 'lazy', + protected bool $obsolete = false, ) {} /** diff --git a/src/Configurator.php b/src/Configurator.php index 68c5f0f0..f5eddda5 100644 --- a/src/Configurator.php +++ b/src/Configurator.php @@ -9,7 +9,6 @@ use Cycle\Annotated\Annotation\Entity; use Cycle\Annotated\Annotation\ForeignKey; use Cycle\Annotated\Annotation\GeneratedValue; -use Cycle\Annotated\Annotation\Obsolete; use Cycle\Annotated\Annotation\Relation as RelationAnnotation; use Cycle\Annotated\Exception\AnnotationException; use Cycle\Annotated\Exception\AnnotationRequiredArgumentsException; @@ -114,7 +113,6 @@ public function initFields(EntitySchema $entity, \ReflectionClass $class, string $field = $this->initField($property->getName(), $column, $class, $columnPrefix); $field->setEntityClass($property->getDeclaringClass()->getName()); - $field->setObsolete($this->isObsolete($property)); $entity->getFields()->set($property->getName(), $field); } } @@ -174,7 +172,6 @@ public function initRelations(EntitySchema $entity, \ReflectionClass $class): vo $relation->getOptions()->set($option, $value); } - $relation->setObsolete($this->isObsolete($property)); // need relation definition $entity->getRelations()->set($property->getName(), $relation); } @@ -264,6 +261,8 @@ public function initField(string $name, Column $column, \ReflectionClass $class, $field->getAttributes()->set($k, $v); } + $field->getAttributes()->set('obsolete', $column->isObsolete()); + return $field; } @@ -416,9 +415,4 @@ private function isOnInsertGeneratedField(Field $field): bool default => $field->isPrimary(), }; } - - private function isObsolete(\ReflectionProperty $property): bool - { - return $this->reader->firstPropertyMetadata($property, Obsolete::class) !== null; - } } diff --git a/tests/Annotated/Fixtures/Fixtures26/City.php b/tests/Annotated/Fixtures/Fixtures26/City.php new file mode 100644 index 00000000..119ba684 --- /dev/null +++ b/tests/Annotated/Fixtures/Fixtures26/City.php @@ -0,0 +1,28 @@ +address = new Address(); + $this->bornCity = $bornCity; } } From 0e9075f2553821653122a0e31423b56e721e4534 Mon Sep 17 00:00:00 2001 From: Alexander Strizhak Date: Sun, 27 Apr 2025 19:40:38 +0300 Subject: [PATCH 10/14] attr --- composer.json | 14 ++++++++++++-- .../Functional/Driver/Common/ObsoleteTest.php | 3 +++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 202fa8ff..fb54836e 100644 --- a/composer.json +++ b/composer.json @@ -38,8 +38,8 @@ ], "require": { "php": ">=8.1", - "cycle/orm": "^2.9.2", - "cycle/schema-builder": "^2.11.1", + "cycle/orm": "dev-obsolete-column as 2.9.2", + "cycle/schema-builder": "dev-obsolete-column as 2.11.1", "spiral/attributes": "^2.8|^3.0", "spiral/tokenizer": "^2.8|^3.0", "doctrine/inflector": "^2.0" @@ -61,6 +61,16 @@ "Cycle\\Annotated\\Tests\\": "tests/Annotated/" } }, + "repositories": [ + { + "type": "git", + "url": "https://github.com/gam6itko/cycle-orm.git" + }, + { + "type": "git", + "url": "https://github.com/gam6itko/schema-builder.git" + } + ], "scripts": { "cs:diff": "php-cs-fixer fix --dry-run -v --diff", "cs:fix": "php-cs-fixer fix -v", diff --git a/tests/Annotated/Functional/Driver/Common/ObsoleteTest.php b/tests/Annotated/Functional/Driver/Common/ObsoleteTest.php index b8bdd793..daa078be 100644 --- a/tests/Annotated/Functional/Driver/Common/ObsoleteTest.php +++ b/tests/Annotated/Functional/Driver/Common/ObsoleteTest.php @@ -61,7 +61,10 @@ public function testObsoleteColumn(ReaderInterface $reader): void $this->assertArrayNotHasKey('skype', $schema['user'][SchemaInterface::TYPECAST]); $this->assertArrayNotHasKey('passport', $schema['user'][SchemaInterface::RELATIONS]); $this->assertArrayNotHasKey('address', $schema['user'][SchemaInterface::RELATIONS]); + $this->assertArrayNotHasKey('bornCity', $schema['user'][SchemaInterface::RELATIONS]); // passport $this->assertArrayNotHasKey('user', $schema['passport'][SchemaInterface::RELATIONS]); + // city + $this->assertArrayNotHasKey('bornUsers', $schema['city'][SchemaInterface::RELATIONS]); } } From 5a81436910e671325394341332d410ef9a240ca0 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sun, 27 Apr 2025 16:41:02 +0000 Subject: [PATCH 11/14] style(php-cs-fixer): fix coding standards --- src/Annotation/Column.php | 3 +-- tests/Annotated/Fixtures/Fixtures26/User.php | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Annotation/Column.php b/src/Annotation/Column.php index 45468bd1..e3166298 100644 --- a/src/Annotation/Column.php +++ b/src/Annotation/Column.php @@ -4,7 +4,6 @@ namespace Cycle\Annotated\Annotation; -use Cycle\ORM\Parser\Typecast; use Doctrine\Common\Annotations\Annotation\Target; use JetBrains\PhpStorm\ExpectedValues; use Spiral\Attributes\NamedArgumentConstructor; @@ -39,7 +38,7 @@ class Column * @param bool $nullable Set column as nullable. * @param mixed|null $default Default column value. * @param bool $obsolete The property should not be displayed in schema but must remains in the database. - */ + */ public function __construct( #[ExpectedValues(values: ['primary', 'bigPrimary', 'enum', 'boolean', 'integer', 'tinyInteger', 'smallInteger', 'bigInteger', 'string', 'text', 'tinyText', 'longText', 'double', diff --git a/tests/Annotated/Fixtures/Fixtures26/User.php b/tests/Annotated/Fixtures/Fixtures26/User.php index b3924404..e2fdb4b8 100644 --- a/tests/Annotated/Fixtures/Fixtures26/User.php +++ b/tests/Annotated/Fixtures/Fixtures26/User.php @@ -25,7 +25,7 @@ class User * * @deprecated Since May 5, 2025 */ - #[Column(type: 'string', nullable: true, obsolete:true)] + #[Column(type: 'string', nullable: true, obsolete: true)] public $skype = null; /** From 315b457fe6f4f0a6c828a2e8487fb5ab86411c9e Mon Sep 17 00:00:00 2001 From: Alexander Strizhak Date: Tue, 29 Apr 2025 12:52:15 +0300 Subject: [PATCH 12/14] wip --- composer.json | 10 ---------- src/Annotation/Column.php | 10 +++++++++- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/composer.json b/composer.json index fb54836e..caee70c5 100644 --- a/composer.json +++ b/composer.json @@ -61,16 +61,6 @@ "Cycle\\Annotated\\Tests\\": "tests/Annotated/" } }, - "repositories": [ - { - "type": "git", - "url": "https://github.com/gam6itko/cycle-orm.git" - }, - { - "type": "git", - "url": "https://github.com/gam6itko/schema-builder.git" - } - ], "scripts": { "cs:diff": "php-cs-fixer fix --dry-run -v --diff", "cs:fix": "php-cs-fixer fix -v", diff --git a/src/Annotation/Column.php b/src/Annotation/Column.php index 45468bd1..f5725d6d 100644 --- a/src/Annotation/Column.php +++ b/src/Annotation/Column.php @@ -38,8 +38,16 @@ class Column * @param bool $primary Explicitly set column as a primary key. * @param bool $nullable Set column as nullable. * @param mixed|null $default Default column value. + * @param callable|non-empty-string|null $typecast Typecast rule name. + * Regarding the default Typecast handler {@see Typecast} the value can be `callable` or + * one of ("int"|"float"|"bool"|"datetime") based on column type. + * If you want to use another rule you should add in the `typecast` argument of the {@see Entity} attribute + * a relevant Typecast handler that supports the rule. + * @param bool $readonlySchema Set to true to disable schema synchronization for the assigned column. + * @param mixed ...$attributes Other database specific attributes. Use named notation to define them. + * For example: #[Column('smallInt', unsigned: true, zerofill: true)] * @param bool $obsolete The property should not be displayed in schema but must remains in the database. - */ + */ public function __construct( #[ExpectedValues(values: ['primary', 'bigPrimary', 'enum', 'boolean', 'integer', 'tinyInteger', 'smallInteger', 'bigInteger', 'string', 'text', 'tinyText', 'longText', 'double', From f0d3183133fc3b894dace42c3ee86df6b0c2f612 Mon Sep 17 00:00:00 2001 From: Alexander Strizhak Date: Tue, 29 Apr 2025 12:53:15 +0300 Subject: [PATCH 13/14] wip --- tests/generate.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/generate.php b/tests/generate.php index 46576ea7..2219ebee 100644 --- a/tests/generate.php +++ b/tests/generate.php @@ -24,7 +24,6 @@ declare(strict_types=1); -use Cycle\Annotated\Tests\Functional\Driver\Common\BaseTest; use Spiral\Tokenizer; error_reporting(E_ALL | E_STRICT); From 957374c31b774d50f3d19d9aa4120e816e5d9fa0 Mon Sep 17 00:00:00 2001 From: Alexander Strizhak Date: Tue, 29 Apr 2025 12:53:39 +0300 Subject: [PATCH 14/14] wip --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index caee70c5..202fa8ff 100644 --- a/composer.json +++ b/composer.json @@ -38,8 +38,8 @@ ], "require": { "php": ">=8.1", - "cycle/orm": "dev-obsolete-column as 2.9.2", - "cycle/schema-builder": "dev-obsolete-column as 2.11.1", + "cycle/orm": "^2.9.2", + "cycle/schema-builder": "^2.11.1", "spiral/attributes": "^2.8|^3.0", "spiral/tokenizer": "^2.8|^3.0", "doctrine/inflector": "^2.0"