Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[9.x] Introduce a fake() helper to resolve faker singletons, per locale #42844

Merged
merged 2 commits into from
Jun 21, 2022

Conversation

timacdonald
Copy link
Member

@timacdonald timacdonald commented Jun 17, 2022

This PR introduces a global fake() helper to allow you to easily access a shared (singleton) faker instance across your application.

The helper is only created if the function doesn't already exist AND if Faker is installed, which it is only in "dev-dependencies" out of the box.

This is super useful in different scenarios.

  1. Prototyping
  2. Testing
  3. Factories / Seeding

Now some of these are already covered with their own implementations, but this improves the scenario across all.

First, this IMO is much nicer to look at...a little more "high end" some might say 🧐

return [
    'name' => fake()->name(),
];

When it comes to a Rapid Application Development framework like Laravel, speed of prototyping a feature is through the roof. But often you will want to prototype a view without having the even create any backing models or anything. You just wanna throw some tailwind and some blade at the problem and get some eyes on it.

@for($i = 0; $i < 10; $i++)
    <dl>
        <dt>Name</dt>
        <dd>{{ fake()->name() }}</dd>

        <dt>Phone</dt>
        <dd>{{ fake()->phoneNumber() }}</dd>

    </dl>
@endfor

You can also swap between languages, and you'll be guaranteed to always have a singleton for each locale...

fake()->name() // config('app.faker_locale') ?? 'en_US'
fake('en_AU')->name() // en_AU

This makes it nice when you want to ensure unique values across all the different things. You might want to use faker in your factories and in your seeder and be sure that you are getting unique values when you run them in the same thread.

// UserFactory...
function definition()
{
    return [
        'name' => fake()->unique()->name(),
    ];
}


// DB Seeder...

function run()
{
    DB::table('users')->insert(['name' => fake()->unique()->name()]);
}

As with all the helpers, don't use them if you don't like them 👍

PRs suggesting a faker helper have been proposed previously, but hoping with a few more examples of its utility, this might be reconsidered.

This won't conflict with any of the existing framework faker bindings (see DatabaseServiceProvider) as the key is the container is suffixed with a colon and then the locale, .e.g \Faker\Factory:en_US and it is only bound at runtime, not during boot, so if you do not call this function nothing changes.

Previous PRs:

@timacdonald timacdonald changed the title introduce a fake() helper to resolve a faker singletons [9.x] introduce a fake() helper to resolve a faker singletons Jun 17, 2022
@timacdonald timacdonald changed the title [9.x] introduce a fake() helper to resolve a faker singletons [9.x] introduce a fake() helper to resolve faker singletons, per locale Jun 17, 2022
@bert-w
Copy link
Contributor

bert-w commented Jun 17, 2022

pretty handy but I prefer the use of faker() over fake(), since the latter indicates something like a mock-object to me (or some no-op function).

@DarkGhostHunter
Copy link
Contributor

DarkGhostHunter commented Jun 17, 2022

Let me introduce you to Faker memory leaks. Now available globally.

@phanan
Copy link
Contributor

phanan commented Jun 17, 2022

Not sure if a Faker instance, which should only be used for testing purposes, should be available at global scope in all environments this way. I'd tend to agree with Taylor in one of the linked PR, if you need it, just implement it in the userland.

@taylorotwell
Copy link
Member

@DarkGhostHunter can you elaborate?

@OwenMelbz
Copy link
Contributor

Not sure if a Faker instance, which should only be used for testing purposes, should be available at global scope in all environments this way. I'd tend to agree with Taylor in one of the linked PR, if you need it, just implement it in the userland.

Feel free to correct me if I'm wrong, but you've said "in all environments" but the PR defaults to only environments using dev-dependencies rather than all environments?

@DarkGhostHunter
Copy link
Contributor

DarkGhostHunter commented Jun 18, 2022 via email

@timacdonald
Copy link
Member Author

@DarkGhostHunter that issue exists regardless of this PR though. The faker instance utilised in database factories is already a container bound singleton.

@DarkGhostHunter
Copy link
Contributor

@DarkGhostHunter that issue exists regardless of this PR though. The faker instance utilised in database factories is already a container bound singleton.

Then I'll have to rant there.

@timacdonald
Copy link
Member Author

I’m always here for a good rant. I do them often myself haha ☺️

@bert-w
Copy link
Contributor

bert-w commented Jun 19, 2022

More often than not I tend to have memory exhaustion problems when I use some of faker texts generators on tests or seeding. Maybe it’s a problem on PHP itself. Try ‘realText()’ 10.000 times and you will hit the roof. Italo Baeza C.

El 18-06-2022, a la(s) 01:47, Taylor Otwell @.***> escribió:  @DarkGhostHunter can you elaborate? — Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you were mentioned.

Off-topic though but I tested some stuff out:

$faker = \Faker\Factory::create();
$start = memory_get_usage();
for ($i = 0; $i <= 10000; $i++) {
    $faker->realText;
}
var_dump(memory_get_usage() - $start);
die('done');

Output (in bytes):

int(80347016) done

Now call the same thing using $faker->realText() (so a function call vs __get() overload):

int(10741104) done

Idk if that resolves your issue but it appears that calling properties does indeed increase the memory output considerably, also if you increase the length of the loop. It remains mostly static if you use the function call, regardless of loop length.

However I noticed that the Faker code says:

trigger_deprecation('fakerphp/faker', '1.14', 'Accessing property "%s" is deprecated, use "%s()" instead.', $attribute, $attribute);

So it's not recommended to call it using a property anyway.

@DarkGhostHunter
Copy link
Contributor

DarkGhostHunter commented Jun 19, 2022 via email

@taylorotwell taylorotwell merged commit 7746337 into laravel:9.x Jun 21, 2022
@GrahamCampbell GrahamCampbell changed the title [9.x] introduce a fake() helper to resolve faker singletons, per locale [9.x] Introduce a fake() helper to resolve faker singletons, per locale Jun 21, 2022
@timacdonald timacdonald deleted the fake branch June 24, 2022 05:35
@matiaslauriti
Copy link
Contributor

I know I am not (shamely) a member nor a contributor to the Laravel framework (I will one day!) but after working at least 5 years with Laravel, through out a lot of developers (seniorities), I just found this addition to be very bad:

  1. Why fake and not faker? Looks very confusing when we are all used to faker
  2. I saw lot of ugly and really bad bad BAD code, so allowing someone to use a faker helper globally is really a BAD idea

I do know it is limited to having Faker installed locally in your project (required-dev), but that will allow anyone to do use fake() anywhere and then (if no one catched that) will fail on production because we have the faker installed check on the helper.

As any helper/tool, it can be a double edge sword, you must know what you are doing (most of the time...) but specially helping new people using Laravel + PHP or even joining the development world...

One more "rant", if we want to use it on seeders (as the author mentioned), then we cannot truly do so, as you do not have Faker on production but if you are using a seeder in there you are done...

What I just want to say is that there is no possible test to catch that you are using fake() outside a testing environment, because you would need to remove/delete Faker, but that would prevent tests from running/passing...

That looks like a code smell or similar... Sorry for the rant, but I truly do not believe this adds value at all, specially for new developers (most people using Laravel)...

Still, you are already a member, so thanks for any possible additions you can do to the framework ❤️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants