From b3f8b40e66c865e295c876434d25756934abb4cb Mon Sep 17 00:00:00 2001 From: Nicolas PHILIPPE Date: Sat, 22 Feb 2025 18:03:33 +0100 Subject: [PATCH] feat: introduce forEach() and mapEach() method --- docs/index.rst | 33 ++++++++++++++ src/Factory.php | 12 ++++++ src/FactoryCollection.php | 23 ++++++++++ .../Fixture/Factories/SimpleObjectFactory.php | 43 +++++++++++++++++++ tests/Fixture/SimpleObject.php | 24 +++++++++++ ...FakerSeedSetFromLegacyConfigKernelTest.php | 3 -- tests/Unit/ObjectFactoryTest.php | 38 ++++++++++++++++ 7 files changed, 173 insertions(+), 3 deletions(-) create mode 100644 tests/Fixture/Factories/SimpleObjectFactory.php create mode 100644 tests/Fixture/SimpleObject.php diff --git a/docs/index.rst b/docs/index.rst index cca19b5de..be3b2883d 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -505,6 +505,39 @@ Sequences help to create different objects in one call: ] )->create(); +Distribute values over a collection +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you have a collection of values that you want to distribute over a collection, you can use the ``distribute()`` method: + +:: + + // let's say we have 2 categories... + $categories = CategoryFactory::createSequence( + [ + ['name' => 'category 1'], + ['name' => 'category 2'], + ] + ); + + // ...that we want to "distribute" over 2 posts + $posts = PostFactory::new() + ->sequence( + [ + ['name' => 'post 1'], + ['name' => 'post 2'], + ] + ) + + // "post 1" will have "category 1" and "post 2" will have "category 2" + ->distribute('category', $categories) + + // you can even chain "distribute()" methods: + // first post is published today, second post is published tomorrow + ->distribute('publishedAt', [new \DateTimeImmutable('today'), new \DateTimeImmutable('tomorrow')]) + + ->create(); + Faker ~~~~~ diff --git a/src/Factory.php b/src/Factory.php index 54e8ce061..5c5acfacc 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -131,6 +131,18 @@ final public function sequence(iterable|callable $sequence): FactoryCollection return FactoryCollection::sequence($this, $sequence); // @phpstan-ignore return.type } + /** + * @param list $values + * + * @return FactoryCollection + */ + final public function distribute(string $field, array $values): FactoryCollection + { + return $this->sequence( + \array_map(fn($value) => [$field => $value], $values) + ); + } + /** * @phpstan-param Attributes $attributes * diff --git a/src/FactoryCollection.php b/src/FactoryCollection.php index b2319f909..38712dd58 100644 --- a/src/FactoryCollection.php +++ b/src/FactoryCollection.php @@ -164,6 +164,29 @@ function(Factory $f) { ); } + /** + * @param list $values + * + * @return self + */ + public function distribute(string $field, array $values): self + { + $factories = $this->all(); + + if (\count($factories) !== \count($values)) { + throw new \InvalidArgumentException('Number of values must match number of factories.'); + } + + return new self( + $this->factory, + static fn() => \array_map( + static fn(Factory $f, $value) => $f->with([$field => $value]), + $factories, + $values + ) + ); + } + public function getIterator(): \Traversable { return new \ArrayIterator($this->all()); diff --git a/tests/Fixture/Factories/SimpleObjectFactory.php b/tests/Fixture/Factories/SimpleObjectFactory.php new file mode 100644 index 000000000..f7b2edf6c --- /dev/null +++ b/tests/Fixture/Factories/SimpleObjectFactory.php @@ -0,0 +1,43 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Zenstruck\Foundry\Tests\Fixture\Factories; + +use Zenstruck\Foundry\ObjectFactory; +use Zenstruck\Foundry\Tests\Fixture\SimpleObject; + +/** + * @author Nicolas PHILIPPE + * + * @extends ObjectFactory + */ +final class SimpleObjectFactory extends ObjectFactory +{ + public static function class(): string + { + return SimpleObject::class; + } + + public function withProps(string $prop1, ?string $prop2 = null): static + { + return $this->with([ + 'prop1' => $prop1, + 'prop2' => $prop2, + ]); + } + + protected function defaults(): array + { + return [ + 'prop1' => self::faker()->word(), + ]; + } +} diff --git a/tests/Fixture/SimpleObject.php b/tests/Fixture/SimpleObject.php new file mode 100644 index 000000000..826abdf10 --- /dev/null +++ b/tests/Fixture/SimpleObject.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Zenstruck\Foundry\Tests\Fixture; + +/** + * @author Nicolas PHILIPPE + */ +final class SimpleObject +{ + public function __construct( + public string $prop1, + public ?string $prop2 = null, + ) { + } +} diff --git a/tests/Integration/Faker/FakerSeedSetFromLegacyConfigKernelTest.php b/tests/Integration/Faker/FakerSeedSetFromLegacyConfigKernelTest.php index 44153cff1..8b8ec95e0 100644 --- a/tests/Integration/Faker/FakerSeedSetFromLegacyConfigKernelTest.php +++ b/tests/Integration/Faker/FakerSeedSetFromLegacyConfigKernelTest.php @@ -31,9 +31,6 @@ final class FakerSeedSetFromLegacyConfigKernelTest extends KernelTestCase { use Factories, FakerTestTrait, ResetDatabase; - /** - * @test - */ #[Test] #[IgnoreDeprecations] public function faker_seed_by_configuration_is_deprecated(): void diff --git a/tests/Unit/ObjectFactoryTest.php b/tests/Unit/ObjectFactoryTest.php index 7e80ce14e..b32dfbe37 100644 --- a/tests/Unit/ObjectFactoryTest.php +++ b/tests/Unit/ObjectFactoryTest.php @@ -19,6 +19,7 @@ use Zenstruck\Foundry\Test\Factories; use Zenstruck\Foundry\Tests\Fixture\Factories\Object1Factory; use Zenstruck\Foundry\Tests\Fixture\Factories\Object2Factory; +use Zenstruck\Foundry\Tests\Fixture\Factories\SimpleObjectFactory; use Zenstruck\Foundry\Tests\Fixture\Object1; use function Zenstruck\Foundry\factory; @@ -434,6 +435,43 @@ public function can_use_sequence_with_associative_array(): void ); } + /** + * @test + */ + #[Test] + public function distribute(): void + { + $objects = SimpleObjectFactory::new()->distribute('prop1', ['foo', 'bar'])->create(); + + self::assertCount(2, $objects); + self::assertSame('foo', $objects[0]->prop1); + self::assertSame('bar', $objects[1]->prop1); + } + + /** + * @test + */ + #[Test] + public function distribute_on_factory_collection(): void + { + $objects = SimpleObjectFactory::new()->many(2)->distribute('prop1', ['foo', 'bar'])->create(); + + self::assertCount(2, $objects); + self::assertSame('foo', $objects[0]->prop1); + self::assertSame('bar', $objects[1]->prop1); + } + + /** + * @test + */ + #[Test] + public function providing_invalid_values_number_to_distribute_throws(): void + { + $this->expectException(\InvalidArgumentException::class); + + SimpleObjectFactory::new()->many(2)->distribute('prop1', ['foo']); + } + /** * @test */