From 4dd8b6edfc287099da11e5071c635c4e1776f440 Mon Sep 17 00:00:00 2001 From: Craig Morris Date: Mon, 25 Jan 2021 09:06:43 +0000 Subject: [PATCH] add support for class based factorise --- README.md | 49 ++++++++++++++++++++++++++++++- src/PopoFactory.php | 23 +++++---------- tests/PopoFactoryTest.php | 14 +++++++++ tests/Popos/PersonData.php | 5 +++- tests/Popos/PersonDataFactory.php | 34 +++++++++++++++++++++ 5 files changed, 107 insertions(+), 18 deletions(-) create mode 100644 tests/Popos/PersonDataFactory.php diff --git a/README.md b/README.md index 6f35a58..4aec1c5 100644 --- a/README.md +++ b/README.md @@ -56,9 +56,56 @@ PopoFactory::new(PersonData::class) ``` +## Creating Class Based Factories + +It's useful to define specific factories for particular objects, which can easily be done by extending the `PopoFactory` class. + +My specifying a typehint for the `make()` method you will also get typehints in your IDE for your mocked object. + +```php +state([ + 'firstName' => 'Craig' + ]); + } + + public function gotNoJob() { + return $this->state([ + 'companyName' => null, + ]); + } + + public function worksAtHome() { + return $this->state(function ($attributes) { + return [ + 'workAddress' => $attributes['homeAddress'] + ]; + }); + } +} +``` + +Then using it in tests like so: + +```php +$person = PersonDataFactory::factory() + ->gotNoJob() + ->worksAtHome() + ->make(); +``` + + ## Extending -It used to be that you had to extend the factory class to utilize custom types. You can now do so through the static `registerProvider()` method on the `PropertyFactory` class. This method takes two arguments. The first should be the FQDN of the class you are providing (e.g. `Carbon\Carbon`) OR the built-in type (e.g. `string`). The second should be a callback that returns the generated value. This callback is passed two properties when called to assist in generating the value. The first is an instance of `Anteris\FakerMap\FakerMap` which can be used to help generate fake data. The second is an instance of `ReflectionProperty` which contains information about the property being generated. +You can easily extend the factory to support other data types. You can do tthis through the static `registerProvider()` method on the `PropertyFactory` class. This method takes two arguments. The first should be the FQDN of the class you are providing (e.g. `Carbon\Carbon`) OR the built-in type (e.g. `string`). The second should be a callback that returns the generated value. This callback is passed two properties when called to assist in generating the value. The first is an instance of `Anteris\FakerMap\FakerMap` which can be used to help generate fake data. The second is an instance of `ReflectionProperty` which contains information about the property being generated. For example, to support Carbon: diff --git a/src/PopoFactory.php b/src/PopoFactory.php index 768fc49..14efaa9 100644 --- a/src/PopoFactory.php +++ b/src/PopoFactory.php @@ -2,7 +2,6 @@ namespace Morrislaptop\PopoFactory; -use Morrislaptop\PopoFactory\Exceptions\InvalidObjectException; use ReflectionClass; use ReflectionProperty; use Symfony\Component\Serializer\Serializer; @@ -58,6 +57,7 @@ public function __construct(string $dataTransferObjectClass, Serializer $seriali public function count(int $count): static { $clone = clone $this; + $clone->count = $count; return $clone; @@ -107,7 +107,7 @@ public function state($state): static * DTO Creator **************************************************************************/ - public function make(array $attributes = []): array|object + public function make(array $attributes = []): array | object { // Pass attributes along as state if (! empty($attributes)) { @@ -131,14 +131,6 @@ protected function makeDTO(): object $parameters = []; $properties = $class->getProperties(ReflectionProperty::IS_PUBLIC); - // Resolve all our state options... - $preset = []; - - foreach ($this->states as $state) { - $result = $state(); - $preset = array_merge($preset, is_array($result) ? $result : []); - } - foreach ($properties as $property) { if ($property->isStatic()) { continue; @@ -146,15 +138,14 @@ protected function makeDTO(): object $propertyName = $property->getName(); - if (isset($preset[$propertyName])) { - $parameters[$propertyName] = $preset[$propertyName]; - - continue; - } - $parameters[$propertyName] = PropertyFactory::new()->make($property); } + foreach ($this->states as $state) { + $result = $state($parameters); + $parameters = array_merge($parameters, is_array($result) ? $result : []); + } + return $this->serializer->denormalize($parameters, $this->dataTransferObjectClass); } diff --git a/tests/PopoFactoryTest.php b/tests/PopoFactoryTest.php index 0afd7c1..7a6cfd3 100644 --- a/tests/PopoFactoryTest.php +++ b/tests/PopoFactoryTest.php @@ -6,6 +6,7 @@ use Morrislaptop\PopoFactory\Tests\Popos\FamilyData; use Morrislaptop\PopoFactory\Tests\Popos\PersonData; use Morrislaptop\PopoFactory\Tests\Popos\PersonDataDocBlock; +use Morrislaptop\PopoFactory\Tests\Popos\PersonDataFactory; class PopoFactoryTest extends AbstractTestCase { @@ -56,4 +57,17 @@ public function test_it_can_generate_dto_with_an_array_property() $this->assertInstanceOf(PersonData::class, $child); } } + + public function test_it_can_use_a_factory_to_make_single_dto() + { + $dto = PersonDataFactory::factory() + ->gotNoJob() + ->worksAtHome() + ->make(); + + $this->assertInstanceOf(PersonData::class, $dto); + $this->assertEquals('Craig', $dto->firstName); + $this->assertEquals(null, $dto->companyName); + $this->assertEquals($dto->homeAddress, $dto->workAddress); + } } diff --git a/tests/Popos/PersonData.php b/tests/Popos/PersonData.php index 2299e5e..da9fc38 100644 --- a/tests/Popos/PersonData.php +++ b/tests/Popos/PersonData.php @@ -2,6 +2,8 @@ namespace Morrislaptop\PopoFactory\Tests\Popos; +use Carbon\Carbon; + class PersonData { public function __construct( @@ -9,8 +11,9 @@ public function __construct( public string $lastName, public string $email, public string $homeAddress, - public string $companyName, + public ?string $companyName, public string $workAddress, + public Carbon $dob, public PersonDataDocBlock $spouse, ) { } diff --git a/tests/Popos/PersonDataFactory.php b/tests/Popos/PersonDataFactory.php new file mode 100644 index 0000000..799e77e --- /dev/null +++ b/tests/Popos/PersonDataFactory.php @@ -0,0 +1,34 @@ +state([ + 'firstName' => 'Craig', + ]); + } + + public function gotNoJob() + { + return $this->state([ + 'companyName' => null, + ]); + } + + public function worksAtHome() + { + return $this->state(function ($attributes) { + return [ + 'workAddress' => $attributes['homeAddress'], + ]; + }); + } +}