Dynamic Data Generation: Sequencing Factory Attributes in Laravel

Dynamic Data Generation: Sequencing Factory Attributes in Laravel

When working with Laravel factories, you often need to generate data that follows specific patterns or sequences. Laravel's factory sequencing feature provides a powerful way to create varied and realistic test data. Let's explore how you can use this feature to enhance your data generation process.

Understanding Factory Sequencing

Factory sequencing allows you to define a series of values that will be cycled through as your factory creates models. This is particularly useful for creating data sets with alternating or incrementing values.

Basic Usage

Here's a simple example of how to use sequencing:

use Illuminate\Database\Eloquent\Factories\Sequence;

$users = User::factory()
    ->count(4)
    ->state(new Sequence(
        ['role' => 'admin'],
        ['role' => 'user'],
    ))
    ->create();

This will create four users, alternating between admin and user roles.

Advanced Sequencing

You can use closures for more complex sequencing logic:

$users = User::factory()
    ->count(10)
    ->sequence(fn ($sequence) => ['name' => 'User '.$sequence->index])
    ->create();

This creates users with names like "User 0", "User 1", "User 2", etc.

Practical Applications

1. Creating Tiered Pricing

For an e-commerce application, you might want to create products with tiered pricing:

$products = Product::factory()
    ->count(9)
    ->sequence(
        ['tier' => 'basic', 'price' => 9.99],
        ['tier' => 'pro', 'price' => 19.99],
        ['tier' => 'enterprise', 'price' => 49.99]
    )
    ->create();

This creates three sets of products, each with a different tier and price.

2. Generating Test User Data

When creating test users with varying permissions:

$users = User::factory()
    ->count(5)
    ->sequence(
        ['role' => 'admin', 'permissions' => ['manage_users', 'manage_content']],
        ['role' => 'editor', 'permissions' => ['edit_content']],
        ['role' => 'viewer', 'permissions' => []]
    )
    ->create();

3. Simulating Time-based Data

For creating data that simulates entries over time:

$posts = Post::factory()
    ->count(10)
    ->sequence(fn ($sequence) => [
        'created_at' => now()->subDays($sequence->index),
        'title' => 'Post for day -' . $sequence->index
    ])
    ->create();

This creates posts with decreasing dates and corresponding titles.

Real-World Scenario: E-commerce Order System

Let's consider a more complex scenario for an e-commerce order system:

use App\Models\Order;
use App\Models\Product;
use Illuminate\Database\Eloquent\Factories\Sequence;

$orders = Order::factory()
    ->count(20)
    ->sequence(fn ($sequence) => [
        'order_number' => 'ORD-' . str_pad($sequence->index + 1, 5, '0', STR_PAD_LEFT),
        'status' => $sequence->index % 5 == 0 ? 'cancelled' : 'completed',
        'total' => 0 // We'll calculate this later
    ])
    ->create()
    ->each(function ($order) {
        // Add 1-5 products to each order
        $products = Product::factory()
            ->count(rand(1, 5))
            ->sequence(fn ($sequence) => [
                'price' => $sequence->index * 10 + 9.99, // Prices: 9.99, 19.99, 29.99, etc.
            ])
            ->create();

        $order->products()->attach($products, [
            'quantity' => rand(1, 3),
        ]);

        // Calculate and update the total
        $total = $order->products->sum(function ($product) use ($order) {
            return $product->price * $product->pivot->quantity;
        });

        $order->update(['total' => $total]);
    });

This complex example demonstrates:

  • Sequencing order numbers
  • Alternating order statuses
  • Creating products with sequenced prices for each order
  • Calculating order totals based on products and quantities

Using $index and $count

In sequence closures, you have access to both $index and $count:

$users = User::factory()
    ->count(5)
    ->sequence(fn (Sequence $sequence) => [
        'name' => 'User ' . ($sequence->index + 1) . ' of ' . $sequence->count,
    ])
    ->create();

This creates users with names like "User 1 of 5", "User 2 of 5", etc.

Combining with Other Factory Features

Sequencing can be combined with other factory features like states and relationships:

$posts = Post::factory()
    ->count(3)
    ->state(new Sequence(
        ['status' => 'draft'],
        ['status' => 'published'],
        ['status' => 'archived']
    ))
    ->has(Comment::factory()->count(3))
    ->create();

This creates posts with different statuses, each having three comments.

Factory sequencing in Laravel provides a powerful way to generate diverse and realistic test data. By allowing you to define sequences of attributes or use dynamic logic, it enables the creation of complex data sets that closely mimic real-world scenarios. Whether you're writing tests, seeding databases, or generating demo data, mastering factory sequencing can significantly enhance the quality and realism of your generated data.

If this guide was helpful to you, subscribe to my daily newsletter and give me a follow on X/Twitter. It helps a lot!

Subscribe to Harris Raftopoulos

Don’t miss out on the latest issues. Sign up now to get access to the library of members-only issues.
jamie@example.com
Subscribe