Skip to content

Commit

Permalink
Allow multiple relationships to a target entity. (#615)
Browse files Browse the repository at this point in the history
Co-authored-by: Mike Yudin <[email protected]>
  • Loading branch information
mikeyudin and Mike Yudin authored Apr 17, 2024
1 parent a38edf4 commit f65debc
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 1 deletion.
3 changes: 2 additions & 1 deletion src/EventListener/LogRevisionsListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -464,7 +464,8 @@ private function getInsertRevisionSQL(EntityManagerInterface $em, ClassMetadata
*/
private function getInsertJoinTableRevisionSQL(ClassMetadata $class, ClassMetadata $targetClass, array $assoc): string
{
$cacheKey = $class->name.'.'.$targetClass->name;
$cacheKey = $class->name.'.'.$targetClass->name.'.'.$assoc['joinTable']['name'];

if (!isset($this->insertJoinTableRevisionSQL[$cacheKey])
&& isset($assoc['relationToSourceKeyColumns'], $assoc['relationToTargetKeyColumns'], $assoc['joinTable']['name'])) {
$placeholders = ['?', '?'];
Expand Down
93 changes: 93 additions & 0 deletions tests/Fixtures/Relation/ManyToManyMultipleRelationshipEntity.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Sonata\EntityAuditBundle\Tests\Fixtures\Relation;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
class ManyToManyMultipleRelationshipEntity
{
/**
* @var int|null
*/
#[ORM\Id]
#[ORM\Column(type: Types::INTEGER)]
#[ORM\GeneratedValue]
protected $id;

/**
* @var string|null
*/
#[ORM\Column(type: Types::STRING)]
protected $title;

/**
* @var Collection<int, ManyToManyMultipleTargetEntity>
*/
#[ORM\ManyToMany(targetEntity: ManyToManyMultipleTargetEntity::class)]
#[ORM\JoinTable(name: 'many_to_many_primary_target')]
protected $primaryTargets = [];

Check failure on line 43 in tests/Fixtures/Relation/ManyToManyMultipleRelationshipEntity.php

View workflow job for this annotation

GitHub Actions / PHPStan

Property Sonata\EntityAuditBundle\Tests\Fixtures\Relation\ManyToManyMultipleRelationshipEntity::$primaryTargets (Doctrine\Common\Collections\Collection<int, Sonata\EntityAuditBundle\Tests\Fixtures\Relation\ManyToManyMultipleTargetEntity>) does not accept default value of type array{}.

Check failure on line 43 in tests/Fixtures/Relation/ManyToManyMultipleRelationshipEntity.php

View workflow job for this annotation

GitHub Actions / Psalm

InvalidPropertyAssignmentValue

tests/Fixtures/Relation/ManyToManyMultipleRelationshipEntity.php:43:33: InvalidPropertyAssignmentValue: $this->primaryTargets with declared type 'Doctrine\Common\Collections\Collection<int, Sonata\EntityAuditBundle\Tests\Fixtures\Relation\ManyToManyMultipleTargetEntity>' cannot be assigned type 'array<never, never>' (see https://psalm.dev/145)

/**
* @var Collection<int, ManyToManyMultipleTargetEntity>
*/
#[ORM\ManyToMany(targetEntity: ManyToManyMultipleTargetEntity::class)]
#[ORM\JoinTable(name: 'many_to_many_secondary_target')]
protected $secondaryTargets;

public function __construct()
{
$this->primaryTargets = new ArrayCollection();
$this->secondaryTargets = new ArrayCollection();
}


public function getId(): ?int
{
return $this->id;
}

public function getTitle(): ?string
{
return $this->title;
}

public function setTitle(string $title): void
{
$this->title = $title;
}

public function getPrimaryTargets(): ArrayCollection|Collection|array

Check failure on line 74 in tests/Fixtures/Relation/ManyToManyMultipleRelationshipEntity.php

View workflow job for this annotation

GitHub Actions / PHPStan

Method Sonata\EntityAuditBundle\Tests\Fixtures\Relation\ManyToManyMultipleRelationshipEntity::getPrimaryTargets() return type has no value type specified in iterable type array.

Check failure on line 74 in tests/Fixtures/Relation/ManyToManyMultipleRelationshipEntity.php

View workflow job for this annotation

GitHub Actions / PHPStan

Method Sonata\EntityAuditBundle\Tests\Fixtures\Relation\ManyToManyMultipleRelationshipEntity::getPrimaryTargets() return type has no value type specified in iterable type array|Doctrine\Common\Collections\Collection.

Check failure on line 74 in tests/Fixtures/Relation/ManyToManyMultipleRelationshipEntity.php

View workflow job for this annotation

GitHub Actions / PHPStan

Method Sonata\EntityAuditBundle\Tests\Fixtures\Relation\ManyToManyMultipleRelationshipEntity::getPrimaryTargets() return type with generic interface Doctrine\Common\Collections\Collection does not specify its types: TKey, T
{
return $this->primaryTargets;
}

public function addPrimaryTarget(ManyToManyMultipleTargetEntity $target): void
{
$this->primaryTargets[] = $target;
}

public function getSecondaryTargets(): ArrayCollection|Collection

Check failure on line 84 in tests/Fixtures/Relation/ManyToManyMultipleRelationshipEntity.php

View workflow job for this annotation

GitHub Actions / PHPStan

Method Sonata\EntityAuditBundle\Tests\Fixtures\Relation\ManyToManyMultipleRelationshipEntity::getSecondaryTargets() return type with generic interface Doctrine\Common\Collections\Collection does not specify its types: TKey, T
{
return $this->secondaryTargets;
}

public function addSecondaryTarget(ManyToManyMultipleTargetEntity $target): void
{
$this->secondaryTargets[] = $target;
}
}
38 changes: 38 additions & 0 deletions tests/Fixtures/Relation/ManyToManyMultipleTargetEntity.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Sonata Project package.
*
* (c) Thomas Rabaix <[email protected]>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Sonata\EntityAuditBundle\Tests\Fixtures\Relation;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity]
class ManyToManyMultipleTargetEntity
{
/**
* @var int|null
*/
#[ORM\Id]
#[ORM\Column(type: Types::INTEGER)]
#[ORM\GeneratedValue]
protected $id;


public function getId(): ?int
{
return $this->id;
}

}
43 changes: 43 additions & 0 deletions tests/RelationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
use Sonata\EntityAuditBundle\Tests\Fixtures\Relation\DataLegalEntity;
use Sonata\EntityAuditBundle\Tests\Fixtures\Relation\DataPrivateEntity;
use Sonata\EntityAuditBundle\Tests\Fixtures\Relation\FoodCategory;
use Sonata\EntityAuditBundle\Tests\Fixtures\Relation\ManyToManyMultipleRelationshipEntity;
use Sonata\EntityAuditBundle\Tests\Fixtures\Relation\ManyToManyMultipleTargetEntity;
use Sonata\EntityAuditBundle\Tests\Fixtures\Relation\OneToOneAuditedEntity;
use Sonata\EntityAuditBundle\Tests\Fixtures\Relation\OneToOneMasterEntity;
use Sonata\EntityAuditBundle\Tests\Fixtures\Relation\OneToOneNotAuditedEntity;
Expand Down Expand Up @@ -65,6 +67,8 @@ final class RelationTest extends BaseTest
DataLegalEntity::class,
DataPrivateEntity::class,
DataContainerEntity::class,
ManyToManyMultipleRelationshipEntity::class,
ManyToManyMultipleTargetEntity::class,
];

protected $auditedEntities = [
Expand All @@ -87,6 +91,8 @@ final class RelationTest extends BaseTest
DataLegalEntity::class,
DataPrivateEntity::class,
DataContainerEntity::class,
ManyToManyMultipleRelationshipEntity::class,
ManyToManyMultipleTargetEntity::class,
];

public function testUndefinedIndexesInUOWForRelations(): void
Expand Down Expand Up @@ -358,6 +364,43 @@ public function testManyToMany(): void
static::assertCount(1, $audited->getOwnedInverse());
}

public function testManyToManyMultipleRelationshipSameTargetEntity(): void
{
$em = $this->getEntityManager();
$auditReader = $this->getAuditManager()->createAuditReader($em);

// create an entity that has multiple many-to-many relationships to the same target entity.

$targetOne = new ManyToManyMultipleTargetEntity();
$targetTwo = new ManyToManyMultipleTargetEntity();

$manyToMany = new ManyToManyMultipleRelationshipEntity();
$manyToMany->setTitle('manyToMany#1');
$manyToMany->addPrimaryTarget($targetOne);
$manyToMany->addSecondaryTarget($targetTwo);

$em->persist($targetOne);
$em->persist($targetTwo);
$em->persist($manyToMany);

$em->flush(); // #1

$manyToManyId = $manyToMany->getId();
static::assertNotNull($manyToManyId);

$audited = $auditReader->find(ManyToManyMultipleRelationshipEntity::class, $manyToManyId, 1);

static::assertNotNull($audited);

// ensure that there is an audited entry for the primaryTargets property
static::assertInstanceOf(Collection::class, $audited->getPrimaryTargets());
static::assertCount(1, $audited->getPrimaryTargets());

// ensure that there is an audited entry for the secondaryTargets property
static::assertInstanceOf(Collection::class, $audited->getSecondaryTargets());
static::assertCount(1, $audited->getSecondaryTargets());
}

/**
* @group mysql
*/
Expand Down

0 comments on commit f65debc

Please sign in to comment.