Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Adding cycle/annotated 4.0 support #79

Merged
merged 10 commits into from
Jan 9, 2024
6 changes: 3 additions & 3 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
],
"require": {
"php": ">=8.1",
"cycle/annotated": "^3.1",
"cycle/annotated": "^4.0",
"cycle/migrations": "^4.0.1",
"cycle/orm": "^2.0.2",
"cycle/schema-migrations-generator": "^2.1",
Expand All @@ -37,7 +37,6 @@
"spiral/auth": "^3.0",
"spiral/tokenizer": "^3.0",
"spiral/config": "^3.0",
"spiral/validator": "^1.2",
"spiral/filters": "^3.10",
"spiral/data-grid-bridge": "^3.0",
"psr/container": "^1.1 || ^2.0"
Expand All @@ -48,8 +47,9 @@
"infection/infection": "^0.26.6",
"mockery/mockery": "^1.5",
"phpunit/phpunit": "^9.5.20",
"spiral/framework": "^3.9",
"spiral/framework": "^3.11.1",
"spiral/testing": "^2.4",
"spiral/validator": "^1.5",
"spiral/nyholm-bridge": "^1.3",
"spiral-packages/database-seeder": "^3.1",
"vimeo/psalm": "^4.27"
Expand Down
55 changes: 55 additions & 0 deletions src/Annotated/Locator/ListenerEmbeddingsLocator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

declare(strict_types=1);

namespace Spiral\Cycle\Annotated\Locator;

use Cycle\Annotated\Annotation\Embeddable;
use Cycle\Annotated\Exception\AnnotationException;
use Cycle\Annotated\Locator\Embedding;
use Cycle\Annotated\Locator\EmbeddingLocatorInterface;
use Spiral\Attributes\ReaderInterface;
use Spiral\Tokenizer\Attribute\TargetAttribute;
use Spiral\Tokenizer\TokenizationListenerInterface;

#[TargetAttribute(Embeddable::class, useAnnotations: true)]
final class ListenerEmbeddingsLocator implements EmbeddingLocatorInterface, TokenizationListenerInterface
{
/**
* @var Embedding[]
*/
private array $embeddings = [];
private bool $collected = false;

public function __construct(
private readonly ReaderInterface $reader
) {
}

public function listen(\ReflectionClass $class): void
{
try {
$attribute = $this->reader->firstClassMetadata($class, Embeddable::class);
} catch (\Exception $e) {
throw new AnnotationException($e->getMessage(), (int) $e->getCode(), $e);

Check warning on line 34 in src/Annotated/Locator/ListenerEmbeddingsLocator.php

View check run for this annotation

Codecov / codecov/patch

src/Annotated/Locator/ListenerEmbeddingsLocator.php#L33-L34

Added lines #L33 - L34 were not covered by tests
}

if ($attribute !== null) {
$this->embeddings[] = new Embedding($attribute, $class);
}
}

public function finalize(): void
{
$this->collected = true;
}

public function getEmbeddings(): array
{
if (!$this->collected) {
throw new AnnotationException(\sprintf('Tokenizer did not finalize %s listener.', self::class));
}

return $this->embeddings;
}
}
56 changes: 56 additions & 0 deletions src/Annotated/Locator/ListenerEntityLocator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

declare(strict_types=1);

namespace Spiral\Cycle\Annotated\Locator;

use Cycle\Annotated\Annotation\Entity as Attribute;
use Cycle\Annotated\Exception\AnnotationException;
use Cycle\Annotated\Locator\Entity;
use Cycle\Annotated\Locator\EntityLocatorInterface;
use Spiral\Attributes\ReaderInterface;
use Spiral\Tokenizer\Attribute\TargetAttribute;
use Spiral\Tokenizer\TokenizationListenerInterface;

#[TargetAttribute(Attribute::class, useAnnotations: true)]
final class ListenerEntityLocator implements EntityLocatorInterface, TokenizationListenerInterface
{
/**
* @var Entity[]
*/
private array $entities = [];
private bool $collected = false;

public function __construct(
private readonly ReaderInterface $reader
) {
}

public function listen(\ReflectionClass $class): void
{
try {
/** @var Attribute $attribute */
$attribute = $this->reader->firstClassMetadata($class, Attribute::class);
} catch (\Exception $e) {
throw new AnnotationException($e->getMessage(), (int) $e->getCode(), $e);

Check warning on line 35 in src/Annotated/Locator/ListenerEntityLocator.php

View check run for this annotation

Codecov / codecov/patch

src/Annotated/Locator/ListenerEntityLocator.php#L34-L35

Added lines #L34 - L35 were not covered by tests
}

if ($attribute !== null) {
$this->entities[] = new Entity($attribute, $class);
}
}

public function finalize(): void
{
$this->collected = true;
}

public function getEntities(): array
{
if (!$this->collected) {
throw new AnnotationException(\sprintf('Tokenizer did not finalize %s listener.', self::class));
}

return $this->entities;
}
}
36 changes: 25 additions & 11 deletions src/Bootloader/AnnotatedBootloader.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@
use Spiral\Attributes\ReaderInterface;
use Spiral\Boot\Bootloader\Bootloader;
use Spiral\Bootloader\Attributes\AttributesBootloader;
use Spiral\Tokenizer\Bootloader\TokenizerBootloader;
use Spiral\Tokenizer\ClassesInterface;
use Spiral\Cycle\Annotated\Locator\ListenerEmbeddingsLocator;
use Spiral\Cycle\Annotated\Locator\ListenerEntityLocator;
use Spiral\Tokenizer\Bootloader\TokenizerListenerBootloader;

final class AnnotatedBootloader extends Bootloader
{
protected const DEPENDENCIES = [
SchemaBootloader::class,
TokenizerBootloader::class,
TokenizerListenerBootloader::class,
AttributesBootloader::class,
];

Expand All @@ -27,23 +28,37 @@ final class AnnotatedBootloader extends Bootloader
Annotated\MergeIndexes::class => [self::class, 'initMergeIndexes'],
];

public function init(SchemaBootloader $schema): void
{
protected const SINGLETONS = [
ListenerEntityLocator::class => ListenerEntityLocator::class,
ListenerEmbeddingsLocator::class => ListenerEmbeddingsLocator::class,
];

public function init(
SchemaBootloader $schema,
TokenizerListenerBootloader $tokenizer,
ListenerEntityLocator $entityLocator,
ListenerEmbeddingsLocator $embeddingsLocator
): void {
$tokenizer->addListener($entityLocator);
$tokenizer->addListener($embeddingsLocator);

$schema->addGenerator(SchemaBootloader::GROUP_INDEX, Annotated\Embeddings::class);
$schema->addGenerator(SchemaBootloader::GROUP_INDEX, Annotated\Entities::class);
$schema->addGenerator(SchemaBootloader::GROUP_INDEX, Annotated\TableInheritance::class);
$schema->addGenerator(SchemaBootloader::GROUP_INDEX, Annotated\MergeColumns::class);
$schema->addGenerator(SchemaBootloader::GROUP_RENDER, Annotated\MergeIndexes::class);
}

private function initEmbeddings(ClassesInterface $classes, ReaderInterface $reader): Annotated\Embeddings
{
return new Annotated\Embeddings($classes, $reader);
private function initEmbeddings(
ReaderInterface $reader,
ListenerEmbeddingsLocator $embeddingsLocator
): Annotated\Embeddings {
return new Annotated\Embeddings($embeddingsLocator, $reader);
}

public function initEntities(ClassesInterface $classes, ReaderInterface $reader): Annotated\Entities
public function initEntities(ReaderInterface $reader, ListenerEntityLocator $entityLocator): Annotated\Entities
{
return new Annotated\Entities($classes, $reader);
return new Annotated\Entities($entityLocator, $reader);
}

public function initMergeColumns(ReaderInterface $reader): Annotated\MergeColumns
Expand All @@ -61,4 +76,3 @@ public function initMergeIndexes(ReaderInterface $reader): Annotated\MergeIndexe
return new Annotated\MergeIndexes($reader);
}
}

21 changes: 21 additions & 0 deletions tests/app/Entities/Address.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

declare(strict_types=1);

namespace Spiral\App\Entities;

use Cycle\Annotated\Annotation\Embeddable;
use Cycle\Annotated\Annotation\Column;

#[Embeddable]
class Address
{
#[Column(type: 'string')]
public string $country;

#[Column(type: 'string(32)')]
public string $city;

#[Column(type: 'string(100)')]
public string $address;
}
53 changes: 53 additions & 0 deletions tests/src/Annotated/Locator/ListenerEmbeddingsLocatorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

declare(strict_types=1);

namespace Spiral\Tests\Annotated\Locator;

use Cycle\Annotated\Annotation\Embeddable;
use Cycle\Annotated\Exception\AnnotationException;
use Cycle\Annotated\Locator\Embedding;
use PHPUnit\Framework\TestCase;
use Spiral\App\Entities\Address;
use Spiral\App\Entities\User;
use Spiral\Attributes\AttributeReader;
use Spiral\Cycle\Annotated\Locator\ListenerEmbeddingsLocator;

final class ListenerEmbeddingsLocatorTest extends TestCase
{
public function testListen(): void
{
$locator = new ListenerEmbeddingsLocator(new AttributeReader());
$locator->listen(new \ReflectionClass(Address::class));
$locator->finalize();

$this->assertEquals(
[
new Embedding(
new Embeddable(),
new \ReflectionClass(Address::class)
),
],
$locator->getEmbeddings());
}

public function testListenWithoutAttribute(): void
{
$locator = new ListenerEmbeddingsLocator(new AttributeReader());
$locator->listen(new \ReflectionClass(User::class));
$locator->finalize();

$this->assertSame([], $locator->getEmbeddings());
}

public function testGetEmbeddingsWithoutFinalize(): void
{
$this->expectException(AnnotationException::class);
$this->expectExceptionMessage(
\sprintf('Tokenizer did not finalize %s listener.', ListenerEmbeddingsLocator::class)
);

$locator = new ListenerEmbeddingsLocator(new AttributeReader());
$locator->getEmbeddings();
}
}
52 changes: 52 additions & 0 deletions tests/src/Annotated/Locator/ListenerEntityLocatorTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

declare(strict_types=1);

namespace Spiral\Tests\Annotated\Locator;

use Cycle\Annotated\Exception\AnnotationException;
use Cycle\Annotated\Locator\Entity;
use PHPUnit\Framework\TestCase;
use Spiral\App\Entities\User;
use Spiral\App\Repositories\UserRepository;
use Spiral\Attributes\AttributeReader;
use Spiral\Cycle\Annotated\Locator\ListenerEntityLocator;

final class ListenerEntityLocatorTest extends TestCase
{
public function testListen(): void
{
$locator = new ListenerEntityLocator(new AttributeReader());
$locator->listen(new \ReflectionClass(User::class));
$locator->finalize();

$this->assertEquals(
[
new Entity(
new \Cycle\Annotated\Annotation\Entity(repository: UserRepository::class),
new \ReflectionClass(User::class)
),
],
$locator->getEntities());
}

public function testListenWithoutAttribute(): void
{
$locator = new ListenerEntityLocator(new AttributeReader());
$locator->listen(new \ReflectionClass(\stdClass::class));
$locator->finalize();

$this->assertSame([], $locator->getEntities());
}

public function testGetEntitiesWithoutFinalize(): void
{
$this->expectException(AnnotationException::class);
$this->expectExceptionMessage(
\sprintf('Tokenizer did not finalize %s listener.', ListenerEntityLocator::class)
);

$locator = new ListenerEntityLocator(new AttributeReader());
$locator->getEntities();
}
}
12 changes: 12 additions & 0 deletions tests/src/Bootloader/AnnotatedBootloaderTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
use Cycle\Annotated;
use Cycle\Schema\GeneratorInterface;
use Spiral\Attributes\ReaderInterface;
use Spiral\Cycle\Annotated\Locator\ListenerEmbeddingsLocator;
use Spiral\Cycle\Annotated\Locator\ListenerEntityLocator;
use Spiral\Tests\BaseTest;

final class AnnotatedBootloaderTest extends BaseTest
Expand Down Expand Up @@ -40,4 +42,14 @@ public function testGetsAnnotatedMergeIndexes(): void
{
$this->assertContainerBound(Annotated\MergeIndexes::class, GeneratorInterface::class);
}

public function testGetsListenerEntityLocator(): void
{
$this->assertContainerBoundAsSingleton(ListenerEntityLocator::class, ListenerEntityLocator::class);
}

public function testGetsListenerEmbeddingsLocator(): void
{
$this->assertContainerBoundAsSingleton(ListenerEmbeddingsLocator::class, ListenerEmbeddingsLocator::class);
}
}
4 changes: 4 additions & 0 deletions tests/src/Console/Command/CycleOrm/MigrateCommandTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

use Cycle\ORM\SchemaInterface;
use Spiral\Boot\MemoryInterface;
use Spiral\Cycle\Annotated\Locator\ListenerEntityLocator;
use Spiral\Cycle\Config\CycleConfig;
use Spiral\Files\Files;
use Spiral\Tests\ConsoleTest;
Expand Down Expand Up @@ -95,6 +96,9 @@ class Tag
PHP,
);

$listener = $this->getContainer()->get(ListenerEntityLocator::class);
$listener->listen(new \ReflectionClass(\Spiral\App\Entities\Tag::class));

$this->assertConsoleCommandOutputContainsStrings('cycle:migrate', ['-r' => true], [
'default.tags',
'create table',
Expand Down
Loading