Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@
"email": "wolfy-j@spiralscout.com"
},
{
"name": "Aleksei Gagarin (roxblnfk)",
"email": "alexey.gagarin@spiralscout.com"
"name": "Aleksei Gagarin",
"homepage": "https://github.com/roxblnfk"
},
{
"name": "Pavel Butchnev (butschster)",
"email": "pavel.buchnev@spiralscout.com"
"name": "Pavel Butchnev",
"homepage": "https://github.com/butschster"
},
{
"name": "Maksim Smakouz (msmakouz)",
Expand All @@ -38,20 +38,20 @@
],
"require": {
"php": ">=8.1",
"cycle/database": "^2.16",
"cycle/orm": "^2.15",
"cycle/schema-builder": "^2.11.1",
"spiral/attributes": "^2.8|^3.0",
"spiral/tokenizer": "^2.8|^3.0",
"doctrine/inflector": "^2.0"
"cycle/database": "^2.20",
"cycle/orm": "^2.18",
"cycle/schema-builder": "^2.12",
"spiral/attributes": "^2.8 || ^3.1",
"spiral/tokenizer": "^2.8 || ^3.17",
"doctrine/inflector": "^2.1"
},
"require-dev": {
"buggregator/trap": "^1.15",
"doctrine/annotations": "^1.14.3 || ^2.0.1",
"phpunit/phpunit": "^10.1",
"spiral/code-style": "^2.2",
"doctrine/annotations": "^1.14 || ^2.0",
"phpunit/phpunit": "^10.5",
"spiral/code-style": "^2.3",
"spiral/dumper": "^3.3",
"vimeo/psalm": "^5.26 || ^6.0"
"vimeo/psalm": "^5.26 || ^6.16"
},
"autoload": {
"psr-4": {
Expand Down
8 changes: 8 additions & 0 deletions psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,14 @@
<code><![CDATA[TYPE]]></code>
</InvalidClassConstantType>
</file>
<file src="src/Annotation/Relation/Morphed/RefersToMorphed.php">
<DeprecatedClass>
<code><![CDATA[NamedArgumentConstructor]]></code>
</DeprecatedClass>
<InvalidClassConstantType>
<code><![CDATA[TYPE]]></code>
</InvalidClassConstantType>
</file>
<file src="src/Annotation/Relation/RefersTo.php">
<DeprecatedClass>
<code><![CDATA[NamedArgumentConstructor]]></code>
Expand Down
41 changes: 32 additions & 9 deletions resources/relations.meta-storm.xml
Original file line number Diff line number Diff line change
Expand Up @@ -138,20 +138,43 @@

<!-- Belongs To Morphed -->
<!-- target: -->
<classConstructor class="\Cycle\Annotated\Annotation\Relation\Morphed\BelongsToMorphed" argument="1">
<classConstructor class="\Cycle\Annotated\Annotation\Relation\Morphed\BelongsToMorphed" argument="0">
<collection name="cycle/orm:entity-class" argument="0" />
<collection name="cycle/orm:entity-role" argument="0" />
</classConstructor>
<!-- innerKey: -->
<classConstructor class="\Cycle\Annotated\Annotation\Relation\Morphed\BelongsToMorphed" argument="1" targetInArray="value">
<classConstructor class="\Cycle\Annotated\Annotation\Relation\Morphed\BelongsToMorphed" argument="3" targetInArray="value">
<properties xpath="$containingClass">
<filters>
<hasAttribute class="\Cycle\Annotated\Annotation\Column"/>
</filters>
</properties>
</classConstructor>
<!-- outerKey: -->
<classConstructor class="\Cycle\Annotated\Annotation\Relation\Morphed\BelongsToMorphed" argument="2" targetInArray="value">
<classConstructor class="\Cycle\Annotated\Annotation\Relation\Morphed\BelongsToMorphed" argument="4" targetInArray="value">
<properties xpath="$argument[0]">
<filters>
<hasAttribute class="\Cycle\Annotated\Annotation\Column"/>
</filters>
</properties>
</classConstructor>

<!-- Refers To Morphed -->
<!-- target: -->
<classConstructor class="\Cycle\Annotated\Annotation\Relation\Morphed\RefersToMorphed" argument="0">
<collection name="cycle/orm:entity-class" argument="0" />
<collection name="cycle/orm:entity-role" argument="0" />
</classConstructor>
<!-- innerKey: -->
<classConstructor class="\Cycle\Annotated\Annotation\Relation\Morphed\RefersToMorphed" argument="3" targetInArray="value">
<properties xpath="$containingClass">
<filters>
<hasAttribute class="\Cycle\Annotated\Annotation\Column"/>
</filters>
</properties>
</classConstructor>
<!-- outerKey: -->
<classConstructor class="\Cycle\Annotated\Annotation\Relation\Morphed\RefersToMorphed" argument="4" targetInArray="value">
<properties xpath="$argument[0]">
<filters>
<hasAttribute class="\Cycle\Annotated\Annotation\Column"/>
Expand All @@ -161,20 +184,20 @@

<!-- Morphed Has Many -->
<!-- target: -->
<classConstructor class="\Cycle\Annotated\Annotation\Relation\Morphed\MorphedHasMany" argument="1">
<classConstructor class="\Cycle\Annotated\Annotation\Relation\Morphed\MorphedHasMany" argument="0">
<collection name="cycle/orm:entity-class" argument="0" />
<collection name="cycle/orm:entity-role" argument="0" />
</classConstructor>
<!-- innerKey: -->
<classConstructor class="\Cycle\Annotated\Annotation\Relation\Morphed\MorphedHasMany" argument="1" targetInArray="value">
<classConstructor class="\Cycle\Annotated\Annotation\Relation\Morphed\MorphedHasMany" argument="3" targetInArray="value">
<properties xpath="$containingClass">
<filters>
<hasAttribute class="\Cycle\Annotated\Annotation\Column"/>
</filters>
</properties>
</classConstructor>
<!-- outerKey: -->
<classConstructor class="\Cycle\Annotated\Annotation\Relation\Morphed\MorphedHasMany" argument="2" targetInArray="value">
<classConstructor class="\Cycle\Annotated\Annotation\Relation\Morphed\MorphedHasMany" argument="4" targetInArray="value">
<properties xpath="$argument[0]">
<filters>
<hasAttribute class="\Cycle\Annotated\Annotation\Column"/>
Expand All @@ -184,20 +207,20 @@

<!-- Morphed Has One -->
<!-- target: -->
<classConstructor class="\Cycle\Annotated\Annotation\Relation\Morphed\MorphedHasOne" argument="1">
<classConstructor class="\Cycle\Annotated\Annotation\Relation\Morphed\MorphedHasOne" argument="0">
<collection name="cycle/orm:entity-class" argument="0" />
<collection name="cycle/orm:entity-role" argument="0" />
</classConstructor>
<!-- innerKey: -->
<classConstructor class="\Cycle\Annotated\Annotation\Relation\Morphed\MorphedHasOne" argument="1" targetInArray="value">
<classConstructor class="\Cycle\Annotated\Annotation\Relation\Morphed\MorphedHasOne" argument="3" targetInArray="value">
<properties xpath="$containingClass">
<filters>
<hasAttribute class="\Cycle\Annotated\Annotation\Column"/>
</filters>
</properties>
</classConstructor>
<!-- outerKey: -->
<classConstructor class="\Cycle\Annotated\Annotation\Relation\Morphed\MorphedHasOne" argument="2" targetInArray="value">
<classConstructor class="\Cycle\Annotated\Annotation\Relation\Morphed\MorphedHasOne" argument="4" targetInArray="value">
<properties xpath="$argument[0]">
<filters>
<hasAttribute class="\Cycle\Annotated\Annotation\Column"/>
Expand Down
58 changes: 58 additions & 0 deletions src/Annotation/Relation/Morphed/RefersToMorphed.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

declare(strict_types=1);

namespace Cycle\Annotated\Annotation\Relation\Morphed;

use Cycle\Annotated\Annotation\Relation\Inverse;
use Cycle\Annotated\Annotation\Relation\Relation;
use Cycle\Annotated\Annotation\Relation\Traits\InverseTrait;
use Doctrine\Common\Annotations\Annotation\Target;
use JetBrains\PhpStorm\ExpectedValues;
use Spiral\Attributes\NamedArgumentConstructor;

/**
* Morphed variation of the refers-to relation. Like {@see BelongsToMorphed} it stores the outer key
* and the target role on the owner, but resolves the outer key in a deferred way, so it can be used
* for self-linked and cyclic morphed references.
*
* @Annotation
* @NamedArgumentConstructor
* @Target("PROPERTY")
*/
#[\Attribute(\Attribute::TARGET_PROPERTY), NamedArgumentConstructor]
class RefersToMorphed extends Relation
{
use InverseTrait;

protected const TYPE = 'refersToMorphed';

/**
* @param non-empty-string $target
* @param bool $cascade Automatically save related data with source entity.
* @param bool $nullable Defines if the relation can be nullable (child can have no parent).
* @param array|non-empty-string|null $innerKey Inner key in source entity. Defaults to `{relationName}_{outerKey}`.
* @param array|non-empty-string|null $outerKey Outer key in the related entity. Defaults to primary key.
* @param non-empty-string|null $morphKey Name of key to store related entity role. Defaults to `{relationName}_role`.
* @param int $morphKeyLength The length of morph key.
* @param bool $indexCreate Create an index on morphKey and innerKey.
* @param non-empty-string $load Relation load approach.
*/
public function __construct(
string $target,
protected bool $cascade = true,
protected bool $nullable = true,
protected array|string|null $innerKey = null,
protected array|string|null $outerKey = null,
protected ?string $morphKey = null,
protected int $morphKeyLength = 32,
protected bool $indexCreate = true,
#[ExpectedValues(values: ['lazy', 'eager'])]
string $load = 'lazy',
?Inverse $inverse = null,
) {
$this->inverse = $inverse;

parent::__construct($target, $load);
}
}
28 changes: 28 additions & 0 deletions tests/Annotated/Fixtures/RefersToMorphed/Comment.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace Cycle\Annotated\Tests\Fixtures\RefersToMorphed;

use Cycle\Annotated\Annotation\Column;
use Cycle\Annotated\Annotation\Entity;
use Cycle\Annotated\Annotation\Relation\Morphed\RefersToMorphed;

/**
* @Entity
*/
#[Entity]
class Comment
{
/** @Column(type="primary") */
#[Column(type: 'primary')]
protected $id;

/** @Column(type="string") */
#[Column(type: 'string')]
protected $message;

/** @RefersToMorphed(target="MorphedParentInterface") */
#[RefersToMorphed(target: 'MorphedParentInterface')]
protected $parent;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php

declare(strict_types=1);

namespace Cycle\Annotated\Tests\Fixtures\RefersToMorphed;

interface MorphedParentInterface {}
23 changes: 23 additions & 0 deletions tests/Annotated/Fixtures/RefersToMorphed/Post.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

declare(strict_types=1);

namespace Cycle\Annotated\Tests\Fixtures\RefersToMorphed;

use Cycle\Annotated\Annotation\Column;
use Cycle\Annotated\Annotation\Entity;

/**
* @Entity
*/
#[Entity]
class Post implements MorphedParentInterface
{
/** @Column(type="primary") */
#[Column(type: 'primary')]
protected $id;

/** @Column(type="string") */
#[Column(type: 'string')]
protected $title;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

declare(strict_types=1);

namespace Cycle\Annotated\Tests\Functional\Driver\Common\Relation\Morphed;

use Cycle\Annotated\Entities;
use Cycle\Annotated\Locator\TokenizerEntityLocator;
use Cycle\Annotated\MergeColumns;
use Cycle\Annotated\MergeIndexes;
use Cycle\Annotated\Tests\Fixtures\RefersToMorphed\MorphedParentInterface;
use Cycle\Annotated\Tests\Functional\Driver\Common\BaseTestCase;
use Cycle\ORM\Relation;
use Cycle\ORM\SchemaInterface as Schema;
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\Registry;
use PHPUnit\Framework\Attributes\DataProvider;
use Spiral\Attributes\ReaderInterface;
use Spiral\Tokenizer\Config\TokenizerConfig;
use Spiral\Tokenizer\Tokenizer;

abstract class RefersToMorphedTestCase extends BaseTestCase
{
#[DataProvider('allReadersProvider')]
public function testRelation(ReaderInterface $reader): void
{
$locator = (new Tokenizer(new TokenizerConfig([
'directories' => [__DIR__ . '/../../../../../Fixtures/RefersToMorphed'],
'exclude' => [],
])))->classLocator();

$r = new Registry($this->dbal);

$schema = (new Compiler())->compile($r, [
new Entities(new TokenizerEntityLocator($locator, $reader), $reader),
new ResetTables(),
new MergeColumns($reader),
new GenerateRelations(),
new RenderTables(),
new RenderRelations(),
new MergeIndexes($reader),
new SyncTables(),
new GenerateTypecast(),
]);

$this->assertArrayHasKey('parent', $schema['comment'][Schema::RELATIONS]);
$this->assertSame(
Relation::REFERS_TO_MORPHED,
$schema['comment'][Schema::RELATIONS]['parent'][Relation::TYPE],
);
$this->assertSame(
MorphedParentInterface::class,
$schema['comment'][Schema::RELATIONS]['parent'][Relation::TARGET],
);

// Morphed refers-to stores the outer key and the target role on the source entity.
$this->assertContains('parent_id', $schema['comment'][Schema::COLUMNS]);
$this->assertContains('parent_role', $schema['comment'][Schema::COLUMNS]);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace Cycle\Annotated\Tests\Functional\Driver\MySQL\Relation\Morphed;

// phpcs:ignore
use Cycle\Annotated\Tests\Functional\Driver\Common\Relation\Morphed\RefersToMorphedTestCase;
use PHPUnit\Framework\Attributes\Group;

#[Group('driver')]
#[Group('driver-mysql')]
final class RefersToMorphedTest extends RefersToMorphedTestCase
{
public const DRIVER = 'mysql';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace Cycle\Annotated\Tests\Functional\Driver\Postgres\Relation\Morphed;

// phpcs:ignore
use Cycle\Annotated\Tests\Functional\Driver\Common\Relation\Morphed\RefersToMorphedTestCase;
use PHPUnit\Framework\Attributes\Group;

#[Group('driver')]
#[Group('driver-postgres')]
final class RefersToMorphedTest extends RefersToMorphedTestCase
{
public const DRIVER = 'postgres';
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

namespace Cycle\Annotated\Tests\Functional\Driver\SQLServer\Relation\Morphed;

// phpcs:ignore
use Cycle\Annotated\Tests\Functional\Driver\Common\Relation\Morphed\RefersToMorphedTestCase;
use PHPUnit\Framework\Attributes\Group;

#[Group('driver')]
#[Group('driver-sqlserver')]
final class RefersToMorphedTest extends RefersToMorphedTestCase
{
public const DRIVER = 'sqlserver';
}
Loading
Loading