4

I'm trying to change my factories directory to a custom path, so I'm using this as I saw in a laracasts thread:

use Illuminate\Database\Eloquent\Factory as Factory;

class FactoryServiceProvider extends ServiceProvider
{
    public function register()
    {
            $this->app->singleton(Factory::class, function () {
                return Factory::construct(new Faker\Generator, app_path() .'/Core/Database/Factories');
            });
    }
}

The new path works, my factory files inside the new directory are loaded. But now when I try to use the factory from seeders on php artisan migrate:refresh --seed I'm getting

[InvalidArgumentException] Unknown formatter "name"

from the $faker instance inside the factory definition:

$factory->define(User::class, function (Faker\Generator $faker) {
    return[
        'name' => $faker->name,
        'email' => $faker->freeEmail,
        'password' => bcrypt($faker->word),
        'remember_token' => str_random(10)
    ];
});

This error appears with all the formatters, not just with name.

Where is the problem? The factory works fine before I change the path.

Gerard Reches
  • 3,048
  • 3
  • 28
  • 39

2 Answers2

10

I couldn't find answer for a while, so maybe this will help someone.

In your service provider, load additional path to your factories. This way Laravel not only looks for factories in default folder, but also in custom folder.

use Illuminate\Database\Eloquent\Factory;
...
  public function boot() {
    $this->registerEloquentFactoriesFrom(__DIR__ . '/../Database/Factories');
}


protected function registerEloquentFactoriesFrom($path) {
    $this->app->make(Factory::class)->load($path);
}

__DIR__ is path to to the directory you have your provider in. My directory structure looks like this.

src
 |    
 +-- Providers
 |  |  
 |  +-- CustomServiceProvider.php
 |    
 +-- Database
 |  |  
 |  +-- Factories

Of course different approach will also work for it.

Found on https://github.com/caffeinated/modules/issues/337

Mateusz
  • 968
  • 8
  • 9
  • Please add some explanation to your code - keep in mind that others should be able to learn from it – Nico Haase Mar 14 '19 at 21:11
  • This Answer is much better and more flexible than the Accepted Answer because it doesn't utilize a singleton (the use of which precludes using this technique in "modules" or "extensions"). Furthermore, the Accepted Answer is over-complicated in that it involves Faker, which, as evidenced in this Answer, isn't necessary at all. – Ben Johnson Jun 13 '19 at 17:32
  • you can do this in Laravel 8 now: https://laravel.com/docs/8.x/database-testing#connecting-factories-and-models – Thomas Sep 16 '21 at 11:27
1

Okay, finally I found how to make it work:

<?php

use Faker\Generator as FakerGenerator;
use Illuminate\Database\Eloquent\Factory as EloquentFactory;
use Illuminate\Support\ServiceProvider;

class FactoryServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->singleton(EloquentFactory::class, function ($app){
            $faker = $app->make(FakerGenerator::class);
            $factories_path = 'Your/Custom/Path/To/Factories';
            return EloquentFactory::construct($faker, $factories_path);
        });
    }
}

The app->make does the trick:

$app->make(FakerGenerator::class)

Gerard Reches
  • 3,048
  • 3
  • 28
  • 39