BillionaireClubCollc
  • News
  • Notifications
  • Shop
  • Cart
  • Media
  • Advertise with Us
  • Profile
  • Groups
  • Games
  • My Story
  • Chat
  • Contact Us
home shop notifications more
Signin
  •  Profile
  •  Sign Out
Skip to content

Billionaire Club Co LLC

Believe It and You Will Achieve It

Primary Menu
  • Home
  • Politics
  • TSR
  • Anime
  • Michael Jordan vs.Lebron James
  • Crypto
  • Soccer
  • Dating
  • Airplanes
  • Forex
  • Tax
  • New Movies Coming Soon
  • Games
  • CRYPTO INSURANCE
  • Sport
  • MEMES
  • K-POP
  • AI
  • The Bahamas
  • Digital NoMad
  • Joke of the Day
  • RapVerse
  • Stocks
  • SPORTS BETTING
  • Glamour
  • Beauty
  • Travel
  • Celebrity Net Worth
  • TMZ
  • Lotto
  • COVD-19
  • Fitness
  • The Bible is REAL
  • OutDoor Activity
  • Lifestyle
  • Culture
  • Boxing
  • Food
  • LGBTQ
  • Poetry
  • Music
  • Misc
  • Open Source
  • NASA
  • Science
  • Natural & Holstict Med
  • Gardening
  • DYI
  • History
  • Art
  • Education
  • Pets
  • Aliens
  • Astrology
  • Farming and LiveStock
  • LAW
  • Fast & Furious
  • Fishing & Hunting
  • Health
  • Credit Repair
  • Grants
  • All things legal
  • Reality TV
  • Africa Today
  • China Today
  • "DUMB SHIT.."
  • CRYPTO INSURANCE

Laravel Under The Hood - How to Extend the Framework

A few days ago, I was fixing a flaky test, and it turned out I needed some unique and valid values within my factory. Laravel wraps FakerPHP, which we usually access through the fake() helper. FakerPHP comes with modifiers like valid() and unique(), but you can use only one at a time, so you can't do fake()->unique()->valid(), which is exactly what I needed.
\
This made me think, what if we want to create our own modifier? For example, uniqueAndValid() or any other modifier. How can we extend the framework?
Thinking Out Loud

I will be dumping my train of thought.

\
Before jumping into any over-engineered solution, I always want to check if there is a simpler option and understand what I'm dealing with. So, let's take a look at the fake() helper:
function fake($locale = null)
{
if (app()->bound('config')) {
$locale ??= app('config')->get('app.faker_locale');
}

$locale ??= 'en_US';

$abstract = \Faker\Generator::class.':'.$locale;

if (! app()->bound($abstract)) {
app()->singleton($abstract, fn () => \Faker\Factory::create($locale));
}

return app()->make($abstract);
}

Reading the code, we can see that Laravel binds a singleton to the container. However, if we inspect the abstract, it's a regular class that does not implement any interface, and the object is created via a factory. This complicates things. Why?
\

Because if it were an interface, we could just create a new class that extends the base \Faker\Generator class, add some new features, and rebind it to the container. But we don't have this luxury.
\
There is a factory involved. This means it's not a simple instantiation; there is some logic being run. In this case, the factory adds some providers (PhoneNumber, Text, UserAgent, etc.). So, even if we try to rebind, we will have to use the factory, which will return the original \Faker\Generator.

\
Solutions 🤔? One might think, "What's stopping us from creating our own factory that returns the new generator as outlined in point 1?" Well, nothing, we can do that, but we won't! We use a framework for several reasons, one of them being updates. What will happen if FakerPHP adds a new provider or has a major upgrade? Laravel will adjust the code, and people who haven't made any changes won't notice anything. However, we would be left out, and our code might even break (most likely). So, yes, we don't want to go that far.
So, What Do We Do?
Now that we've explored the basic options, we can start thinking of more advanced ones, like design patterns. We don't need an exact implementation, just something familiar to our problem. This is why I always say it's good to know them. In this case, we can "decorate" the Generator class by adding new features while maintaining the old ones. Sounds good? Let's see how!
\
First, let's create a new class, FakerGenerator:
<?php

namespace App\Support;

use Closure;
use Faker\Generator;
use Illuminate\Support\Traits\ForwardsCalls;

class FakerGenerator
{
use ForwardsCalls;

public function __construct(private readonly Generator $generator)
{
}

public function uniqueAndValid(Closure $validator = null): UniqueAndValidGenerator
{
return new UniqueAndValidGenerator($this->generator, $validator);
}

public function __call($method, $parameters): mixed
{
return $this->forwardCallTo($this->generator, $method, $parameters);
}
}

This will be our "decorator" (kinda). It is a simple class that expects the base Generator as a dependency and introduces a new modifier, uniqueAndValid(). It also uses the ForwardsCalls trait from Laravel, which allows it to proxy calls to the base object.
\

This trait has two methods: forwardCallTo and forwardDecoratedCallTo. Use the latter when you want to chain methods on the decorated object. In our case, we will always have a single call.

\
We also need to implement the UniqueAndValidGenerator, which is the custom modifier, but this is not the point of the article. If you are interested in the implementation, this class is basically a mixture of the ValidGenerator and UniqueGenerator that ship with FakerPHP, you can find it here.
\
Now, let's extend the framework, in the AppServiceProvider:
<?php

namespace App\Providers;

use Closure;
use Faker\Generator;
use App\Support\FakerGenerator;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
public function register(): void
{
$this->app->extend(
$this->fakerAbstractName(),
fn (Generator $base) => new FakerGenerator($base)
);
}

private function fakerAbstractName(): string
{
// This is important, it matches the name bound by the fake() helper
return Generator::class . ':' . app('config')->get('app.faker_locale');
}
}

\
The extend() method checks if an abstract matching the given name has been bound to the container. If so, it overrides its value with the result of the closure, take a look:
// Laravel: src/Illuminate/Container/Container.php

public function extend($abstract, Closure $closure)
{
$abstract = $this->getAlias($abstract);

if (isset($this->instances[$abstract])) {
// You are interested here
$this->instances[$abstract] = $closure($this->instances[$abstract], $this);

$this->rebound($abstract);
} else {
$this->extenders[$abstract][] = $closure;

if ($this->resolved($abstract)) {
$this->rebound($abstract);
}
}
}

That's why we defined the fakerAbstractName() method, which generates the same name as the fake() helper binds in the container.
\

Recheck the code above if you missed it, I left a comment.

\
Now, every time we call fake(), an instance of FakerGenerator will be returned, and we will have access to the custom modifier we introduced. Every time we invoke a call that does not exist on the FakerGenerator class, __call() will be triggered, and it will proxy it to the base Generator using the forwardCallTo() method.
\
That's it! I can finally do fake()->uniqueAndValid()->randomElement(), and it works like a charm!
\

Before we conclude, I want to point out that this is not a pure decorator pattern. However, patterns are not sacred texts; tweak them to fit your needs and solve the problem.

\
Conclusion
Frameworks are incredibly helpful, and Laravel comes with a lot of built-in features. However, they can't cover all the edge cases in your projects, and sometimes, you might hit a dead end. When that happens, you can always extend the framework. We've seen how simple it is, and I hope you understood the main idea, which applies beyond just this Faker example.
\
Always start simple and look for the simplest solution to the problem. Complexity will come when it's necessary, so if basic inheritance does the trick, there's no need to implement a decorator or anything else. When you do extend the framework, ensure you don't go too far, where the loss outweighs the gain. You don't want to end up maintaining a part of the framework on your own.
\

Welcome to Billionaire Club Co LLC, your gateway to a brand-new social media experience! Sign up today and dive into over 10,000 fresh daily articles and videos curated just for your enjoyment. Enjoy the ad free experience, unlimited content interactions, and get that coveted blue check verification—all for just $1 a month!

Source link

Share
What's your thought on the article, write a comment
0 Comments
×

Sign In to perform this Activity

Sign in
×

Account Frozen

Your account is frozen. You can still view content but cannot interact with it.

Please go to your settings to update your account status.

Open Profile Settings

Ads

  • Billionaire128 Liquid Gold Bean Bag Chair COVER

    $ 70.00
  • Premium Billionaire128 Crop Hoodie

    $ 44.00
  • Billionaire128 Liquid Gold Flip-Flops

    $ 18.00
  • News Social

    • Facebook
    • Twitter
    • Facebook
    • Twitter
    Copyright © 2024 Billionaire Club Co LLC. All rights reserved