Skip to content

Workbench UserFactory conflicts with app factories, causing recursive definitions in PHP Intelephense VS Code #608

@aryanjaya

Description

@aryanjaya

Fortify Version

1.30.0

Laravel Version

12.31.1

PHP Version

8.2.9

Database Driver & Version

No response

Description

When installing Laravel Fortify, the vendor/laravel/fortify/workbench/database/factories/UserFactory.php file creates a naming conflict with the application's own database/factories/UserFactory.php. This leads to IDE static analysis tools (e.g., PHP Intelephense in VS Code) indexing both files, resulting in incorrect type inference for factory methods like create(). Specifically:

  • Intelephense generates a recursive/malformed type definition: class UserFactory extends UserFactory with @template TModel of \App\Models\User and @extends \Illuminate\Database\Eloquent\Factories\Factory<TModel>.
  • This causes $user = UserFactory::new()->create(); to be inferred as \Illuminate\Database\Eloquent\Collection<int, \Illuminate\Database\Eloquent\Model>|\Illuminate\Database\Eloquent\Model instead of the expected \App\Models\User|\Illuminate\Database\Eloquent\Collection<int, \App\Models\User>, breaking autocomplete and diagnostics.

The conflict occurs because both factories are in the Database\Factories namespace and share the same class name. Fortify's workbench file extends \Orchestra\Testbench\Factories\UserFactory (for package testing), but it's unexpectedly included in the vendor index for end-user projects.

Steps To Reproduce

  1. Create a fresh Laravel project:

    composer create-project laravel/laravel example-app
    cd example-app
  2. Install Laravel Fortify to include the workbench directory:

    composer require laravel/fortify

    Verify that vendor/laravel/fortify/workbench/database/factories/UserFactory.php exists, containing:

    <?php
    namespace Database\Factories;
    use App\Models\User;
    /**
     * @template TModel of \App\Models\User
     * @extends \Illuminate\Database\Eloquent\Factories\Factory<TModel>
     */
    class UserFactory extends \Orchestra\Testbench\Factories\UserFactory
    {
        protected $model = User::class;
    }
  3. Generate the default application UserFactory:

    php artisan make:factory UserFactory

    Ensure database/factories/UserFactory.php has the standard structure:

    <?php
    namespace Database\Factories;
    use Illuminate\Database\Eloquent\Factories\Factory;
    /**
     * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
     */
    class UserFactory extends Factory
    {
        public function definition(): array
        {
            return [
                'name' => fake()->name(),
                'email' => fake()->unique()->safeEmail(),
                'email_verified_at' => now(),
                'password' => \Illuminate\Support\Facades\Hash::make('password'),
                'remember_token' => \Illuminate\Support\Str::random(10),
            ];
        }
    }
  4. Create a test file (e.g., tests/Feature/UserFactoryTest.php) with:

    <?php
    namespace Tests\Feature;
    use Database\Factories\UserFactory;
    use Illuminate\Foundation\Testing\RefreshDatabase;
    use Tests\TestCase;
    class UserFactoryTest extends TestCase
    {
        use RefreshDatabase;
        public function test_user_factory_creates_user(): void
        {
            $user = UserFactory::new()->create();
            $this->assertNotNull($user);
        }
    }
  5. Open the project in VS Code with the PHP Intelephense extension installed.

  6. Hover over UserFactory in the test file (line $user = UserFactory::new()->create();) or use "Go to Definition" to view the generated type information. Intelephense shows conflicting definitions:

    Database\Factories\UserFactory
    
    <?php
    class UserFactory extends Factory { }
    @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\User>
    
    Database\Factories\UserFactory
    
    <?php
    class UserFactory extends UserFactory { }
    @template TModel of \App\Models\User
    @extends \Illuminate\Database\Eloquent\Factories\Factory<TModel>
    
  7. Hover over $user in the test file. Intelephense infers the type as:

    \Illuminate\Database\Eloquent\Collection<int, \Illuminate\Database\Eloquent\Model>|\Illuminate\Database\Eloquent\Model
    

    Instead of the expected:

    \Illuminate\Database\Eloquent\Collection<int, \App\Models\User>|\App\Models\User
    

The issue occurs due to the namespace collision between the application’s UserFactory and Fortify’s workbench UserFactory, causing Intelephense to misparse the class and fall back to generic type inference.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions