Skip to content

Commit

Permalink
Merge pull request #175 from fsevestre/173-chainable-filter
Browse files Browse the repository at this point in the history
  • Loading branch information
mnapoli authored Mar 8, 2023
2 parents 14daed4 + 0de0336 commit 7284c22
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 1 deletion.
35 changes: 34 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@ $matcher = new TypeMatcher('Doctrine\Common\Collections\Collection');
- `DeepCopy\Filter` applies a transformation to the object attribute matched by `DeepCopy\Matcher`
- `DeepCopy\TypeFilter` applies a transformation to any element matched by `DeepCopy\TypeMatcher`

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


#### `SetNullFilter` (filter)

Expand Down Expand Up @@ -226,6 +229,34 @@ $copy = $copier->copy($object);
```


#### `ChainableFilter` (filter)

If you use cloning on proxy classes, you might want to apply two filters for:
1. loading the data
2. applying a transformation

You can use the `ChainableFilter` as a decorator of the proxy loader filter, which won't stop the chain of filters (i.e.
the next ones may be applied).


```php
use DeepCopy\DeepCopy;
use DeepCopy\Filter\ChainableFilter;
use DeepCopy\Filter\Doctrine\DoctrineProxyFilter;
use DeepCopy\Filter\SetNullFilter;
use DeepCopy\Matcher\Doctrine\DoctrineProxyMatcher;
use DeepCopy\Matcher\PropertyNameMatcher;

$copier = new DeepCopy();
$copier->addFilter(new ChainableFilter(new DoctrineProxyFilter()), new DoctrineProxyMatcher());
$copier->addFilter(new SetNullFilter(), new PropertyNameMatcher('id'));

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

echo $copy->id; // null
```


#### `DoctrineCollectionFilter` (filter)

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

```php
use DeepCopy\DeepCopy;
use DeepCopy\Filter\Doctrine\DoctrineProxyFilter;
use DeepCopy\Matcher\Doctrine\DoctrineProxyMatcher;

$copier = new DeepCopy();
$copier->addFilter(new DoctrineProxyFilter(), new DoctrineProxyMatcher());
$copier->addFilter(new ChainableFilter(new DoctrineProxyFilter()), new DoctrineProxyMatcher());

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

Expand Down
24 changes: 24 additions & 0 deletions fixtures/f013/A.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace DeepCopy\f013;

use Doctrine\Persistence\Proxy;

class A implements Proxy
{
public $foo = 1;

/**
* @inheritdoc
*/
public function __load()
{
}

/**
* @inheritdoc
*/
public function __isInitialized()
{
}
}
34 changes: 34 additions & 0 deletions fixtures/f013/B.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?php

namespace DeepCopy\f013;

use Doctrine\Persistence\Proxy;

class B implements Proxy
{
private $foo;

/**
* @inheritdoc
*/
public function __load()
{
}

/**
* @inheritdoc
*/
public function __isInitialized()
{
}

public function getFoo()
{
return $this->foo;
}

public function setFoo($foo)
{
$this->foo = $foo;
}
}
13 changes: 13 additions & 0 deletions fixtures/f013/C.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace DeepCopy\f013;

class C
{
public $foo = 1;

public function __clone()
{
$this->foo = null;
}
}
5 changes: 5 additions & 0 deletions src/DeepCopy/DeepCopy.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use DateTimeInterface;
use DateTimeZone;
use DeepCopy\Exception\CloneException;
use DeepCopy\Filter\ChainableFilter;
use DeepCopy\Filter\Filter;
use DeepCopy\Matcher\Matcher;
use DeepCopy\Reflection\ReflectionHelper;
Expand Down Expand Up @@ -239,6 +240,10 @@ function ($object) {
}
);

if ($filter instanceof ChainableFilter) {
continue;
}

// If a filter matches, we stop processing this property
return;
}
Expand Down
24 changes: 24 additions & 0 deletions src/DeepCopy/Filter/ChainableFilter.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace DeepCopy\Filter;

/**
* Defines a decorator filter that will not stop the chain of filters.
*/
class ChainableFilter implements Filter
{
/**
* @var Filter
*/
protected $filter;

public function __construct(Filter $filter)
{
$this->filter = $filter;
}

public function apply($object, $property, $objectCopier)
{
$this->filter->apply($object, $property, $objectCopier);
}
}
34 changes: 34 additions & 0 deletions tests/DeepCopyTest/DeepCopyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,12 @@
use DeepCopy\f009;
use DeepCopy\f011;
use DeepCopy\f012\Suit;
use DeepCopy\f013;
use DeepCopy\Filter\ChainableFilter;
use DeepCopy\Filter\Doctrine\DoctrineProxyFilter;
use DeepCopy\Filter\KeepFilter;
use DeepCopy\Filter\SetNullFilter;
use DeepCopy\Matcher\Doctrine\DoctrineProxyMatcher;
use DeepCopy\Matcher\PropertyNameMatcher;
use DeepCopy\Matcher\PropertyTypeMatcher;
use DeepCopy\TypeFilter\ShallowCopyFilter;
Expand Down Expand Up @@ -508,6 +512,36 @@ public function test_it_keeps_enums()
$this->assertSame($enum, $copy);
}

/**
* @ticket https://github.com/myclabs/DeepCopy/issues/98
*/
public function test_it_can_apply_two_filters_with_chainable_filter()
{
$object = new f013\A();

$deepCopy = new DeepCopy();
$deepCopy->addFilter(new ChainableFilter(new DoctrineProxyFilter()), new DoctrineProxyMatcher());
$deepCopy->addFilter(new SetNullFilter(), new PropertyNameMatcher('foo'));

$copy = $deepCopy->copy($object);

$this->assertNull($copy->foo);
}

public function test_it_can_copy_property_after_applying_doctrine_proxy_filter_with_chainable_filter()
{
$object = new f013\B();
$object->setFoo(new f013\C());

$deepCopy = new DeepCopy();
$deepCopy->addFilter(new ChainableFilter(new DoctrineProxyFilter()), new DoctrineProxyMatcher());

/** @var f013\B $copy */
$copy = $deepCopy->copy($object);

$this->assertNotEquals($copy->getFoo(), $object->getFoo());
}

private function assertEqualButNotSame($expected, $val)
{
$this->assertEquals($expected, $val);
Expand Down

0 comments on commit 7284c22

Please sign in to comment.