diff --git a/README.md b/README.md index 0becd2e..633cab4 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,9 @@ To install and authenticate via public key call: Usage ----- -###Set up +###Tutorial + +####Set Up TypeConverter requires explicitly defined one-way conversions methods to convert data types from one to another. These methods should be established during the @@ -122,11 +124,55 @@ Fahrenheit. In the `register` method, `ConversionCollection::addConversion` is called for each conversion method, providing the "from type", the "to type" and the callback. -The "from type" and "to type" should each either be a *fully qualified* class name, +[Learn more about adding conversion methods](#registering-conversion-methods) + +####Initialization + +The easiest way to integrate TypeConverter is with Silex, which we will use here. +Simply register the TypeConverterServiceProvider with your application. + +```PHP +$app->register(new UCI\TypeConverter\TypeConverterServiceProvider()); +``` + +After registration, you can add any ConversionModules that you may need to the +TypeConversionBuilder. This should be done before `$app['type_converter.converter']` +is called upon in your application's execution. + +```PHP +// Add a couple of ConversionModules to the builder using chaining +// The second module has a dependency on ThingyFactory +$app['type_converter.builder'] + ->addConversionModule(new Temperature\TemperatureConversionModule()) + ->addConversionModule(new ThingyConversionModule($app['thingy_factory'])); +``` + +####Converting Things + +Once everything is set up, converting things is easy. + +```PHP +$converter = $app['type_converter.converter']; + +$f = new Fahrenheit(); +$f->temperature = 72; +$c = $converter->convert($f, 'Temperature\Celsius'); +echo $c->temperature; // prints 22.2222 +``` + +###More Details + +####Registering Conversion Methods + +```PHP +ConversionCollection::addConversion($from_type, $to_type, $callback) +``` + +The `$from_type` and `$to_type` should each either be a *fully qualified* class name, or the name of a primitive as it would be returned by `gettype()`. -For the callbacks, in this case we provide callable arrays, but any valid -callback type is fine. Here are a few examples. +`$callback` can be any valid callable type. In the tutorial above, we provided +callable arrays, but there are other options. Here are a few examples. ```PHP // Call a method of this ConversionModule @@ -158,36 +204,43 @@ adding methods as object method calls on a ConversionModule has a few advantages 3. It makes unit testing a snap because you can just instantiate the ConversionModule class and test its conversion methods directly. -####Initialization +####Recursive Conversions -The easiest way to integrate TypeConverter is with Silex, which we will use here. -Simply register the TypeConverterServiceProvider with your application. +There may be cases when dealing with large, compound objects when it will be useful +to call TypeConverter from within a conversion method. For example, suppose +you had the weather forecast objects AmericanForecast and EuropeanForecast. ```PHP -$app->register(new UCI\TypeConverter\TypeConverterServiceProvider()); -``` - -After registration, you can add any ConversionModules that you may need to the -TypeConversionBuilder. This should be done before `$app['type_converter.convert']` -is called upon in your application's execution. +class AmericanForecast { + /** + * @var 'Temperature\Fahrenheit' + */ + public $temperature; +} -```PHP -// Add a couple of ConversionModules to the builder using chaining -// The second module has a dependency on ThingyFactory -$app['type_converter.builder'] - ->addConversionModule(new Temperature\TemperatureConversionModule()) - ->addConversionModule(new ThingyConversionModule($app['thingy_factory'])); +class EuropeanForecast { + /** + * @var 'Temperature\Celsius' + */ + public $temperature; +} ``` -###Converting Things +We've already defined conversion methods for Fahrenheit and Celsius, so it would +be nice if we could use those while converting between AmericanForecast and +EuropeanForecast. Fortunately, we can. -Once everything is set up, converting things is easy. +The TypeConverter itself is passed into each conversion method on invocation +as an optional second parameter. You can use the TypeConverter to apply conversions +recursively. ```PHP -$converter = $app['type_converter.converter']; - -$f = new Fahrenheit(); -$f->temperature = 72; -$c = $converter->convert($f, 'Temperature\Celsius'); -echo $c->temperature; // prints 22.2222 +function americanForecastToEuropeanForecast( + AmericanForecast $af, + TypeConverter $type_converter +) { + $ef = new EuropeanForecast(); + $ef->temperature = + $type_converter->convert($af->temperature, 'Temperature\Celsius'); +} ``` diff --git a/composer.lock b/composer.lock index b837b1e..ee27d3b 100644 --- a/composer.lock +++ b/composer.lock @@ -3,7 +3,7 @@ "This file locks the dependencies of your project to a known state", "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" ], - "hash": "501fa0033f0d71ef3c1201fe2dc8bd99", + "hash": "6be78d98f0df9d3253000cad6f32c6ab", "packages": [ { "name": "pimple/pimple", @@ -175,17 +175,17 @@ }, { "name": "symfony/debug", - "version": "v2.4.0", + "version": "v2.4.1", "target-dir": "Symfony/Component/Debug", "source": { "type": "git", "url": "https://github.com/symfony/Debug.git", - "reference": "848565cb83d985eb77dac3f9e4486f3bce92b4a2" + "reference": "74110be5ec681a83fc5bd66dd5fd29fe85fe9c1f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Debug/zipball/848565cb83d985eb77dac3f9e4486f3bce92b4a2", - "reference": "848565cb83d985eb77dac3f9e4486f3bce92b4a2", + "url": "https://api.github.com/repos/symfony/Debug/zipball/74110be5ec681a83fc5bd66dd5fd29fe85fe9c1f", + "reference": "74110be5ec681a83fc5bd66dd5fd29fe85fe9c1f", "shasum": "" }, "require": { @@ -226,21 +226,21 @@ ], "description": "Symfony Debug Component", "homepage": "http://symfony.com", - "time": "2013-12-03 14:52:22" + "time": "2014-01-01 09:02:49" }, { "name": "symfony/event-dispatcher", - "version": "v2.4.0", + "version": "v2.4.1", "target-dir": "Symfony/Component/EventDispatcher", "source": { "type": "git", "url": "https://github.com/symfony/EventDispatcher.git", - "reference": "acd1707236f6eb96fbb8d58f63d289b72ebc2f6e" + "reference": "e3ba42f6a70554ed05749e61b829550f6ac33601" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/acd1707236f6eb96fbb8d58f63d289b72ebc2f6e", - "reference": "acd1707236f6eb96fbb8d58f63d289b72ebc2f6e", + "url": "https://api.github.com/repos/symfony/EventDispatcher/zipball/e3ba42f6a70554ed05749e61b829550f6ac33601", + "reference": "e3ba42f6a70554ed05749e61b829550f6ac33601", "shasum": "" }, "require": { @@ -280,21 +280,21 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "http://symfony.com", - "time": "2013-12-03 14:52:22" + "time": "2013-12-28 08:12:03" }, { "name": "symfony/http-foundation", - "version": "v2.4.0", + "version": "v2.4.1", "target-dir": "Symfony/Component/HttpFoundation", "source": { "type": "git", "url": "https://github.com/symfony/HttpFoundation.git", - "reference": "381245ba3e507a3e9c5b4c2cbf344a312fb6081d" + "reference": "6c6b8a7bcd7e2cc920cd6acace563fdbf121d844" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/381245ba3e507a3e9c5b4c2cbf344a312fb6081d", - "reference": "381245ba3e507a3e9c5b4c2cbf344a312fb6081d", + "url": "https://api.github.com/repos/symfony/HttpFoundation/zipball/6c6b8a7bcd7e2cc920cd6acace563fdbf121d844", + "reference": "6c6b8a7bcd7e2cc920cd6acace563fdbf121d844", "shasum": "" }, "require": { @@ -330,21 +330,21 @@ ], "description": "Symfony HttpFoundation Component", "homepage": "http://symfony.com", - "time": "2013-11-28 10:27:26" + "time": "2014-01-05 02:10:50" }, { "name": "symfony/http-kernel", - "version": "v2.4.0", + "version": "v2.4.1", "target-dir": "Symfony/Component/HttpKernel", "source": { "type": "git", "url": "https://github.com/symfony/HttpKernel.git", - "reference": "e5afbeabef99f916f0e0d76f68a36343aa293387" + "reference": "0605eedeb52c4d3a3144128d8336395a57be60d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/HttpKernel/zipball/e5afbeabef99f916f0e0d76f68a36343aa293387", - "reference": "e5afbeabef99f916f0e0d76f68a36343aa293387", + "url": "https://api.github.com/repos/symfony/HttpKernel/zipball/0605eedeb52c4d3a3144128d8336395a57be60d4", + "reference": "0605eedeb52c4d3a3144128d8336395a57be60d4", "shasum": "" }, "require": { @@ -401,21 +401,21 @@ ], "description": "Symfony HttpKernel Component", "homepage": "http://symfony.com", - "time": "2013-12-03 15:23:03" + "time": "2014-01-05 02:12:11" }, { "name": "symfony/routing", - "version": "v2.4.0", + "version": "v2.4.1", "target-dir": "Symfony/Component/Routing", "source": { "type": "git", "url": "https://github.com/symfony/Routing.git", - "reference": "a7a665e2a0e3a203ca9a690c021066e76295bf6a" + "reference": "4abfb500aab8be458c9e3a227ea56b190584f78a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Routing/zipball/a7a665e2a0e3a203ca9a690c021066e76295bf6a", - "reference": "a7a665e2a0e3a203ca9a690c021066e76295bf6a", + "url": "https://api.github.com/repos/symfony/Routing/zipball/4abfb500aab8be458c9e3a227ea56b190584f78a", + "reference": "4abfb500aab8be458c9e3a227ea56b190584f78a", "shasum": "" }, "require": { @@ -467,7 +467,7 @@ "uri", "url" ], - "time": "2013-11-22 17:42:00" + "time": "2014-01-05 02:10:50" } ], "packages-dev": [ @@ -477,12 +477,12 @@ "source": { "type": "git", "url": "https://github.com/padraic/mockery.git", - "reference": "121389af4cebe21c529a21b9d341f3fe198326e8" + "reference": "ab2c696982fa47bb21d3bff1cd97b6b657d6f4f4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/padraic/mockery/zipball/121389af4cebe21c529a21b9d341f3fe198326e8", - "reference": "121389af4cebe21c529a21b9d341f3fe198326e8", + "url": "https://api.github.com/repos/padraic/mockery/zipball/ab2c696982fa47bb21d3bff1cd97b6b657d6f4f4", + "reference": "ab2c696982fa47bb21d3bff1cd97b6b657d6f4f4", "shasum": "" }, "require": { @@ -490,9 +490,14 @@ "php": ">=5.3.2" }, "require-dev": { - "hamcrest/hamcrest": "1.1.0" + "hamcrest/hamcrest-php": "~1.1" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.9.x-dev" + } + }, "autoload": { "psr-0": { "Mockery": "library/" @@ -523,7 +528,7 @@ "test double", "testing" ], - "time": "2013-12-20 15:53:19" + "time": "2014-01-13 13:26:51" }, { "name": "phpunit/php-code-coverage", @@ -894,17 +899,17 @@ }, { "name": "symfony/yaml", - "version": "v2.4.0", + "version": "v2.4.1", "target-dir": "Symfony/Component/Yaml", "source": { "type": "git", "url": "https://github.com/symfony/Yaml.git", - "reference": "1ae235a1b9d3ad3d9f3860ff20acc072df95b7f5" + "reference": "4e1a237fc48145fae114b96458d799746ad89aa0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/Yaml/zipball/1ae235a1b9d3ad3d9f3860ff20acc072df95b7f5", - "reference": "1ae235a1b9d3ad3d9f3860ff20acc072df95b7f5", + "url": "https://api.github.com/repos/symfony/Yaml/zipball/4e1a237fc48145fae114b96458d799746ad89aa0", + "reference": "4e1a237fc48145fae114b96458d799746ad89aa0", "shasum": "" }, "require": { @@ -937,7 +942,7 @@ ], "description": "Symfony Yaml Component", "homepage": "http://symfony.com", - "time": "2013-11-26 16:40:27" + "time": "2013-12-28 08:12:03" } ], "aliases": [ diff --git a/src/UCI/TypeConverter/TypeConverter.php b/src/UCI/TypeConverter/TypeConverter.php index fdd0900..c743182 100644 --- a/src/UCI/TypeConverter/TypeConverter.php +++ b/src/UCI/TypeConverter/TypeConverter.php @@ -47,7 +47,7 @@ public function convert($object, $to_type) { $from_type = $this->getType($object); $conversion_method = $this->conversionCollection->getConversion($from_type, $to_type); - $result = $conversion_method($object); + $result = $conversion_method($object, $this); return $result; } diff --git a/tests/UCI/Tests/TypeConverter/TypeConverterTest.php b/tests/UCI/Tests/TypeConverter/TypeConverterTest.php index 996ba6f..89f2f0c 100644 --- a/tests/UCI/Tests/TypeConverter/TypeConverterTest.php +++ b/tests/UCI/Tests/TypeConverter/TypeConverterTest.php @@ -36,6 +36,13 @@ public function setUp() 'UCI\Tests\TypeConverter\TypeA' ) ->andReturn([$this, 'BToA']); + $collection + ->shouldReceive('getConversion') + ->with( + 'UCI\Tests\TypeConverter\SuperTypeA', + 'UCI\Tests\TypeConverter\SuperTypeB' + ) + ->andReturn([$this, 'superTypeAToSuperTypeB']); $this->converter = new TypeConverter($collection); } @@ -56,6 +63,17 @@ public function testConvert() $this->assertEquals($b->valueB, $a->valueA); } + public function testRecursiveConvert() + { + $super_a = new SuperTypeA(); + $super_a->typeA = new TypeA(); + $super_a->typeA->valueA = 'foo'; + $super_b = $this->converter->convert($super_a, 'UCI\Tests\TypeConverter\SuperTypeB'); + $this->assertInstanceOf('UCI\Tests\TypeConverter\SuperTypeB', $super_b); + $this->assertInstanceOf('UCI\Tests\TypeConverter\TypeB', $super_b->typeB); + $this->assertEquals($super_b->typeB->valueB, $super_a->typeA->valueA); + } + /** * A conversion function for converting TypeA to TypeB * @@ -81,6 +99,19 @@ public function bToA(TypeB $b) $a->valueA = $b->valueB; return $a; } + + /** + * A conversion function for converting SuperTypeA to SuperTypeB + * @param SuperTypeA $super_a + * @param TypeConverter $type_converter + * @return SuperTypeB + */ + public function superTypeAToSuperTypeB(SuperTypeA $super_a, TypeConverter $type_converter) + { + $super_b = new SuperTypeB(); + $super_b->typeB = $type_converter->convert($super_a->typeA, 'UCI\Tests\TypeConverter\TypeB'); + return $super_b; + } } /** @@ -101,4 +132,30 @@ class TypeA class TypeB { public $valueB; +} + +/** + * Class SuperTypeA + * + * A class that contains a TypeA + */ +class SuperTypeA +{ + /** + * @var TypeA + */ + public $typeA; +} + +/** + * Class SuperTypeB + * + * A class that contains a TypeB + */ +class SuperTypeB +{ + /** + * @var TypeB + */ + public $typeB; } \ No newline at end of file