Skip to content
Draft
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ a release.
---

## [Unreleased]
### Added
- Tree: Support of Symfony UIDs at binary format

## [3.16.1]
### Fixed
Expand Down
25 changes: 16 additions & 9 deletions src/Tree/Strategy/ORM/Nested.php
Original file line number Diff line number Diff line change
Expand Up @@ -508,8 +508,10 @@ public function updateNode(EntityManagerInterface $em, $node, $parent, $position
$qb = $em->createQueryBuilder();
$qb->update($config['useObjectClass'], 'node');
if (isset($config['root'])) {
$wrappedNewRoot = AbstractWrapper::wrap($newRoot, $em);
$newRootId = $wrappedNewRoot->getIdentifier();
$qb->set('node.'.$config['root'], ':rid');
$qb->setParameter('rid', $newRoot);
$qb->setParameter('rid', $newRootId, $meta->getTypeOfField($identifierField));
$wrapped->setPropertyValue($config['root'], $newRoot);
$em->getUnitOfWork()->setOriginalEntityProperty($oid, $config['root'], $newRoot);
}
Expand All @@ -522,15 +524,15 @@ public function updateNode(EntityManagerInterface $em, $node, $parent, $position
$wrappedNewParent = AbstractWrapper::wrap($newParent, $em);
$newParentId = $wrappedNewParent->getIdentifier();
$qb->set('node.'.$config['parent'], ':pid');
$qb->setParameter('pid', $newParentId);
$qb->setParameter('pid', $newParentId, $meta->getTypeOfField($identifierField));
$wrapped->setPropertyValue($config['parent'], $newParent);
$em->getUnitOfWork()->setOriginalEntityProperty($oid, $config['parent'], $newParent);
}
$qb->set('node.'.$config['left'], $left + $diff);
$qb->set('node.'.$config['right'], $right + $diff);
// node id cannot be null
$qb->where($qb->expr()->eq('node.'.$identifierField, ':id'));
$qb->setParameter('id', $nodeId);
$qb->setParameter('id', $nodeId, $meta->getTypeOfField($identifierField));
$qb->getQuery()->getSingleScalarResult();
$wrapped->setPropertyValue($config['left'], $left + $diff);
$wrapped->setPropertyValue($config['right'], $right + $diff);
Expand Down Expand Up @@ -575,10 +577,10 @@ public function max(EntityManagerInterface $em, $class, $rootId = 0)
/**
* Shift tree left and right values by delta
*
* @param string $class
* @param int $first
* @param int $delta
* @param int|string $root
* @param string $class
* @param int $first
* @param int $delta
* @param int|string|object|null $root
*
* @phpstan-param class-string $class
*
Expand All @@ -587,8 +589,13 @@ public function max(EntityManagerInterface $em, $class, $rootId = 0)
public function shiftRL(EntityManagerInterface $em, $class, $first, $delta, $root = null)
{
$meta = $em->getClassMetadata($class);
$identifierField = $meta->getSingleIdentifierFieldName();
$config = $this->listener->getConfiguration($em, $class);

if (isset($config['root']) && is_object($root)) {
$root = AbstractWrapper::wrap($root, $em)->getIdentifier();
}

$sign = ($delta >= 0) ? ' + ' : ' - ';
$absDelta = abs($delta);
$qb = $em->createQueryBuilder();
Expand All @@ -597,7 +604,7 @@ public function shiftRL(EntityManagerInterface $em, $class, $first, $delta, $roo
->where($qb->expr()->gte('node.'.$config['left'], $first));
if (isset($config['root'])) {
$qb->andWhere($qb->expr()->eq('node.'.$config['root'], ':rid'));
$qb->setParameter('rid', $root);
$qb->setParameter('rid', $root, $meta->getTypeOfField($identifierField));
}
$qb->getQuery()->getSingleScalarResult();

Expand All @@ -607,7 +614,7 @@ public function shiftRL(EntityManagerInterface $em, $class, $first, $delta, $roo
->where($qb->expr()->gte('node.'.$config['right'], $first));
if (isset($config['root'])) {
$qb->andWhere($qb->expr()->eq('node.'.$config['root'], ':rid'));
$qb->setParameter('rid', $root);
$qb->setParameter('rid', $root, $meta->getTypeOfField($identifierField));
}

$qb->getQuery()->getSingleScalarResult();
Expand Down
166 changes: 166 additions & 0 deletions tests/Gedmo/Tree/Fixture/RootAssociationCategoryBinaryUuid.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
<?php

declare(strict_types=1);

/*
* This file is part of the Doctrine Behavioral Extensions package.
* (c) Gediminas Morkevicius <[email protected]> http://www.gediminasm.org
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Gedmo\Tests\Tree\Fixture;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Mapping\Annotation as Gedmo;
use Gedmo\Tree\Entity\Repository\NestedTreeRepository;
use Symfony\Bridge\Doctrine\Types\UuidType;
use Symfony\Component\Uid\Uuid;

/**
* @ORM\Entity(repositoryClass="Gedmo\Tree\Entity\Repository\NestedTreeRepository")
*
* @Gedmo\Tree(type="nested")
*/
#[ORM\Entity(repositoryClass: NestedTreeRepository::class)]
#[Gedmo\Tree(type: 'nested')]
class RootAssociationCategoryBinaryUuid
{
/**
* @var Collection<int, self>
*
* @ORM\OneToMany(targetEntity="RootAssociationCategory", mappedBy="parent")
*/
#[ORM\OneToMany(targetEntity: self::class, mappedBy: 'parent')]
protected $children;
/**
* @ORM\Column(name="id", type="uuid", nullable=false)
* @ORM\Id
* @ORM\GeneratedValue(strategy="NONE")
*/
#[ORM\Id]
#[ORM\GeneratedValue(strategy: 'NONE')]
#[ORM\Column(name: 'id', type: UuidType::NAME, nullable: false)]
private ?Uuid $id = null;

/**
* @ORM\Column(name="title", type="string", length=64)
*/
#[ORM\Column(name: 'title', type: Types::STRING, length: 64)]
private ?string $title = null;

/**
* @var int
*
* @Gedmo\TreeLeft
*
* @ORM\Column(name="lft", type="integer")
*/
#[ORM\Column(name: 'lft', type: Types::INTEGER)]
#[Gedmo\TreeLeft]
private int $lft;

/**
* @var int
*
* @Gedmo\TreeRight
*
* @ORM\Column(name="rgt", type="integer")
*/
#[ORM\Column(name: 'rgt', type: Types::INTEGER)]
#[Gedmo\TreeRight]
private int $rgt;

/**
* @Gedmo\TreeParent
*
* @ORM\ManyToOne(targetEntity="RootAssociationCategory", inversedBy="children")
* @ORM\JoinColumns({
* @ORM\JoinColumn(name="parent_id", referencedColumnName="id", onDelete="CASCADE")
* })
*/
#[ORM\ManyToOne(targetEntity: self::class, inversedBy: 'children')]
#[ORM\JoinColumn(name: 'parent_id', referencedColumnName: 'id', onDelete: 'CASCADE')]
#[Gedmo\TreeParent]
private ?RootAssociationCategoryBinaryUuid $parent = null;

/**
* @var self|null
*
* @Gedmo\TreeRoot
*
* @ORM\ManyToOne(targetEntity="RootAssociationCategory")
* @ORM\JoinColumns({
* @ORM\JoinColumn(name="tree_root", referencedColumnName="id", onDelete="CASCADE")
* })
*/
#[ORM\ManyToOne(targetEntity: self::class)]
#[ORM\JoinColumn(name: 'tree_root', referencedColumnName: 'id', onDelete: 'CASCADE')]
#[Gedmo\TreeRoot]
private ?RootAssociationCategoryBinaryUuid $root = null;

/**
* @var int
*
* @Gedmo\TreeLevel
*
* @ORM\Column(name="lvl", type="integer")
*/
#[ORM\Column(name: 'lvl', type: Types::INTEGER)]
#[Gedmo\TreeLevel]
private int $level;

public function __construct()
{
$this->id = Uuid::v7();
$this->children = new ArrayCollection();
}

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

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

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

public function setParent(?self $parent = null): void
{
$this->parent = $parent;
}

public function getParent(): ?self
{
return $this->parent;
}

public function getRoot(): ?self
{
return $this->root;
}

public function getLeft(): ?int
{
return $this->lft;
}

public function getRight(): ?int
{
return $this->rgt;
}

public function getLevel(): ?int
{
return $this->level;
}
}
68 changes: 68 additions & 0 deletions tests/Gedmo/Tree/NestedTreePositionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
use Doctrine\Common\EventManager;
use Gedmo\Tests\Tool\BaseTestCaseORM;
use Gedmo\Tests\Tree\Fixture\Category;
use Gedmo\Tests\Tree\Fixture\RootAssociationCategoryBinaryUuid;
use Gedmo\Tests\Tree\Fixture\RootCategory;
use Gedmo\Tree\TreeListener;

Expand All @@ -26,6 +27,7 @@ final class NestedTreePositionTest extends BaseTestCaseORM
{
private const CATEGORY = Category::class;
private const ROOT_CATEGORY = RootCategory::class;
private const ROOT_CATEGORY_BINARY_UUID = RootAssociationCategoryBinaryUuid::class;

protected function setUp(): void
{
Expand Down Expand Up @@ -335,6 +337,72 @@ public function testRootTreePositionedInserts(): void
static::assertTrue($repo->verify());
}

/**
* This test is the same as above (testRootTreePositionedInserts), apart testing behavior of binary column for UUID
*/
public function testRootBinaryUuidTreePositionedInserts(): void
{
$repo = $this->em->getRepository(self::ROOT_CATEGORY_BINARY_UUID);

// test child positioned inserts
$food = new RootAssociationCategoryBinaryUuid();
$food->setTitle('Food');

$fruits = new RootAssociationCategoryBinaryUuid();
$fruits->setTitle('Fruits');

$vegitables = new RootAssociationCategoryBinaryUuid();
$vegitables->setTitle('Vegitables');

$milk = new RootAssociationCategoryBinaryUuid();
$milk->setTitle('Milk');

$meat = new RootAssociationCategoryBinaryUuid();
$meat->setTitle('Meat');

$repo
->persistAsFirstChild($food)
->persistAsFirstChildOf($fruits, $food)
->persistAsFirstChildOf($vegitables, $food)
->persistAsLastChildOf($milk, $food)
->persistAsLastChildOf($meat, $food);

$this->em->flush();

static::assertSame(4, $fruits->getLeft());
static::assertSame(5, $fruits->getRight());

static::assertSame(2, $vegitables->getLeft());
static::assertSame(3, $vegitables->getRight());

static::assertSame(6, $milk->getLeft());
static::assertSame(7, $milk->getRight());

static::assertSame(8, $meat->getLeft());
static::assertSame(9, $meat->getRight());

// test sibling positioned inserts
$cookies = new RootAssociationCategoryBinaryUuid();
$cookies->setTitle('Cookies');

$drinks = new RootAssociationCategoryBinaryUuid();
$drinks->setTitle('Drinks');

$repo
->persistAsNextSiblingOf($cookies, $milk)
->persistAsPrevSiblingOf($drinks, $milk);

$this->em->flush();

static::assertSame(6, $drinks->getLeft());
static::assertSame(7, $drinks->getRight());

static::assertSame(10, $cookies->getLeft());
static::assertSame(11, $cookies->getRight());

static::assertTrue($repo->verify());
}

public function testRootlessTreeTopLevelInserts(): void
{
$repo = $this->em->getRepository(self::CATEGORY);
Expand Down