Learn the fundamentals of using PHP Builder Generator to create builders for your classes.
The core of PHP Builder Generator is the #[Builder]
attribute. Add it to any class you want to generate a builder for:
<?php
namespace App\Model;
use MaxBeckers\PhpBuilderGenerator\Attributes\Builder;
#[Builder]
class User
{
public function __construct(
public string $name,
public string $email,
public ?int $age = null
) {}
}
Builders are automatically generated when you:
composer install
composer update
Generate builders manually using the CLI:
./vendor/bin/php-builder-generator
Once generated, builders provide a fluent interface for object creation:
<?php
use App\Model\UserBuilder;
// Create using builder pattern
$user = UserBuilder::builder()
->name('John Doe')
->email('john@example.com')
->age(30)
->build();
// Equivalent to:
$user = new User(
name: 'John Doe',
email: 'john@example.com',
age: 30
);
Every builder provides a static factory method (default name builder()
):
$builder = UserBuilder::builder();
For each property/constructor parameter, a setter method is generated:
$builder->name('John Doe'); // For 'name' parameter
$builder->email('john@test.com'); // For 'email' parameter
$builder->age(25); // For 'age' parameter
The build()
method creates the final object:
$user = $builder->build();
By default, setter methods return $this
, enabling method chaining:
$user = UserBuilder::builder()
->name('John')
->email('john@example.com')
->age(30)
->build();
For classes with constructors, the builder uses constructor parameters:
#[Builder]
class Product
{
public function __construct(
public string $name,
public float $price,
public ?string $description = null
) {}
}
$product = ProductBuilder::builder()
->name('Laptop')
->price(999.99)
->description('High-performance laptop')
->build();
For classes with public properties (no constructor required):
#[Builder]
class Settings
{
public string $theme = 'dark';
public bool $notifications = true;
public int $timeout = 30;
}
$settings = SettingsBuilder::builder()
->theme('light')
->notifications(false)
->timeout(60)
->build();
Builders preserve all type information from your original class:
#[Builder]
class TypedClass
{
public function __construct(
public string $name, // string required
public ?int $age = null, // nullable int
public array $roles = [], // array with default
public User $owner, // object required
public \DateTimeImmutable $created = new \DateTimeImmutable() // object with default
) {}
}
$instance = TypedClassBuilder::builder()
->name('Test') // ✅ string
->age(25) // ✅ int
->age(null) // ✅ null allowed
->roles(['admin']) // ✅ array
->owner($userInstance) // ✅ User object
->created($dateTime) // ✅ DateTimeImmutable
->build();
Default values from constructor parameters are respected:
#[Builder]
class User
{
public function __construct(
public string $name,
public bool $active = true, // Default value
public array $roles = [] // Default value
) {}
}
// These are equivalent:
$user1 = UserBuilder::builder()->name('John')->build();
$user2 = new User('John'); // active=true, roles=[]
Default values from property declarations are also preserved:
#[Builder]
class Config
{
public string $theme = 'dark'; // Property default
public int $timeout = 30; // Property default
}
$config = ConfigBuilder::builder()->build(); // theme='dark', timeout=30
#[Builder]
class Order
{
public function __construct(
public User $customer,
public \DateTimeImmutable $orderDate,
public Money $total
) {}
}
$order = OrderBuilder::builder()
->customer($customer) // User object
->orderDate(new \DateTimeImmutable()) // DateTime object
->total(new Money(2999, 'USD')) // Money object
->build();
#[Builder]
class Cart
{
public function __construct(
public array $items = [],
public array $metadata = []
) {}
}
$cart = CartBuilder::builder()
->items([$item1, $item2, $item3]) // Array of objects
->metadata(['source' => 'web', 'campaign' => 'summer2024'])
->build();
enum Status: string
{
case ACTIVE = 'active';
case INACTIVE = 'inactive';
}
#[Builder]
class Account
{
public function __construct(
public string $name,
public Status $status = Status::ACTIVE
) {}
}
$account = AccountBuilder::builder()
->name('John Doe')
->status(Status::INACTIVE)
->build();
Generated builders provide full IDE support:
By default, builders are generated in:
generated/php-builder-generator/YourNamespace/YourClassBuilder.php
Add the generated directory to your composer.json
:
{
"autoload-dev": {
"psr-4": {
"App\\": "generated/php-builder-generator/"
}
}
}
Don’t forget to run:
composer dump-autoload
Builders are particularly useful in tests:
public function testUserCreation(): void
{
$user = UserBuilder::builder()
->name('Test User')
->email('test@example.com')
->build();
$this->assertEquals('Test User', $user->name);
}
class UserTestBuilder
{
public static function defaultUser(): UserBuilder
{
return UserBuilder::builder()
->name('Test User')
->email('test@example.com')
->active(true);
}
}
// Usage:
$user = UserTestBuilder::defaultUser()->name('Custom Name')->build();
Combine builders with static factory methods:
#[Builder]
class User
{
public function __construct(
public string $name,
public string $email,
public \DateTimeImmutable $createdAt
) {}
public static function create(string $name, string $email): self
{
return UserBuilder::builder()
->name($name)
->email($email)
->createdAt(new \DateTimeImmutable())
->build();
}
}
// Create builder with some defaults
$baseUser = UserBuilder::builder()
->active(true)
->roles(['user']);
// Create variations
$admin = clone $baseUser;
$admin->roles(['admin', 'user'])->build();
$guest = clone $baseUser;
$guest->roles(['guest'])->build();
$builder = UserBuilder::builder()
->name($name)
->email($email);
if ($isAdmin) {
$builder->roles(['admin', 'user']);
}
if ($age !== null) {
$builder->age($age);
}
$user = $builder->build();
Next: Configuration Options