Skip to content

Commit 7284c22

Browse files
authored
Merge pull request #175 from fsevestre/173-chainable-filter
2 parents 14daed4 + 0de0336 commit 7284c22

File tree

7 files changed

+168
-1
lines changed

7 files changed

+168
-1
lines changed

README.md

+34-1
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,9 @@ $matcher = new TypeMatcher('Doctrine\Common\Collections\Collection');
186186
- `DeepCopy\Filter` applies a transformation to the object attribute matched by `DeepCopy\Matcher`
187187
- `DeepCopy\TypeFilter` applies a transformation to any element matched by `DeepCopy\TypeMatcher`
188188

189+
By design, matching a filter will stop the chain of filters (i.e. the next ones will not be applied).
190+
Using the ([`ChainableFilter`](#chainablefilter-filter)) won't stop the chain of filters.
191+
189192

190193
#### `SetNullFilter` (filter)
191194

@@ -226,6 +229,34 @@ $copy = $copier->copy($object);
226229
```
227230

228231

232+
#### `ChainableFilter` (filter)
233+
234+
If you use cloning on proxy classes, you might want to apply two filters for:
235+
1. loading the data
236+
2. applying a transformation
237+
238+
You can use the `ChainableFilter` as a decorator of the proxy loader filter, which won't stop the chain of filters (i.e.
239+
the next ones may be applied).
240+
241+
242+
```php
243+
use DeepCopy\DeepCopy;
244+
use DeepCopy\Filter\ChainableFilter;
245+
use DeepCopy\Filter\Doctrine\DoctrineProxyFilter;
246+
use DeepCopy\Filter\SetNullFilter;
247+
use DeepCopy\Matcher\Doctrine\DoctrineProxyMatcher;
248+
use DeepCopy\Matcher\PropertyNameMatcher;
249+
250+
$copier = new DeepCopy();
251+
$copier->addFilter(new ChainableFilter(new DoctrineProxyFilter()), new DoctrineProxyMatcher());
252+
$copier->addFilter(new SetNullFilter(), new PropertyNameMatcher('id'));
253+
254+
$copy = $copier->copy($object);
255+
256+
echo $copy->id; // null
257+
```
258+
259+
229260
#### `DoctrineCollectionFilter` (filter)
230261

231262
If you use Doctrine and want to copy an entity, you will need to use the `DoctrineCollectionFilter`:
@@ -268,14 +299,16 @@ Doctrine proxy class (...\\\_\_CG\_\_\Proxy).
268299
You can use the `DoctrineProxyFilter` to load the actual entity behind the Doctrine proxy class.
269300
**Make sure, though, to put this as one of your very first filters in the filter chain so that the entity is loaded
270301
before other filters are applied!**
302+
We recommend to decorate the `DoctrineProxyFilter` with the `ChainableFilter` to allow applying other filters to the
303+
cloned lazy loaded entities.
271304

272305
```php
273306
use DeepCopy\DeepCopy;
274307
use DeepCopy\Filter\Doctrine\DoctrineProxyFilter;
275308
use DeepCopy\Matcher\Doctrine\DoctrineProxyMatcher;
276309

277310
$copier = new DeepCopy();
278-
$copier->addFilter(new DoctrineProxyFilter(), new DoctrineProxyMatcher());
311+
$copier->addFilter(new ChainableFilter(new DoctrineProxyFilter()), new DoctrineProxyMatcher());
279312

280313
$copy = $copier->copy($object);
281314

fixtures/f013/A.php

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace DeepCopy\f013;
4+
5+
use Doctrine\Persistence\Proxy;
6+
7+
class A implements Proxy
8+
{
9+
public $foo = 1;
10+
11+
/**
12+
* @inheritdoc
13+
*/
14+
public function __load()
15+
{
16+
}
17+
18+
/**
19+
* @inheritdoc
20+
*/
21+
public function __isInitialized()
22+
{
23+
}
24+
}

fixtures/f013/B.php

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
namespace DeepCopy\f013;
4+
5+
use Doctrine\Persistence\Proxy;
6+
7+
class B implements Proxy
8+
{
9+
private $foo;
10+
11+
/**
12+
* @inheritdoc
13+
*/
14+
public function __load()
15+
{
16+
}
17+
18+
/**
19+
* @inheritdoc
20+
*/
21+
public function __isInitialized()
22+
{
23+
}
24+
25+
public function getFoo()
26+
{
27+
return $this->foo;
28+
}
29+
30+
public function setFoo($foo)
31+
{
32+
$this->foo = $foo;
33+
}
34+
}

fixtures/f013/C.php

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
namespace DeepCopy\f013;
4+
5+
class C
6+
{
7+
public $foo = 1;
8+
9+
public function __clone()
10+
{
11+
$this->foo = null;
12+
}
13+
}

src/DeepCopy/DeepCopy.php

+5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use DateTimeInterface;
88
use DateTimeZone;
99
use DeepCopy\Exception\CloneException;
10+
use DeepCopy\Filter\ChainableFilter;
1011
use DeepCopy\Filter\Filter;
1112
use DeepCopy\Matcher\Matcher;
1213
use DeepCopy\Reflection\ReflectionHelper;
@@ -239,6 +240,10 @@ function ($object) {
239240
}
240241
);
241242

243+
if ($filter instanceof ChainableFilter) {
244+
continue;
245+
}
246+
242247
// If a filter matches, we stop processing this property
243248
return;
244249
}
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<?php
2+
3+
namespace DeepCopy\Filter;
4+
5+
/**
6+
* Defines a decorator filter that will not stop the chain of filters.
7+
*/
8+
class ChainableFilter implements Filter
9+
{
10+
/**
11+
* @var Filter
12+
*/
13+
protected $filter;
14+
15+
public function __construct(Filter $filter)
16+
{
17+
$this->filter = $filter;
18+
}
19+
20+
public function apply($object, $property, $objectCopier)
21+
{
22+
$this->filter->apply($object, $property, $objectCopier);
23+
}
24+
}

tests/DeepCopyTest/DeepCopyTest.php

+34
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,12 @@
2020
use DeepCopy\f009;
2121
use DeepCopy\f011;
2222
use DeepCopy\f012\Suit;
23+
use DeepCopy\f013;
24+
use DeepCopy\Filter\ChainableFilter;
25+
use DeepCopy\Filter\Doctrine\DoctrineProxyFilter;
2326
use DeepCopy\Filter\KeepFilter;
2427
use DeepCopy\Filter\SetNullFilter;
28+
use DeepCopy\Matcher\Doctrine\DoctrineProxyMatcher;
2529
use DeepCopy\Matcher\PropertyNameMatcher;
2630
use DeepCopy\Matcher\PropertyTypeMatcher;
2731
use DeepCopy\TypeFilter\ShallowCopyFilter;
@@ -508,6 +512,36 @@ public function test_it_keeps_enums()
508512
$this->assertSame($enum, $copy);
509513
}
510514

515+
/**
516+
* @ticket https://github.com/myclabs/DeepCopy/issues/98
517+
*/
518+
public function test_it_can_apply_two_filters_with_chainable_filter()
519+
{
520+
$object = new f013\A();
521+
522+
$deepCopy = new DeepCopy();
523+
$deepCopy->addFilter(new ChainableFilter(new DoctrineProxyFilter()), new DoctrineProxyMatcher());
524+
$deepCopy->addFilter(new SetNullFilter(), new PropertyNameMatcher('foo'));
525+
526+
$copy = $deepCopy->copy($object);
527+
528+
$this->assertNull($copy->foo);
529+
}
530+
531+
public function test_it_can_copy_property_after_applying_doctrine_proxy_filter_with_chainable_filter()
532+
{
533+
$object = new f013\B();
534+
$object->setFoo(new f013\C());
535+
536+
$deepCopy = new DeepCopy();
537+
$deepCopy->addFilter(new ChainableFilter(new DoctrineProxyFilter()), new DoctrineProxyMatcher());
538+
539+
/** @var f013\B $copy */
540+
$copy = $deepCopy->copy($object);
541+
542+
$this->assertNotEquals($copy->getFoo(), $object->getFoo());
543+
}
544+
511545
private function assertEqualButNotSame($expected, $val)
512546
{
513547
$this->assertEquals($expected, $val);

0 commit comments

Comments
 (0)