Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

ella-stinnes's avatar

Factory Sort Order Field with Relationship

I'm trying to set the sort_order field in the task factory. The task sort_order should start from 1 within each project.

Database Tables

projects
id
project_name
tasks
id
project_id
task_name
sort_order

Task Factory

I don't know how to access the project_id if it's passed in from the project seeder.

public function definition(): array
{
	return [
		'project_id'	=> Project::inRandomOrder()->first()->id,
		'task_name'     => $this->faker->sentence(6),
		'sort_order'	=> Task::where('project_id', $project_id)->max('sort_order') + 1,
	];
}

Project Seeder

Project::factory()->count(20)->hasTasks(rand(5, 30))->create();
0 likes
8 replies
ella-stinnes's avatar

@jj15 Thank you for the suggestion, I've attempted to use the closure approach as you suggested but this is setting the sort order value to 1 for all records?

newbie360's avatar

@ella-stinnes Hello, avoid doing any sql query in the definition(), if you create 20 tasks, it will loop 20 times

public function definition(): array
{
	info('This is definition'); // Check the laravel.log

    return [
		'project_id'	=> Project::inRandomOrder()->first()->id, // move this to seeder or use state
		'task_name'     => $this->faker->sentence(6),
		'sort_order'	=> Task::where('project_id', $project_id)->max('sort_order') + 1, // you can use Sequence
	];
}

For your sort_order problem, you can use https://laravel.com/docs/11.x/eloquent-factories#factory-callbacks

use Illuminate\Database\Eloquent\Factories\Sequence;

// ...

    public function configure(): static
    {
        info('This is configure'); // Check the laravel.log

        return $this->sequence(function (Sequence $sequence) {
            return [
                'sort_order' => $sequence->index + 1,
            ];
        });
    }

public function definition(): array
{
	info('This is definition'); // Check the laravel.log

	return [
		'project_id'	=> Project::factory(),
		'task_name'     => $this->faker->sentence(6),
		// 'sort_order' => 0,
	];
}
ella-stinnes's avatar

@newbie360 Thank you for the suggestion - the sequence increments the sort_order field by 1 for each record, however it doesn't account for the different projects.

For each project, the task sort_order field should reset to 1. Is there a way to implement this using the configure method?

newbie360's avatar

@ella-stinnes Oops sorry, i tested on factory for only, seems some problem of factory has

so i think need to use count property to do some calculation, but the count property only visible in state

can you try this

    private int $orderCount = 0;
    private int $orderIndex = 0;

    public function configure(): static
    {
        return $this->state(function () {
            if ($this->orderCount == 0) {
                $this->orderCount = $this->count; // marker
            }

            if ($this->orderIndex == $this->orderCount) { // reset
                $this->orderCount = 0;
                $this->orderIndex = 0;
            }

            return [
                'sort_order' => ++$this->orderIndex,
            ];
        });
    }

and then test factory has and factory for like this

Project::factory()
    ->count(rand(1, 4))
    ->has(Task::factory()->count(rand(10, 15)))
    ->create()

Task::factory()
    ->count(rand(5, 8))
    ->for(Project::factory())
    ->create()
newbie360's avatar
newbie360
Best Answer
Level 24

@ella-stinnes With this

<?php

namespace Database\Factories;

use Illuminate\Database\Eloquent\Factories\Factory;

class TaskFactory extends Factory
{
    public function configure(): static
    {
        return $this->state(function () {
            dd($this->count);
        });
    }

    public function definition(): array
    {
        return [
            'project_id'	=> Project::factory(),
            'task_name'     => $this->faker->sentence(6),
            // 'sort_order' => 0,
        ];
    }
}

open Tinker

Task::factory()->count(4)->create()

did you see 4 ?

ella-stinnes's avatar

@newbie360 Thank you, this is now working!

For my understanding, you mentioned that the following code in the definition would loop round 20 times so it's not efficient - but any ideas why every record created just had 1 as the sort order?

It wasn't incrementing and I have no idea why.

'sort_order' => function (array $attributes) {
    return Task::where('project_id', $attributes['project_id'])->max('sort_order') + 1;
},

Please or to participate in this conversation.