Skip to content

Commit

Permalink
Remove ForeignKeysConfigurator
Browse files Browse the repository at this point in the history
  • Loading branch information
msmakouz committed Nov 2, 2023
1 parent eddcc64 commit 2d683fc
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 40 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"require": {
"php": ">=8.0",
"cycle/orm": "^2.2.0",
"cycle/schema-builder": "^2.4",
"cycle/schema-builder": "^2.6",
"doctrine/annotations": "^1.14.3 || ^2.0.1",
"spiral/attributes": "^2.8|^3.0",
"spiral/tokenizer": "^2.8|^3.0",
Expand Down
14 changes: 2 additions & 12 deletions src/Annotation/ForeignKey.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ class ForeignKey
{
public function __construct(
public string $target,
protected array|string $outerKey,
protected array|string|null $innerKey = null,
public array|string $outerKey,
public array|string|null $innerKey = null,
/**
* @Enum({"NO ACTION", "CASCADE", "SET NULL"})
*/
Expand All @@ -31,14 +31,4 @@ public function __construct(
public bool $indexCreate = true,
) {
}

public function getOuterKey(): array
{
return (array) $this->outerKey;
}

public function getInnerKey(): ?array
{
return $this->innerKey === null ? null : (array) $this->innerKey;
}
}
89 changes: 79 additions & 10 deletions src/Configurator.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
use Cycle\Annotated\Annotation\Column;
use Cycle\Annotated\Annotation\Embeddable;
use Cycle\Annotated\Annotation\Entity;
use Cycle\Annotated\Annotation\ForeignKey;
use Cycle\Annotated\Annotation\Relation as RelationAnnotation;
use Cycle\Annotated\Exception\AnnotationException;
use Cycle\Annotated\Exception\AnnotationRequiredArgumentsException;
use Cycle\Annotated\Exception\AnnotationWrongTypeArgumentException;
use Cycle\Annotated\Utils\EntityUtils;
use Cycle\Schema\Definition\Entity as EntitySchema;
use Cycle\Schema\Definition\ForeignKey as ForeignKeySchema;
use Cycle\Schema\Definition\Field;
use Cycle\Schema\Definition\Relation;
use Cycle\Schema\Generator\SyncTables;
Expand Down Expand Up @@ -110,11 +112,8 @@ public function initFields(EntitySchema $entity, \ReflectionClass $class, string
public function initRelations(EntitySchema $entity, \ReflectionClass $class): void
{
foreach ($class->getProperties() as $property) {
try {
$metadata = $this->reader->getPropertyMetadata($property, RelationAnnotation\RelationInterface::class);
} catch (Exception $e) {
throw new AnnotationException($e->getMessage(), $e->getCode(), $e);
}

$metadata = $this->getPropertyMetadata($property, RelationAnnotation\RelationInterface::class);

foreach ($metadata as $meta) {
assert($meta instanceof RelationAnnotation\RelationInterface);
Expand Down Expand Up @@ -168,11 +167,7 @@ public function initRelations(EntitySchema $entity, \ReflectionClass $class): vo

public function initModifiers(EntitySchema $entity, \ReflectionClass $class): void
{
try {
$metadata = $this->reader->getClassMetadata($class, SchemaModifierInterface::class);
} catch (Exception $e) {
throw new AnnotationException($e->getMessage(), $e->getCode(), $e);
}
$metadata = $this->getClassMetadata($class, SchemaModifierInterface::class);

foreach ($metadata as $meta) {
assert($meta instanceof SchemaModifierInterface);
Expand Down Expand Up @@ -254,6 +249,44 @@ public function initField(string $name, Column $column, \ReflectionClass $class,
return $field;
}

public function initForeignKeys(Entity $ann, EntitySchema $entity, \ReflectionClass $class): void
{
$foreignKeys = [];
foreach ($ann->getForeignKeys() as $foreignKey) {
$foreignKeys[] = $foreignKey;
}

foreach ($this->getClassMetadata($class, ForeignKey::class) as $foreignKey) {
$foreignKeys[] = $foreignKey;
}

foreach ($class->getProperties() as $property) {
foreach ($this->getPropertyMetadata($property, ForeignKey::class) as $foreignKey) {
if ($foreignKey->innerKey === null) {
$foreignKey->innerKey = [$property->getName()];
}
$foreignKeys[] = $foreignKey;
}
}

foreach ($foreignKeys as $foreignKey) {
if ($foreignKey->innerKey === null) {
throw new AnnotationException(
"Inner column definition for the foreign key is required on `{$entity->getClass()}`"
);
}

$fk = new ForeignKeySchema();
$fk->setTable($foreignKey->target);
$fk->setInnerColumns((array) $foreignKey->innerKey);
$fk->setOuterColumns((array) $foreignKey->outerKey);
$fk->createIndex($foreignKey->indexCreate);
$fk->setAction($foreignKey->action);

$entity->getForeignKeys()->set($fk);
}
}

/**
* Resolve class or role name relative to the current class.
*/
Expand Down Expand Up @@ -300,4 +333,40 @@ private function resolveTypecast(mixed $typecast, \ReflectionClass $class): mixe

return $typecast;
}

/**
* @template T
*
* @param class-string<T>|null
*
* @return iterable<T>
*
* @throws AnnotationException
*/
private function getClassMetadata(\ReflectionClass $class, string $name): iterable
{
try {
return $this->reader->getClassMetadata($class, $name);
} catch (\Exception $e) {
throw new AnnotationException($e->getMessage(), $e->getCode(), $e);
}
}

/**
* @template T
*
* @param class-string<T>|null $name
*
* @return iterable<T>
*
* @throws AnnotationException
*/
private function getPropertyMetadata(\ReflectionProperty $property, string $name): iterable
{
try {
return $this->reader->getPropertyMetadata($property, $name);
} catch (\Exception $e) {
throw new AnnotationException($e->getMessage(), $e->getCode(), $e);
}
}
}
53 changes: 36 additions & 17 deletions src/Entities.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,15 @@ final class Entities implements GeneratorInterface
private ReaderInterface $reader;
private Configurator $generator;
private EntityUtils $utils;
private ForeignKeysConfigurator $foreignKeysConfigurator;

public function __construct(
private ClassesInterface $locator,
DoctrineReader|ReaderInterface $reader = null,
int $tableNamingStrategy = self::TABLE_NAMING_PLURAL,
$foreignKeysConfigurator = null
) {
$this->reader = ReaderFactory::create($reader);
$this->utils = new EntityUtils($this->reader);
$this->generator = new Configurator($this->reader, $tableNamingStrategy);
$this->foreignKeysConfigurator = $foreignKeysConfigurator
?? new ForeignKeysConfigurator($this->utils, $this->reader);
}

public function run(Registry $registry): Registry
Expand Down Expand Up @@ -71,22 +67,17 @@ public function run(Registry $registry): Registry
// schema modifiers
$this->generator->initModifiers($e, $class);

// foreign keys
$this->generator->initForeignKeys($ann, $e, $class);

// additional columns (mapped to local fields automatically)
$this->generator->initColumns($e, $ann->getColumns(), $class);

// foreign keys
$this->foreignKeysConfigurator->addFromEntity($e, $ann);
$this->foreignKeysConfigurator->addFromClass($e, $class);

if ($this->utils->hasParent($e->getClass())) {
foreach ($this->utils->findParents($e->getClass()) as $parent) {
// additional columns from parent class
$ann = $this->reader->firstClassMetadata($parent, Entity::class);
$this->generator->initColumns($e, $ann->getColumns(), $parent);

// additional foreign keys from parent class
$this->foreignKeysConfigurator->addFromEntity($e, $ann);
$this->foreignKeysConfigurator->addFromClass($e, $parent);
}

$children[] = $e;
Expand All @@ -98,9 +89,6 @@ public function run(Registry $registry): Registry
$registry->linkTable($e, $e->getDatabase(), $e->getTableName());
}

// register foreign keys
$this->foreignKeysConfigurator->configure($registry);

foreach ($children as $e) {
$registry->registerChildWithoutMerge($registry->getEntity($this->utils->findParent($e->getClass())), $e);
}
Expand All @@ -110,15 +98,14 @@ public function run(Registry $registry): Registry

private function normalizeNames(Registry $registry): Registry
{
// resolve all the relation target names into roles
foreach ($this->locator->getClasses() as $class) {
if (! $registry->hasEntity($class->getName())) {
continue;
}

$e = $registry->getEntity($class->getName());

// relations
// resolve all the relation target names into roles
foreach ($e->getRelations() as $name => $r) {
try {
$r->setTarget($this->utils->resolveTarget($registry, $r->getTarget()));
Expand Down Expand Up @@ -166,8 +153,40 @@ private function normalizeNames(Registry $registry): Registry
);
}
}

// resolve foreign key table and column names
foreach ($e->getForeignKeys() as $foreignKey) {
$target = $this->utils->resolveTarget($registry, $foreignKey->getTable());
\assert(!empty($target), 'Unable to resolve foreign key target entity.');
$targetEntity = $registry->getEntity($target);

$foreignKey->setTable($targetEntity->getTableName());
$foreignKey->setInnerColumns($this->getColumnNames($e, $foreignKey->getInnerColumns()));
$foreignKey->setOuterColumns($this->getColumnNames($targetEntity, $foreignKey->getOuterColumns()));
}
}

return $registry;
}

/**
* @param array<non-empty-string> $columns
*
* @throws AnnotationException
*
* @return array<non-empty-string>
*/
private function getColumnNames(EntitySchema $entity, array $columns): array
{
$names = [];
foreach ($columns as $name) {
$names[] = match (true) {
$entity->getFields()->has($name) => $entity->getFields()->get($name)->getColumn(),
$entity->getFields()->hasColumn($name) => $name,
default => throw new AnnotationException('Unable to resolve column name.'),
};
}

return $names;
}
}

0 comments on commit 2d683fc

Please sign in to comment.