This package is a fresh take on how to set the state of your DTOs in a simple and delightful way.
The API is takes some hints from Laravel's Eloquent Factories, but adds some niceties such as setting state via dot syntax and using the set() helper method on the fly.
This package does not require any other dependencies, allowing you to make a factory for anything.
The examples use the DataModel trait, making easier to build your DTOs, but it is not required.
Install the package via Composer:
composer require zero-to-prod/data-model-factory
- DataModel: Transform data into a class.
- DataModelHelper: Helpers for a
DataModel
. - Transformable: Transform a
DataModel
into different types.
This example makes use of the DataModel trait to instantiate the User
class.
You can install the DataModel package like this:
composer require zero-to-prod/data-model
If you don't want to use this trait, you can customize the class instantiation this way.
-
Include the Factory trait in your factory class.
-
Set the
$model
property to the class you want to instantiate. -
Implement a
definition()
method that returns an array of default values. -
NOTE: The
$this->state()
method accepts dot syntax, arrays, or a callback.
class User
{
use \Zerotoprod\DataModelFactory\DataModel;
public $first_name;
public $last_name;
public $address;
public static function factory(array $states = []): UserFactory
{
return new UserFactory($states);
}
}
class UserFactory
{
use \Zerotoprod\DataModelFactory\Factory;
/* This is the class to be instantiated with the make() method */
protected $model = User::class;
protected function definition(): array
{
return [
'first_name' => 'John',
'last_name' => 'N/A',
'address' => [
'street' => 'Memory Lane'
]
];
}
public function setStreet(string $value): self
{
/** Dot Syntax */
return $this->state('address.street', $value);
}
public function setFirstName(string $value): self
{
/** Array Syntax */
return $this->state(['first_name' => $value]);
}
public function setLastName(): self
{
/** Closure Syntax */
return $this->state(function ($context) {
return ['first_name' => $context['last_name']];
});
}
/* Optionally implement for better static analysis */
public function make(): User
{
return $this->instantiate();
}
}
$User = UserFactory::factory([User::last_name => 'Doe'])
->setFirstName('Jane')
->make();
User::factory([User::last_name => 'Doe'])->make(); // Also works for this example
echo $User->first_name; // 'Jane'
echo $User->last_name; // 'Doe'
You can use the set()
helper method to fluently modify the state of your model in a convenient way.
This is a great way to modify a model without having to implement a method in the factory.
$User = User::factory()
->set('first_name', 'John')
->set(['last_name' => 'Doe'])
->set(function ($context) {
return ['surname' => $context['last_name']];
})
->set('address.postal_code', '46789') // dot syntax for nested values
->make();
echo $User->first_name; // John
echo $User->last_name; // Doe
echo $User->surname; // Doe
echo $User->address->postal_code; // 46789
To customize instantiation, override the make()
method.
class User
{
public function __construct(public string $fist_name, public string $last_name)
{
}
}
class UserFactory
{
use \Zerotoprod\DataModelFactory\Factory;
private function definition(): array
{
return [
'first_name' => 'John',
'last_name' => 'Doe',
];
}
private function make(): User
{
return new User($this->context['first_name'], $this->context['last_name']);
}
}
$User = UserFactory::factory()->make();
echo $User->first_name; // 'Jane'
echo $User->last_name; // 'Doe'