Skip to content

Commit

Permalink
Merge pull request #79 from spiral/annotated-4-0
Browse files Browse the repository at this point in the history
Adding cycle/annotated 4.0 support
  • Loading branch information
butschster authored Jan 9, 2024
2 parents 6673bae + 436f8c3 commit 696a920
Show file tree
Hide file tree
Showing 9 changed files with 281 additions and 14 deletions.
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);
}

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);
}

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

0 comments on commit 696a920

Please sign in to comment.