├── .gitattributes
├── .github
└── workflows
│ ├── run-cs-fix.yml
│ └── run-tests.yml
├── .gitignore
├── composer.json
├── composer.lock
├── license.md
├── phpunit.xml
├── readme.md
└── src
├── CrossJoinSequence.php
├── Data.php
├── Factory.php
├── HasFactory.php
└── Sequence.php
/.gitattributes:
--------------------------------------------------------------------------------
1 | art/ export-ignore
2 | tests/ export-ignore
--------------------------------------------------------------------------------
/.github/workflows/run-cs-fix.yml:
--------------------------------------------------------------------------------
1 | name: run-cs-fix
2 |
3 | on:
4 | push:
5 |
6 | jobs:
7 | lint:
8 | runs-on: ubuntu-latest
9 | strategy:
10 | fail-fast: true
11 | matrix:
12 | php: [8.3]
13 |
14 | steps:
15 | - name: Checkout code
16 | uses: actions/checkout@v4
17 |
18 | - name: Setup PHP
19 | uses: shivammathur/setup-php@v2
20 | with:
21 | php-version: ${{ matrix.php }}
22 | extensions: json, dom, curl, libxml, mbstring
23 | coverage: none
24 |
25 | - name: Install Pint
26 | run: composer global require laravel/pint
27 |
28 | - name: Run Pint
29 | run: pint
30 |
31 | - name: Commit linted files
32 | uses: stefanzweifel/git-auto-commit-action@v5
33 | with:
34 | commit_message: "Fix code style"
35 |
--------------------------------------------------------------------------------
/.github/workflows/run-tests.yml:
--------------------------------------------------------------------------------
1 | name: run-tests
2 |
3 | on:
4 | push:
5 | pull_request:
6 | schedule:
7 | - cron: "0 0 * * *"
8 |
9 | jobs:
10 | run-tests:
11 | runs-on: ${{ matrix.os }}
12 | strategy:
13 | fail-fast: false
14 | matrix:
15 | os: [ ubuntu-latest ]
16 | php: [ 8.3, 8.2, 8.1 ]
17 | laravel: [ 11.*, 10.*, 9.* ]
18 | dependency-version: [ prefer-stable ]
19 | include:
20 | - laravel: 11.*
21 | testbench: 9.*
22 | - laravel: 10.*
23 | testbench: 8.*
24 | - laravel: 9.*
25 | testbench: 7.*
26 | exclude:
27 | - laravel: 11.*
28 | php: 8.1
29 | - laravel: 9.*
30 | php: 8.3
31 |
32 | name: ${{ matrix.os }} - P${{ matrix.php }} - L${{ matrix.laravel }} - ${{ matrix.dependency-version }}
33 |
34 | steps:
35 | - name: Checkout code
36 | uses: actions/checkout@v4
37 |
38 | - name: Cache dependencies
39 | uses: actions/cache@v4
40 | with:
41 | path: ~/.composer/cache/files
42 | key: dependencies-laravel-${{ matrix.laravel }}-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }}
43 |
44 | - name: Setup PHP
45 | uses: shivammathur/setup-php@v2
46 | with:
47 | php-version: ${{ matrix.php }}
48 |
49 | - name: Install dependencies
50 | run: |
51 | composer require "laravel/framework:${{ matrix.laravel }}" "orchestra/testbench:${{ matrix.testbench }}" --no-interaction --no-update --dev
52 | composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction
53 |
54 | - name: Execute tests
55 | run: vendor/bin/pest
56 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor/
2 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "directorytree/dummy",
3 | "type": "library",
4 | "license": "MIT",
5 | "archive": {
6 | "exclude": [
7 | "/tests"
8 | ]
9 | },
10 | "autoload": {
11 | "psr-4": {
12 | "DirectoryTree\\Dummy\\": "src/"
13 | }
14 | },
15 | "autoload-dev": {
16 | "psr-4": {
17 | "DirectoryTree\\Dummy\\Tests\\": "tests/"
18 | }
19 | },
20 | "authors": [
21 | {
22 | "name": "Steve Bauman",
23 | "email": "steven_bauman@outlook.com"
24 | }
25 | ],
26 | "required": {
27 | "fakerphp/faker": "^1.0",
28 | "illuminate/collections": "^9.0|^10.0|^11.0"
29 | },
30 | "require-dev": {
31 | "laravel/pint": "^1.0",
32 | "pestphp/pest": "^1.0|^2.0|^3.0",
33 | "orchestra/testbench": "^7.0|^8.0|^9.0"
34 | },
35 | "config": {
36 | "allow-plugins": {
37 | "pestphp/pest-plugin": true
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/license.md:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Steve Bauman
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
2 |
2 |
3 |
6 | Generate PHP class instances populated with dummy data using Faker 7 |
8 | 9 | 15 | 16 | --- 17 | 18 | ## Index 19 | 20 | - [Requirements](#requirements) 21 | - [Installation](#installation) 22 | - [Introduction](#introduction) 23 | - [Setup](#setup) 24 | - [HasFactory Trait](#hasfactory-trait) 25 | - [Class Factory](#class-factory) 26 | - [Usage](#usage) 27 | - [Factory States](#factory-states) 28 | - [Factory Callbacks](#factory-callbacks) 29 | - [Factory Sequences](#factory-sequences) 30 | - [Factory Collections](#factory-collections) 31 | 32 | ## Requirements 33 | 34 | - PHP >= 8.0 35 | 36 | ## Installation 37 | 38 | You can install the package via composer: 39 | 40 | ```bash 41 | composer require directorytree/dummy --dev 42 | ``` 43 | 44 | ## Introduction 45 | 46 | Consider you have a class representing a restaurant reservation: 47 | 48 | ```php 49 | namespace App\Data; 50 | 51 | class Reservation 52 | { 53 | /** 54 | * Create a new reservation instance. 55 | */ 56 | public function __construct( 57 | public string $name, 58 | public string $email, 59 | public DateTime $date, 60 | ) {} 61 | } 62 | ``` 63 | 64 | To make dummy instances of this class during testing, you have to manually populate it with dummy data. 65 | 66 | This can quickly get out of hand as your class grows, and you may find yourself writing the same dummy data generation code over and over again. 67 | 68 | Dummy provides you with a simple way to generate dummy instances of your classes using a simple API: 69 | 70 | ```php 71 | // Generate one instance: 72 | $reservation = Reservation::factory()->make(); 73 | 74 | // Generate multiple instances: 75 | $collection = Reservation::factory()->count(5)->make(); 76 | ``` 77 | 78 | ## Setup 79 | 80 | Dummy provides you two different ways to generate classes with dummy data. 81 | 82 | ### HasFactory Trait 83 | 84 | The `HasFactory` trait is applied directly to the class you would like to generate dummy instances of. 85 | 86 | To use the `HasFactory` trait, you must implement the `toFactoryInstance` and `getFactoryDefinition` methods: 87 | 88 | > [!note] 89 | > The `HasFactory` trait does not provide you the capability of defining state methods or callbacks. 90 | > If you need this functionality, you should define a separate `Factory` class instead. 91 | 92 | ```php 93 | namespace App\Data; 94 | 95 | use DateTime; 96 | use Faker\Generator; 97 | use DirectoryTree\Dummy\HasFactory; 98 | 99 | class Reservation 100 | { 101 | use HasFactory; 102 | 103 | /** 104 | * Create a new reservation instance. 105 | */ 106 | public function __construct( 107 | public string $name, 108 | public string $email, 109 | public DateTime $date, 110 | ) {} 111 | 112 | /** 113 | * Define the factory's default state. 114 | */ 115 | protected function getFactoryDefinition(Generator $faker): array 116 | { 117 | return [ 118 | 'name' => $faker->name(), 119 | 'email' => $faker->email(), 120 | 'datetime' => $faker->dateTime(), 121 | ]; 122 | } 123 | 124 | /** 125 | * Create a new instance of the class using the factory definition. 126 | */ 127 | protected static function toFactoryInstance(array $attributes): static 128 | { 129 | return new static( 130 | $attributes['name'], 131 | $attributes['email'], 132 | $attributes['datetime'], 133 | ); 134 | } 135 | } 136 | ``` 137 | 138 | Once implemented, you may call the `Reservation::factory()` method to create a new dummy factory: 139 | 140 | ```php 141 | $factory = Reservation::factory(); 142 | ``` 143 | 144 | ### Class Factory 145 | 146 | If you need more control over the dummy data generation process, you may use the `Factory` class. 147 | 148 | The `Factory` class is used to generate dummy instances of a class using a separate factory class definition. 149 | 150 | To use the `Factory` class, you must extend it with your own and override the `definition` and `generate` methods: 151 | 152 | ```php 153 | namespace App\Factories; 154 | 155 | use App\Data\Reservation; 156 | use DirectoryTree\Dummy\Factory; 157 | 158 | class ReservationFactory extends Factory 159 | { 160 | /** 161 | * Define the factory's default state. 162 | */ 163 | protected function definition(): array 164 | { 165 | return [ 166 | 'name' => $this->faker->name(), 167 | 'email' => $this->faker->email(), 168 | 'datetime' => $this->faker->dateTime(), 169 | ]; 170 | } 171 | 172 | /** 173 | * Generate a new instance of the class. 174 | */ 175 | protected function generate(array $attributes): Reservation 176 | { 177 | return new Reservation( 178 | $attributes['name'], 179 | $attributes['email'], 180 | $attributes['datetime'], 181 | ); 182 | } 183 | } 184 | ``` 185 | 186 | ## Usage 187 | 188 | Once you've defined a factory, you can generate dummy instances of your class using the `make` method: 189 | 190 | ```php 191 | // Using the trait: 192 | $reservation = Reservation::factory()->make(); 193 | 194 | // Using the factory class: 195 | $reservation = ReservationFactory::new()->make(); 196 | ``` 197 | 198 | To add or override attributes in your definition, you may pass an array of attributes to the `make` method: 199 | 200 | ```php 201 | $reservation = Reservation::factory()->make([ 202 | 'name' => 'John Doe', 203 | ]); 204 | ``` 205 | 206 | To generate multiple instances of the class, you may use the `count` method: 207 | 208 | > This will return a `Illuminate\SupportCollection` instance containing the generated classes. 209 | 210 | ```php 211 | $collection = Reservation::factory()->count(5)->make(); 212 | ``` 213 | 214 | ### Factory States 215 | 216 | State manipulation methods allow you to define discrete modifications 217 | that can be applied to your dummy factories in any combination. 218 | 219 | For example, your `App\Factories\Reservation` factory might contain a `tomorrow` 220 | state method that modifies one of its default attribute values: 221 | 222 | ```php 223 | class ReservationFactory extends Factory 224 | { 225 | // ... 226 | 227 | /** 228 | * Indicate that the reservation is for tomorrow. 229 | */ 230 | public function tomorrow(): Factory 231 | { 232 | return $this->state(function (array $attributes) { 233 | return ['datetime' => new DateTime('tomorrow')]; 234 | }); 235 | } 236 | } 237 | ``` 238 | 239 | ### Factory Callbacks 240 | 241 | Factory callbacks are registered using the `afterMaking` method and allow you to perform 242 | additional tasks after making or creating a class. You should register these callbacks 243 | by defining a `configure` method on your factory class. This method will be 244 | automatically called when the factory is instantiated: 245 | 246 | ```php 247 | class ReservationFactory extends Factory 248 | { 249 | // ... 250 | 251 | /** 252 | * Configure the dummy factory. 253 | */ 254 | protected function configure(): static 255 | { 256 | return $this->afterMaking(function (Reservation $reservation) { 257 | // ... 258 | }); 259 | } 260 | } 261 | ``` 262 | 263 | ### Factory Sequences 264 | 265 | Sometimes you may wish to alternate the value of a given attribute for each generated 266 | class. 267 | 268 | You may accomplish this by defining a state transformation as a `sequence`: 269 | 270 | ```php 271 | Reservation::factory() 272 | ->count(3) 273 | ->sequence( 274 | ['datetime' => new Datetime('tomorrow')], 275 | ['datetime' => new Datetime('next week')], 276 | ['datetime' => new Datetime('next month')], 277 | ) 278 | ->make(); 279 | ``` 280 | 281 | ### Factory Collections 282 | 283 | By default, when making more than one dummy class, an instance of `Illuminate\Support\Collection` will be returned. 284 | 285 | If you need to customize the collection of classes generated by a factory, you may override the `collect` method: 286 | 287 | ```php 288 | class ReservationFactory extends Factory 289 | { 290 | // ... 291 | 292 | /** 293 | * Create a new collection of classes. 294 | */ 295 | public function collect(array $instances = []): ReservationCollection 296 | { 297 | return new ReservationCollection($instances); 298 | } 299 | } 300 | ``` 301 | -------------------------------------------------------------------------------- /src/CrossJoinSequence.php: -------------------------------------------------------------------------------- 1 | $value) { 22 | $this->attributes[$key] = $value; 23 | } 24 | } 25 | 26 | /** 27 | * Set an attribute on the data instance using "dot" notation. 28 | */ 29 | public function set(string $key, mixed $value): static 30 | { 31 | data_set($this->attributes, $key, $value); 32 | 33 | return $this; 34 | } 35 | 36 | /** 37 | * Get an attribute from the data instance using "dot" notation. 38 | */ 39 | public function get(string $key, mixed $default = null): mixed 40 | { 41 | return data_get($this->attributes, $key, $default); 42 | } 43 | 44 | /** 45 | * Get an attribute from the data instance. 46 | */ 47 | public function value(string $key, mixed $default = null): mixed 48 | { 49 | if (array_key_exists($key, $this->attributes)) { 50 | return $this->attributes[$key]; 51 | } 52 | 53 | return value($default); 54 | } 55 | 56 | /** 57 | * Get the value of the given key as a new data instance. 58 | */ 59 | public function scope(string $key, mixed $default = null): static 60 | { 61 | return new static( 62 | (array) $this->get($key, $default) 63 | ); 64 | } 65 | 66 | /** 67 | * Get the attributes from the data instance. 68 | * 69 | * @param array|mixed|null $keys 70 | */ 71 | public function all(mixed $keys = null): array 72 | { 73 | $data = $this->attributes; 74 | 75 | if (! $keys) { 76 | return $data; 77 | } 78 | 79 | $results = []; 80 | 81 | foreach (is_array($keys) ? $keys : func_get_args() as $key) { 82 | Arr::set($results, $key, Arr::get($data, $key)); 83 | } 84 | 85 | return $results; 86 | } 87 | 88 | /** 89 | * Convert the object into something JSON serializable. 90 | */ 91 | public function jsonSerialize(): array 92 | { 93 | return $this->attributes; 94 | } 95 | 96 | /** 97 | * Determine if the given offset exists. 98 | */ 99 | public function offsetExists(mixed $offset): bool 100 | { 101 | return isset($this->attributes[$offset]); 102 | } 103 | 104 | /** 105 | * Get the value for a given offset. 106 | */ 107 | public function offsetGet(mixed $offset): mixed 108 | { 109 | return $this->value($offset); 110 | } 111 | 112 | /** 113 | * Set the value at the given offset. 114 | */ 115 | public function offsetSet(mixed $offset, mixed $value): void 116 | { 117 | $this->attributes[$offset] = $value; 118 | } 119 | 120 | /** 121 | * Unset the value at the given offset. 122 | */ 123 | public function offsetUnset(mixed $offset): void 124 | { 125 | unset($this->attributes[$offset]); 126 | } 127 | 128 | /** 129 | * Handle dynamic calls to the data instance to set attributes. 130 | */ 131 | public function __call(string $method, array $parameters): static 132 | { 133 | $this->attributes[$method] = count($parameters) > 0 ? reset($parameters) : true; 134 | 135 | return $this; 136 | } 137 | 138 | /** 139 | * Dynamically retrieve the value of an attribute. 140 | */ 141 | public function __get(string $key): mixed 142 | { 143 | return $this->value($key); 144 | } 145 | 146 | /** 147 | * Dynamically set the value of an attribute. 148 | */ 149 | public function __set(string $key, mixed $value): void 150 | { 151 | $this->offsetSet($key, $value); 152 | } 153 | 154 | /** 155 | * Dynamically check if an attribute is set. 156 | */ 157 | public function __isset(string $key): bool 158 | { 159 | return $this->offsetExists($key); 160 | } 161 | 162 | /** 163 | * Dynamically unset an attribute. 164 | */ 165 | public function __unset(string $key): void 166 | { 167 | $this->offsetUnset($key); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /src/Factory.php: -------------------------------------------------------------------------------- 1 | faker = $this->newFaker(); 31 | } 32 | 33 | /** 34 | * Get a new dummy factory instance for the given attributes. 35 | */ 36 | public static function new(Closure|array $attributes = []): static 37 | { 38 | return (new static)->state($attributes)->configure(); 39 | } 40 | 41 | /** 42 | * Get a new dummy factory instance for the given number of classes. 43 | */ 44 | public static function times(int $count): static 45 | { 46 | return static::new()->count($count); 47 | } 48 | 49 | /** 50 | * Define the dummy class' default state. 51 | */ 52 | protected function definition(): array 53 | { 54 | return []; 55 | } 56 | 57 | /** 58 | * Generate a new dummy class instance. 59 | */ 60 | protected function generate(array $attributes): mixed 61 | { 62 | return new Data($attributes); 63 | } 64 | 65 | /** 66 | * Configure the dummy factory. 67 | */ 68 | protected function configure(): static 69 | { 70 | return $this; 71 | } 72 | 73 | /** 74 | * Create a new collection of dummy instances. 75 | */ 76 | protected function collect(array $instances = []): mixed 77 | { 78 | return new Collection($instances); 79 | } 80 | 81 | /** 82 | * Get the raw attributes generated by the factory. 83 | */ 84 | public function raw(Closure|array $attributes = []): array 85 | { 86 | if ($this->count === null) { 87 | return $this->state($attributes)->getExpandedAttributes(); 88 | } 89 | 90 | return array_map(function () use ($attributes) { 91 | return $this->state($attributes)->getExpandedAttributes(); 92 | }, range(1, $this->count)); 93 | } 94 | 95 | /** 96 | * Create a collection of classes. 97 | */ 98 | public function make(Closure|array $attributes = []): mixed 99 | { 100 | if (! empty($attributes)) { 101 | return $this->state($attributes)->make(); 102 | } 103 | 104 | if ($this->count === null) { 105 | return tap($this->makeInstance(), function ($instance) { 106 | $this->callAfterMaking($this->collect([$instance])); 107 | }); 108 | } 109 | 110 | if ($this->count < 1) { 111 | return $this->collect(); 112 | } 113 | 114 | $instances = $this->collect(array_map(function () { 115 | return $this->makeInstance(); 116 | }, range(1, $this->count))); 117 | 118 | $this->callAfterMaking($instances); 119 | 120 | return $instances; 121 | } 122 | 123 | /** 124 | * Make an instance of the class with the given attributes. 125 | */ 126 | protected function makeInstance(): mixed 127 | { 128 | $attributes = $this->getExpandedAttributes(); 129 | 130 | if ($this->using) { 131 | return ($this->using)($this->faker, $attributes); 132 | } 133 | 134 | return $this->generate($attributes); 135 | } 136 | 137 | /** 138 | * Get a raw attributes array for the class. 139 | */ 140 | protected function getExpandedAttributes(): array 141 | { 142 | return $this->expandAttributes($this->getRawAttributes()); 143 | } 144 | 145 | /** 146 | * Get the raw attributes for the class as an array. 147 | */ 148 | protected function getRawAttributes(): array 149 | { 150 | return $this->states->pipe(function ($states) { 151 | return $states; 152 | })->reduce(function ($carry, $state) { 153 | if ($state instanceof Closure) { 154 | $state = $state->bindTo($this); 155 | } 156 | 157 | return array_merge($carry, $state($carry)); 158 | }, $this->definition()); 159 | } 160 | 161 | /** 162 | * Expand all attributes to their underlying values. 163 | */ 164 | protected function expandAttributes(array $definition): array 165 | { 166 | return (new Collection($definition))->map(function ($attribute, $key) use (&$definition) { 167 | if (is_callable($attribute) && ! is_string($attribute) && ! is_array($attribute)) { 168 | $attribute = $attribute($definition); 169 | } 170 | 171 | $definition[$key] = $attribute; 172 | 173 | return $attribute; 174 | })->all(); 175 | } 176 | 177 | /** 178 | * Add a new state transformation to the class definition. 179 | */ 180 | public function state(mixed $state): static 181 | { 182 | return $this->newInstance([ 183 | 'states' => $this->states->concat([ 184 | is_callable($state) ? $state : function () use ($state) { 185 | return $state; 186 | }, 187 | ]), 188 | ]); 189 | } 190 | 191 | /** 192 | * Set a single class attribute. 193 | */ 194 | public function set(string|int $key, mixed $value): static 195 | { 196 | return $this->state([$key => $value]); 197 | } 198 | 199 | /** 200 | * Add a new sequenced state transformation to the class definition. 201 | */ 202 | public function sequence(mixed ...$sequence): static 203 | { 204 | return $this->state(new Sequence(...$sequence)); 205 | } 206 | 207 | /** 208 | * Add a new sequenced state transformation to the class definition and update the pending creation count to the size of the sequence. 209 | */ 210 | public function forEachSequence(array ...$sequence): static 211 | { 212 | return $this->state(new Sequence(...$sequence))->count(count($sequence)); 213 | } 214 | 215 | /** 216 | * Add a new cross joined sequenced state transformation to the class definition. 217 | */ 218 | public function crossJoinSequence(array ...$sequence): static 219 | { 220 | return $this->state(new CrossJoinSequence(...$sequence)); 221 | } 222 | 223 | /** 224 | * Set the callback to be used to create the class instance. 225 | */ 226 | public function using(Closure $callback): static 227 | { 228 | return $this->newInstance([ 229 | 'using' => $callback, 230 | ]); 231 | } 232 | 233 | /** 234 | * Add a new "after making" callback to the class definition. 235 | */ 236 | public function afterMaking(Closure $callback): static 237 | { 238 | return $this->newInstance([ 239 | 'afterMaking' => $this->afterMaking->concat([$callback]), 240 | ]); 241 | } 242 | 243 | /** 244 | * Call the "after making" callbacks for the given class instances. 245 | */ 246 | protected function callAfterMaking(iterable $instances): void 247 | { 248 | foreach ($instances as $instance) { 249 | $this->afterMaking->each( 250 | fn ($callback) => $callback($instance) 251 | ); 252 | } 253 | } 254 | 255 | /** 256 | * Specify how many dummy classes should be generated. 257 | */ 258 | public function count(?int $count): static 259 | { 260 | return $this->newInstance(['count' => $count]); 261 | } 262 | 263 | /** 264 | * Create a new instance of the factory builder with the given mutated properties. 265 | */ 266 | protected function newInstance(array $arguments = []): static 267 | { 268 | return new static(...array_values(array_merge([ 269 | 'count' => $this->count, 270 | 'using' => $this->using, 271 | 'states' => $this->states, 272 | 'afterMaking' => $this->afterMaking, 273 | ], $arguments))); 274 | } 275 | 276 | /** 277 | * Get the Faker instance. 278 | */ 279 | public function faker(): Generator 280 | { 281 | return $this->faker ??= $this->newFaker(); 282 | } 283 | 284 | /** 285 | * Get a new Faker instance. 286 | */ 287 | protected function newFaker(): Generator 288 | { 289 | if (class_exists(Container::class)) { 290 | return Container::getInstance()->make(Generator::class); 291 | } 292 | 293 | return FakerFactory::create(); 294 | } 295 | } 296 | -------------------------------------------------------------------------------- /src/HasFactory.php: -------------------------------------------------------------------------------- 1 | using( 15 | function (Generator $faker, array $attributes) { 16 | return static::toFactoryInstance( 17 | array_merge(static::getFactoryDefinition($faker, $attributes), $attributes) 18 | ); 19 | } 20 | ); 21 | } 22 | 23 | /** 24 | * Get a new factory instance. 25 | */ 26 | protected static function newFactory(array $attributes): Factory 27 | { 28 | return Factory::new($attributes); 29 | } 30 | 31 | /** 32 | * Transform the dummy data into a class instance. 33 | */ 34 | abstract protected static function toFactoryInstance(array $attributes): mixed; 35 | 36 | /** 37 | * Define the dummy data definition. 38 | */ 39 | abstract protected static function getFactoryDefinition(Generator $faker): array; 40 | } 41 | -------------------------------------------------------------------------------- /src/Sequence.php: -------------------------------------------------------------------------------- 1 | sequence = $sequence; 30 | $this->count = count($sequence); 31 | } 32 | 33 | /** 34 | * Get the current count of the sequence items. 35 | */ 36 | public function count(): int 37 | { 38 | return $this->count; 39 | } 40 | 41 | /** 42 | * Get the next value in the sequence. 43 | */ 44 | public function __invoke(): mixed 45 | { 46 | return tap(value($this->sequence[$this->index % $this->count], $this), function () { 47 | $this->index = $this->index + 1; 48 | }); 49 | } 50 | } 51 | --------------------------------------------------------------------------------