Laravel-modules: Factory issues in Laravel 8

Created on 11 Oct 2020  Â·  8Comments  Â·  Source: nWidart/laravel-modules

Hey there! I might be wrong but I think the factory stub should be updated with the new class format introduced in Laravel 8.0?

If this is correct, I will gladly submit a PR.

Most helpful comment

Yeah, that happens because this is how Laravel resolves the factory namespace by default:

    /**
     * Get the factory name for the given model name.
     *
     * @param  string  $modelName
     * @return string
     */
    public static function resolveFactoryName(string $modelName)
    {
        $resolver = static::$factoryNameResolver ?: function (string $modelName) {
            $modelName = Str::startsWith($modelName, 'App\\Models\\')
                ? Str::after($modelName, 'App\\Models\\')
                : Str::after($modelName, 'App\\');

            return static::$namespace.$modelName.'Factory';
        };

        return $resolver($modelName);
    }

So, in order to support the Modules\{Module} namespace you need to put this in your model:

    /**
     * Create a new factory instance for the model.
     *
     * @return \Illuminate\Database\Eloquent\Factories\Factory
     */
    protected static function newFactory()
    {
        return \Modules\Module\Database\Factories\ModelFactory::new();
    }

However, if this package doesn't supply this by default (which currently doesn't), we need to add that in every model that has a factory, which sounds like an annoying boilerplate.

All 8 comments

On another hand, there is also an issue related with the new factory changes. The models should implement the HasFactory trait and then Laravel will try to find the factory magically, based on the App namespace.

Is it good practice to override that magic method and add support for modules namespace? Or are we forced to use the newFactory method (fallback) on every model? Sounds kinda counter intuitive.

I can confirm that Factory has issues on the current 8.0.0 build, even with both the classic definition and the new class format introduced in Laravel 8, seeding a module does not work anymore.

the new format for seeding an entity :

\App\Models\User::factory(10)->create();

Adding the HasFactory trait to a module entity and trying to seed with the global DatabaseSeeder

class Author extends Model {
    use HasFactory;
    ...
}
class DatabaseSeeder extends Seeder
{
    /**
     * Seed the application's database.
     *
     * @return void
     */
    public function run()
    {
        \Modules\Author\Entities::factory(10)->create();
    }
}

Running php artisan db:seed Trace

  Class 'Database\Factories\Modules\Author\Entities\AuthorFactory' not found

  at \vendor\laravel\framework\src\Illuminate\Database\Eloquent\Factories\Factory.php:656
    652â–•     public static function factoryForModel(string $modelName)
    653â–•     {
    654â–•         $factory = static::resolveFactoryName($modelName);
    655â–•
  ➜ 656▕         return $factory::new();
    657â–•     }
    658â–•
    659â–•     /**
    660â–•      * Specify the callback that should be invoked to guess factory names based on dynamic relationship names.

  1   \vendor\laravel\framework\src\Illuminate\Database\Eloquent\Factories\HasFactory.php:15
      Illuminate\Database\Eloquent\Factories\Factory::factoryForModel()

  2   \database\seeders\DatabaseSeeder.php:17
      Modules\Manager\Entities\Manager::factory()

Yeah, that happens because this is how Laravel resolves the factory namespace by default:

    /**
     * Get the factory name for the given model name.
     *
     * @param  string  $modelName
     * @return string
     */
    public static function resolveFactoryName(string $modelName)
    {
        $resolver = static::$factoryNameResolver ?: function (string $modelName) {
            $modelName = Str::startsWith($modelName, 'App\\Models\\')
                ? Str::after($modelName, 'App\\Models\\')
                : Str::after($modelName, 'App\\');

            return static::$namespace.$modelName.'Factory';
        };

        return $resolver($modelName);
    }

So, in order to support the Modules\{Module} namespace you need to put this in your model:

    /**
     * Create a new factory instance for the model.
     *
     * @return \Illuminate\Database\Eloquent\Factories\Factory
     */
    protected static function newFactory()
    {
        return \Modules\Module\Database\Factories\ModelFactory::new();
    }

However, if this package doesn't supply this by default (which currently doesn't), we need to add that in every model that has a factory, which sounds like an annoying boilerplate.

It could indeed be a could idea to add this to the generated model classes by default.

I added the corresponding method to my model Tenant, which also contains the HasFactory namespace:

use HasFactory;
// ...
protected static function newFactory()
{
    return TenantFactory::new();
}

And inside the factory the model is referenced too. But I still receive errors like this when running my tests:

InvalidArgumentException: Unable to locate factory for [Modules\Tenant\Entities\Tenant].

I'm using laravel-modules v 8.2.0 with laravel framework 8.34.0

I added the corresponding method to my model Tenant, which also contains the HasFactory namespace:

use HasFactory;
// ...
protected static function newFactory()
{
    return TenantFactory::new();
}

And inside the factory the model is referenced too. But I still receive errors like this when running my tests:

InvalidArgumentException: Unable to locate factory for [Modules\Tenant\Entities\Tenant].

I'm using laravel-modules v 8.2.0 with laravel framework 8.34.0

Make sure you import your TenantFactory class in your Model, and your Model in your TenantFactory

I added the corresponding method to my model Tenant, which also contains the HasFactory namespace:

use HasFactory;
// ...
protected static function newFactory()
{
    return TenantFactory::new();
}

And inside the factory the model is referenced too. But I still receive errors like this when running my tests:
InvalidArgumentException: Unable to locate factory for [Modules\Tenant\Entities\Tenant].
I'm using laravel-modules v 8.2.0 with laravel framework 8.34.0

Make sure you import your TenantFactory class in your Model, and your Model in your TenantFactory

Did that. Unfortunately this is not the mistake :/

Was this page helpful?
0 / 5 - 0 ratings

Related issues

uncodable picture uncodable  Â·  21Comments

KyawNaingTun picture KyawNaingTun  Â·  16Comments

jakecorn picture jakecorn  Â·  16Comments

n4p4 picture n4p4  Â·  17Comments

githubmarau picture githubmarau  Â·  29Comments