Skip to content

Commit 50352f7

Browse files
mbabkerfranmomu
authored andcommitted
Add a compat trait to address B/C issues with EntityRepository
1 parent eebe57b commit 50352f7

File tree

3 files changed

+181
-88
lines changed

3 files changed

+181
-88
lines changed

phpstan.neon.dist

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,6 @@ parameters:
1919
- '#^Method Gedmo\\Uploadable\\Mapping\\Validator::validateConfiguration\(\) with return type void returns array<string, mixed> but should not return anything\.$#'
2020
- '#^Result of static method Gedmo\\Uploadable\\Mapping\\Validator::validateConfiguration\(\) \(void\) is used\.$#'
2121
- '#^Result of method Gedmo\\Mapping\\Driver::readExtendedMetadata\(\) \(void\) is used\.$#'
22+
excludePaths:
23+
# Generates non-ignorable errors like " Parameter #1 $method (string) of method Gedmo\Tree\Entity\Repository\NestedTreeRepository::__call() is not contravariant with parameter #1 $method (mixed) of method Doctrine\ORM\EntityRepository::__call()."
24+
- src/Tool/ORM/Repository/EntityRepositoryCompat.php
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Doctrine Behavioral Extensions package.
5+
* (c) Gediminas Morkevicius <[email protected]> http://www.gediminasm.org
6+
* For the full copyright and license information, please view the LICENSE
7+
* file that was distributed with this source code.
8+
*/
9+
10+
namespace Gedmo\Tool\ORM\Repository;
11+
12+
use Doctrine\ORM\EntityRepository;
13+
14+
if ((new \ReflectionClass(EntityRepository::class))->getMethod('__call')->hasReturnType()) {
15+
// ORM 3.x
16+
/**
17+
* Helper trait to address compatibility issues between ORM 2.x and 3.x.
18+
*
19+
* @mixin EntityRepository
20+
*
21+
* @internal
22+
*/
23+
trait EntityRepositoryCompat
24+
{
25+
/**
26+
* @param string $method
27+
* @param array $args
28+
*
29+
* @return mixed
30+
*
31+
* @phpstan-param list<mixed> $args
32+
*/
33+
public function __call(string $method, array $args): mixed
34+
{
35+
return $this->doCallWithCompat($method, $args);
36+
}
37+
38+
/**
39+
* @param string $method
40+
* @param array $args
41+
*
42+
* @return mixed
43+
*
44+
* @phpstan-param list<mixed> $args
45+
*/
46+
abstract protected function doCallWithCompat($method, $args);
47+
}
48+
} else {
49+
// ORM 2.x
50+
/**
51+
* Helper trait to address compatibility issues between ORM 2.x and 3.x.
52+
*
53+
* @mixin EntityRepository
54+
*
55+
* @internal
56+
*/
57+
trait EntityRepositoryCompat
58+
{
59+
/**
60+
* @param string $method
61+
* @param array $args
62+
*
63+
* @return mixed
64+
*
65+
* @phpstan-param list<mixed> $args
66+
*/
67+
public function __call($method, $args)
68+
{
69+
return $this->doCallWithCompat($method, $args);
70+
}
71+
72+
/**
73+
* @param string $method
74+
* @param array $args
75+
*
76+
* @return mixed
77+
*
78+
* @phpstan-param list<mixed> $args
79+
*/
80+
abstract protected function doCallWithCompat($method, $args);
81+
}
82+
}

src/Tree/Entity/Repository/NestedTreeRepository.php

Lines changed: 96 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Gedmo\Exception\InvalidArgumentException;
1717
use Gedmo\Exception\RuntimeException;
1818
use Gedmo\Exception\UnexpectedValueException;
19+
use Gedmo\Tool\ORM\Repository\EntityRepositoryCompat;
1920
use Gedmo\Tool\Wrapper\EntityWrapper;
2021
use Gedmo\Tree\Node;
2122
use Gedmo\Tree\Strategy;
@@ -43,94 +44,7 @@
4344
*/
4445
class NestedTreeRepository extends AbstractTreeRepository
4546
{
46-
/**
47-
* Allows the following 'virtual' methods:
48-
* - persistAsFirstChild($node)
49-
* - persistAsFirstChildOf($node, $parent)
50-
* - persistAsLastChild($node)
51-
* - persistAsLastChildOf($node, $parent)
52-
* - persistAsNextSibling($node)
53-
* - persistAsNextSiblingOf($node, $sibling)
54-
* - persistAsPrevSibling($node)
55-
* - persistAsPrevSiblingOf($node, $sibling)
56-
* Inherited virtual methods:
57-
* - find*
58-
*
59-
* @see \Doctrine\ORM\EntityRepository
60-
*
61-
* @throws InvalidArgumentException If arguments are invalid
62-
* @throws \BadMethodCallException If the method called is an invalid find* or persistAs* method
63-
* or no find* either persistAs* method at all and therefore an invalid method call
64-
*
65-
* @return mixed TreeNestedRepository if persistAs* is called
66-
*/
67-
public function __call($method, $args)
68-
{
69-
if ('persistAs' === substr($method, 0, 9)) {
70-
if (!isset($args[0])) {
71-
throw new InvalidArgumentException('Node to persist must be available as first argument.');
72-
}
73-
$node = $args[0];
74-
$wrapped = new EntityWrapper($node, $this->getEntityManager());
75-
$meta = $this->getClassMetadata();
76-
$config = $this->listener->getConfiguration($this->getEntityManager(), $meta->getName());
77-
$position = substr($method, 9);
78-
if ('Of' === substr($method, -2)) {
79-
if (!isset($args[1])) {
80-
throw new InvalidArgumentException('If "Of" is specified you must provide parent or sibling as the second argument.');
81-
}
82-
$parentOrSibling = $args[1];
83-
if (strstr($method, 'Sibling')) {
84-
$wrappedParentOrSibling = new EntityWrapper($parentOrSibling, $this->getEntityManager());
85-
$newParent = $wrappedParentOrSibling->getPropertyValue($config['parent']);
86-
if (null === $newParent && isset($config['root'])) {
87-
throw new UnexpectedValueException('Cannot persist sibling for a root node, tree operation is not possible');
88-
}
89-
90-
if (!$node instanceof Node) {
91-
@trigger_error(\sprintf(
92-
'Not implementing the "%s" interface from node "%s" is deprecated since gedmo/doctrine-extensions'
93-
.' 3.13 and will throw a "%s" error in version 4.0.',
94-
Node::class,
95-
\get_class($node),
96-
\TypeError::class
97-
), \E_USER_DEPRECATED);
98-
}
99-
100-
// @todo: In the next major release, remove the previous condition and uncomment the following one.
101-
102-
// if (!$node instanceof Node) {
103-
// throw new \TypeError(\sprintf(
104-
// 'Node MUST implement "%s" interface.',
105-
// Node::class
106-
// ));
107-
// }
108-
109-
// @todo: In the next major release, remove the `method_exists()` condition and left the `else` branch.
110-
if (!method_exists($node, 'setSibling')) {
111-
$node->sibling = $parentOrSibling;
112-
} else {
113-
$node->setSibling($parentOrSibling);
114-
}
115-
$parentOrSibling = $newParent;
116-
}
117-
$wrapped->setPropertyValue($config['parent'], $parentOrSibling);
118-
$position = substr($position, 0, -2);
119-
}
120-
$wrapped->setPropertyValue($config['left'], 0); // simulate changeset
121-
$oid = spl_object_id($node);
122-
$this->listener
123-
->getStrategy($this->getEntityManager(), $meta->getName())
124-
->setNodePosition($oid, $position)
125-
;
126-
127-
$this->getEntityManager()->persist($node);
128-
129-
return $this;
130-
}
131-
132-
return parent::__call($method, $args);
133-
}
47+
use EntityRepositoryCompat;
13448

13549
public function getRootNodesQueryBuilder($sortByField = null, $direction = 'asc')
13650
{
@@ -1169,6 +1083,100 @@ public function getNodesHierarchy($node = null, $direct = false, array $options
11691083
return $this->getNodesHierarchyQuery($node, $direct, $options, $includeNode)->getArrayResult();
11701084
}
11711085

1086+
/**
1087+
* Allows the following 'virtual' methods:
1088+
* - persistAsFirstChild($node)
1089+
* - persistAsFirstChildOf($node, $parent)
1090+
* - persistAsLastChild($node)
1091+
* - persistAsLastChildOf($node, $parent)
1092+
* - persistAsNextSibling($node)
1093+
* - persistAsNextSiblingOf($node, $sibling)
1094+
* - persistAsPrevSibling($node)
1095+
* - persistAsPrevSiblingOf($node, $sibling)
1096+
* Inherited virtual methods:
1097+
* - find*
1098+
*
1099+
* @param string $method
1100+
* @param array $args
1101+
*
1102+
* @phpstan-param list<mixed> $args
1103+
*
1104+
* @throws \BadMethodCallException If the method called is an invalid find* or persistAs* method
1105+
* or no find* either persistAs* method at all and therefore an invalid method call
1106+
* @throws InvalidArgumentException If arguments are invalid
1107+
*
1108+
* @return mixed TreeNestedRepository if persistAs* is called
1109+
*
1110+
* @see \Doctrine\ORM\EntityRepository
1111+
*/
1112+
protected function doCallWithCompat($method, $args)
1113+
{
1114+
if ('persistAs' === substr($method, 0, 9)) {
1115+
if (!isset($args[0])) {
1116+
throw new InvalidArgumentException('Node to persist must be available as first argument.');
1117+
}
1118+
$node = $args[0];
1119+
$wrapped = new EntityWrapper($node, $this->getEntityManager());
1120+
$meta = $this->getClassMetadata();
1121+
$config = $this->listener->getConfiguration($this->getEntityManager(), $meta->getName());
1122+
$position = substr($method, 9);
1123+
if ('Of' === substr($method, -2)) {
1124+
if (!isset($args[1])) {
1125+
throw new InvalidArgumentException('If "Of" is specified you must provide parent or sibling as the second argument.');
1126+
}
1127+
$parentOrSibling = $args[1];
1128+
if (strstr($method, 'Sibling')) {
1129+
$wrappedParentOrSibling = new EntityWrapper($parentOrSibling, $this->getEntityManager());
1130+
$newParent = $wrappedParentOrSibling->getPropertyValue($config['parent']);
1131+
if (null === $newParent && isset($config['root'])) {
1132+
throw new UnexpectedValueException('Cannot persist sibling for a root node, tree operation is not possible');
1133+
}
1134+
1135+
if (!$node instanceof Node) {
1136+
@trigger_error(\sprintf(
1137+
'Not implementing the "%s" interface from node "%s" is deprecated since gedmo/doctrine-extensions'
1138+
.' 3.13 and will throw a "%s" error in version 4.0.',
1139+
Node::class,
1140+
\get_class($node),
1141+
\TypeError::class
1142+
), \E_USER_DEPRECATED);
1143+
}
1144+
1145+
// @todo: In the next major release, remove the previous condition and uncomment the following one.
1146+
1147+
// if (!$node instanceof Node) {
1148+
// throw new \TypeError(\sprintf(
1149+
// 'Node MUST implement "%s" interface.',
1150+
// Node::class
1151+
// ));
1152+
// }
1153+
1154+
// @todo: In the next major release, remove the `method_exists()` condition and left the `else` branch.
1155+
if (!method_exists($node, 'setSibling')) {
1156+
$node->sibling = $parentOrSibling;
1157+
} else {
1158+
$node->setSibling($parentOrSibling);
1159+
}
1160+
$parentOrSibling = $newParent;
1161+
}
1162+
$wrapped->setPropertyValue($config['parent'], $parentOrSibling);
1163+
$position = substr($position, 0, -2);
1164+
}
1165+
$wrapped->setPropertyValue($config['left'], 0); // simulate changeset
1166+
$oid = spl_object_id($node);
1167+
$this->listener
1168+
->getStrategy($this->getEntityManager(), $meta->getName())
1169+
->setNodePosition($oid, $position)
1170+
;
1171+
1172+
$this->getEntityManager()->persist($node);
1173+
1174+
return $this;
1175+
}
1176+
1177+
return parent::__call($method, $args);
1178+
}
1179+
11721180
protected function validate()
11731181
{
11741182
return Strategy::NESTED === $this->listener->getStrategy($this->getEntityManager(), $this->getClassMetadata()->name)->getName();

0 commit comments

Comments
 (0)