From d111f7a789d9e649d00c0a721d1695e2357fcb16 Mon Sep 17 00:00:00 2001 From: Paul Date: Mon, 12 Jun 2017 17:27:12 +0100 Subject: [PATCH] Added second parameter (data) to Copy to replace Walk functionality. Removed Walk strategy. Updated readme. --- README.md | 85 +++++-------------- src/Strategy/Copy.php | 29 ++++++- src/Strategy/Walk.php | 31 ------- test/Functional/DocumentationTest.php | 30 +++---- test/Integration/Mapper/Strategy/CopyTest.php | 24 ++++++ test/Integration/Mapper/Strategy/WalkTest.php | 25 ------ test/Unit/Mapper/Strategy/CopyTest.php | 28 +++--- 7 files changed, 96 insertions(+), 156 deletions(-) delete mode 100644 src/Strategy/Walk.php create mode 100644 test/Integration/Mapper/Strategy/CopyTest.php delete mode 100644 test/Integration/Mapper/Strategy/WalkTest.php diff --git a/README.md b/README.md index 953d4df..d7cf5fa 100644 --- a/README.md +++ b/README.md @@ -39,11 +39,9 @@ Contents 1. [Merge](#merge) 1. [TakeFirst](#takefirst) 1. [ToList](#tolist) - 1. [Translate](#translate) 1. [TryCatch](#trycatch) 1. [Type](#type) 1. [Unique](#unique) - 1. [Walk](#walk) 1. [Others](#others) 1. [Debug](#debug) 1. [Requirements](#requirements) @@ -287,7 +285,7 @@ The following strategies ship with Mapper and provide a suite of commonly used f #### Fetchers - - [Copy](#copy) – Copies a portion of input data. + - [Copy](#copy) – Copies a portion of input data, or specified data, according to a lookup path. - [CopyContext](#copycontext) – Copies a portion of context data. - [CopyKey](#copykey) – Copies the current key. @@ -305,11 +303,9 @@ The following strategies ship with Mapper and provide a suite of commonly used f - [Merge](#merge) – Merges two data sets together giving precedence to the latter if keys collide. - [TakeFirst](#takefirst) – Takes the first value from a collection one or more times. - [ToList](#tolist) – Converts data to a single-element list unless it is already a list. - - [Translate](#translate) – Translates a value using a mapping. - [TryCatch](#trycatch) – Tries the primary strategy and falls back to an expression if an exception is thrown. - [Type](#type) – Casts data to the specified type. - [Unique](#unique) – Creates a collection of unique values by removing duplicates. - - [Walk](#walk) – Walks a nested structure to the specified element in the same manner as `Copy`. #### Others @@ -317,17 +313,18 @@ The following strategies ship with Mapper and provide a suite of commonly used f ### Copy -Copies a portion of the input data according to the specified path. Supports traversing nested arrays. +Copies a portion of input data, or specified data, according to a lookup path. Supports traversing nested arrays. -`Copy` is probably the most common strategy whether used by itself or injected into other strategies. +`Copy` is probably the most common strategy whether used by itself or injected into other strategies. Since both its *path* and *data* parameters can be mapped expressions it is highly versatile and can be combined with other strategies, or even itself, to produce powerful expressions. #### Signature ```php -Copy(Strategy|Mapping|array|mixed $path) +Copy(Strategy|Mapping|array|mixed $path, Strategy|Mapping|array|mixed $data) ``` 1. `$path` – Array of path components, string of `->`-delimited path components or a strategy or mapping resolving to such an expression. + 2. `$data` – Optional. Array data or an expression that resolves to an array to be copied instead of input data. #### Example @@ -351,9 +348,22 @@ $data = [ > 123 +#### Specified data example + +When data is specified in the second parameter it is used instead of the data sent from `Mapper`. + +```php +(new Mapper)->map( + ['foo' => 'bar'], + new Copy('foo', ['foo' => 'baz']) +); +``` + +> 'baz' + #### Path resolver example -Since the path can be derived from other strategies we could nest `Copy` instances to look up values referenced by other keys. +Since the path can be derived from other strategies we can nest `Copy` instances to look up values referenced by other keys. ```php (new Mapper)->map( @@ -772,33 +782,6 @@ ToList(Strategy|Mapping|array|mixed $expression) > ['bar'] -### Translate - -Translates a value using a mapping. The mapping may derived from any valid expression. - -#### Signature - -```php -Translate(Strategy $value, Strategy|Mapping|array|mixed $mapping) -``` - - 1. `$value` – Value used to match against an entry in the mapping. - 2. `$mapping` – Mapping that specifies what the value may be translated to. - -#### Example - -```php -(new Mapper)->map( - ['foo' => 'foo'], - new Translate( - new Copy('foo'), - ['foo' => 'bar'] - ) -); -``` - -> 'bar' - ### TryCatch Tries the primary strategy and falls back to an expression if an exception is thrown. The thrown exception is passed to the specified exception handler. The handler should throw an exception if it does not expect the exception type it receives. @@ -882,36 +865,6 @@ Unique(Strategy|Mapping|array|mixed $collection) > [1, 2, 3, 4, 5] -### Walk - -Walks a nested structure to the specified element in the same manner as [`Copy`](#copy). - -#### Signature - -```php -Walk(Strategy|Mapping|array|mixed $expression, Strategy|Mapping|array|mixed $path) -``` - - 1. `$expression` – Expression to walk. - 2. `$path` – Array of path components, string of `->`-delimited path components or a strategy or mapping resolving to such an expression. - -#### Example - -```php -(new Mapper)->map( - [ - 'foo' => [ - 'bar' => [ - 'baz' => 123, - ], - ], - ], - new Walk(new Copy('foo'), 'bar->baz') -) -``` - -> 123 - ### Debug Debugs a mapping by breaking the debugger wherever this strategy is inserted. The specified expression will be mapped immediately before triggering the breakpoint. The debugger should see the current data, context and mapped expression. diff --git a/src/Strategy/Copy.php b/src/Strategy/Copy.php index 90c6656..9db6cae 100644 --- a/src/Strategy/Copy.php +++ b/src/Strategy/Copy.php @@ -2,14 +2,32 @@ namespace ScriptFUSION\Mapper\Strategy; use ScriptFUSION\ArrayWalker\ArrayWalker; +use ScriptFUSION\Mapper\Mapping; /** - * Copies a portion of input data. + * Copies a portion of input data, or specified data, according to a lookup path. */ class Copy extends Delegate { const PATH_SEPARATOR = '->'; + private $data; + + /** + * Initializes this instance with the specified path. If data is specified it is always used instead of input data. + * + * @param Strategy|Mapping|array|mixed $path Array of path components, string of `->`-delimited path components or + * a strategy or mapping resolving to such an expression. + * @param Strategy|Mapping|array|mixed $data Optional. Array data or an expression that resolves to an array to be + * copied instead of input data. + */ + public function __construct($path, $data = null) + { + parent::__construct($path); + + $this->data = $data; + } + /** * @param mixed $record * @param mixed $context @@ -18,14 +36,23 @@ class Copy extends Delegate */ public function __invoke($record, $context = null) { + // It is typically an error for record not to be an array but we prefer to avoid throwing exceptions. if (!is_array($record)) { return null; } + // Resolve the path expression. Path will always be an array after this block. if (!is_array($path = parent::__invoke($record, $context))) { + // If it's not an array treat it as a delimited string; implicitly casts other scalar types. $path = explode(self::PATH_SEPARATOR, $path); } + // Overwrite record with resolved data expression if set and ensure it is an array. + if ($this->data !== null && !is_array($record = $this->delegate($this->data, $record, $context))) { + return null; + } + + // Walk path unless it is empty. return $path ? ArrayWalker::walk($record, $path) : null; } } diff --git a/src/Strategy/Walk.php b/src/Strategy/Walk.php deleted file mode 100644 index 7b7e471..0000000 --- a/src/Strategy/Walk.php +++ /dev/null @@ -1,31 +0,0 @@ -`-delimited path components or - * a strategy or mapping resolving to such an expression. - */ - public function __construct($expression, $path) - { - parent::__construct($expression); - - $this->path = $path; - } - - public function __invoke($data, $context = null) - { - $copy = (new Copy($this->delegate($this->path, $data, $context)))->setMapper($this->getMapper()); - - return $copy(parent::__invoke($data, $context), $context); - } -} diff --git a/test/Functional/DocumentationTest.php b/test/Functional/DocumentationTest.php index 1d8645d..064ba13 100644 --- a/test/Functional/DocumentationTest.php +++ b/test/Functional/DocumentationTest.php @@ -18,11 +18,9 @@ use ScriptFUSION\Mapper\Strategy\Merge; use ScriptFUSION\Mapper\Strategy\TakeFirst; use ScriptFUSION\Mapper\Strategy\ToList; -use ScriptFUSION\Mapper\Strategy\Translate; use ScriptFUSION\Mapper\Strategy\TryCatch; use ScriptFUSION\Mapper\Strategy\Type; use ScriptFUSION\Mapper\Strategy\Unique; -use ScriptFUSION\Mapper\Strategy\Walk; use ScriptFUSIONTest\Fixture\BarBucketAddressToAddresesMapping; use ScriptFUSIONTest\Fixture\FooBookAddressToAddresesMapping; use ScriptFUSIONTest\Fixture\FooToBarMapping; @@ -100,6 +98,17 @@ public function testCopy() self::assertSame($bar, (new Mapper)->map($data, new Copy(['foo', 'bar']))); } + public function testSpecifiedDataCopy() + { + self::assertSame( + 'baz', + (new Mapper)->map( + ['foo' => 'bar'], + new Copy('foo', ['foo' => 'baz']) + ) + ); + } + public function testNestedCopy() { self::assertSame( @@ -355,21 +364,4 @@ public function testUnique() ) ); } - - public function testWalk() - { - self::assertSame( - $baz = 123, - (new Mapper)->map( - [ - 'foo' => [ - 'bar' => [ - 'baz' => $baz, - ], - ], - ], - new Walk(new Copy('foo'), 'bar->baz') - ) - ); - } } diff --git a/test/Integration/Mapper/Strategy/CopyTest.php b/test/Integration/Mapper/Strategy/CopyTest.php new file mode 100644 index 0000000..71ea419 --- /dev/null +++ b/test/Integration/Mapper/Strategy/CopyTest.php @@ -0,0 +1,24 @@ +bar', ['foo' => ['bar' => 'baz']])) + ->setMapper(new Mapper); + + self::assertSame('baz', $copy([])); + } + + public function testWalkStrategyPath() + { + $copy = (new Copy(new Copy('foo'), ['bar' => 'baz'])) + ->setMapper(new Mapper); + + self::assertSame('baz', $copy(['foo' => 'bar'])); + } +} diff --git a/test/Integration/Mapper/Strategy/WalkTest.php b/test/Integration/Mapper/Strategy/WalkTest.php deleted file mode 100644 index 7c2f35e..0000000 --- a/test/Integration/Mapper/Strategy/WalkTest.php +++ /dev/null @@ -1,25 +0,0 @@ - ['bar' => 'baz']], 'foo->bar')) - ->setMapper(new Mapper); - - self::assertSame('baz', $walk([])); - } - - public function testWalkStrategyPath() - { - $walk = (new Walk(['bar' => 'baz'], new Copy('foo'))) - ->setMapper(new Mapper); - - self::assertSame('baz', $walk(['foo' => 'bar'])); - } -} diff --git a/test/Unit/Mapper/Strategy/CopyTest.php b/test/Unit/Mapper/Strategy/CopyTest.php index 48f2390..4ede33f 100644 --- a/test/Unit/Mapper/Strategy/CopyTest.php +++ b/test/Unit/Mapper/Strategy/CopyTest.php @@ -9,20 +9,6 @@ final class CopyTest extends \PHPUnit_Framework_TestCase { use MockeryPHPUnitIntegration; - public function testNullRecord() - { - $copy = self::createStrategy(0); - - self::assertNull($copy(null)); - } - - public function testNullPath() - { - $copy = self::createStrategy(null); - - self::assertNull($copy([])); - } - public function testFalsyPathComponentString() { $copy = self::createStrategy('0'); @@ -41,6 +27,20 @@ public function testFalsyPathComponentArray() self::assertSame('bar', $copy([['foo', 'bar']])); } + public function testNullRecord() + { + $copy = self::createStrategy(0); + + self::assertNull($copy(null)); + } + + public function testNullPath() + { + $copy = self::createStrategy(null); + + self::assertNull($copy([])); + } + public function testEmptyPathString() { $copy = self::createStrategy('');