Há alguns dias, eu estava corrigindo um teste instável e descobri que precisava de alguns valores e em minha fábrica. O Laravel envolve o FakerPHP, que normalmente acessamos através do auxiliar . O FakerPHP vem com modificadores como e , mas você pode usar apenas um de cada vez, então você não pode fazer , que é exatamente o que eu precisava. exclusivos válidos fake() valid() unique() fake()->unique()->valid() Isso me fez pensar: e se quisermos criar nosso próprio modificador? Por exemplo, ou qualquer outro modificador. Como podemos a estrutura? uniqueAndValid() estender Pensando alto Estarei abandonando minha linha de pensamento. Antes de saltar para qualquer solução excessivamente projetada, sempre quero verificar se existe uma opção mais simples e entender com o que estou lidando. Então, vamos dar uma olhada no auxiliar : fake() 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); } Lendo o código, podemos ver que o Laravel vincula um singleton ao contêiner. Porém, se inspecionarmos o abstrato, é uma classe regular que não implementa nenhuma interface, e o objeto é criado por meio de uma fábrica. Isso complica as coisas. Por que? Porque se fosse uma interface, poderíamos simplesmente criar uma nova classe que estendesse a classe base, adicionar alguns novos recursos e revinculá-la ao contêiner. Mas não temos esse luxo. \Faker\Generator Há uma fábrica envolvida. Isso significa que não é uma simples instanciação; há alguma lógica sendo executada. Neste caso, a fábrica adiciona alguns provedores (PhoneNumber, Text, UserAgent, etc.). Portanto, mesmo que tentemos religar, teremos que usar a fábrica, que retornará o original. \Faker\Generator Soluções 🤔? Alguém poderia pensar: “O que nos impede de criar nossa própria fábrica que devolva o novo gerador conforme descrito no ponto 1?” Bem, nada, podemos fazer isso, mas não faremos! Usamos uma estrutura por vários motivos, um deles sendo atualizações. O que acontecerá se o FakerPHP adicionar um novo provedor ou fizer uma grande atualização? O Laravel ajustará o código e as pessoas que não fizeram nenhuma alteração não notarão nada. No entanto, ficaríamos de fora e nosso código poderia até quebrar (provavelmente). Então, sim, não queremos ir tão longe. Então, o que fazemos? Agora que exploramos as opções básicas, podemos começar a pensar nas mais avançadas, como padrões de design. Não precisamos de uma implementação exata, apenas de algo familiar ao nosso problema. É por isso que sempre digo que é bom conhecê-los. Neste caso, podemos “decorar” a classe adicionando novos recursos enquanto mantemos os antigos. Parece bom? Vamos ver como! Generator Primeiro, vamos criar uma nova classe, : 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); } } Este será o nosso “decorador” (mais ou menos). É uma classe simples que espera o base como uma dependência e introduz um novo modificador, . Ele também usa o atributo do Laravel, que permite fazer proxy de chamadas para o objeto base. Generator uniqueAndValid() ForwardsCalls Essa característica possui dois métodos: e . Use o último quando quiser encadear métodos no objeto decorado. No nosso caso, teremos sempre uma única chamada. forwardCallTo forwardDecoratedCallTo Também precisamos implementar , que é o modificador personalizado, mas esse não é o objetivo do artigo. Se você estiver interessado na implementação, esta classe é basicamente uma mistura do e que vem com o FakerPHP, você pode encontrá-la . UniqueAndValidGenerator ValidGenerator UniqueGenerator aqui Agora, vamos o framework, no : estender 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'); } } O método verifica se um resumo correspondente ao nome fornecido foi vinculado ao contêiner. Se sim, ele substitui seu valor pelo resultado do fechamento, veja: extend() // 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); } } } É por isso que definimos o método , que gera o mesmo nome que o auxiliar se liga no contêiner. fakerAbstractName() fake() Verifique novamente o código acima se você perdeu, deixei um comentário. Agora, toda vez que chamarmos , uma instância de será retornada e teremos acesso ao modificador personalizado que introduzimos. Cada vez que invocamos uma chamada que não existe na classe , será acionado e fará proxy para o base usando o método . fake() FakerGenerator FakerGenerator __call() Generator forwardCallTo() É isso! Finalmente posso fazer , e funciona perfeitamente! fake()->uniqueAndValid()->randomElement() Antes de concluirmos, quero ressaltar que este não é um padrão puramente decorador. Contudo, os padrões não são textos sagrados; ajuste-os para atender às suas necessidades e resolver o problema. Conclusão Frameworks são incrivelmente úteis e o Laravel vem com muitos recursos integrados. No entanto, eles não podem cobrir todos os casos extremos em seus projetos e, às vezes, você pode chegar a um beco sem saída. Quando isso acontecer, você sempre poderá estender a estrutura. Vimos como é simples e espero que você tenha entendido a ideia principal, que se aplica além deste exemplo do Faker. Comece sempre de forma simples e procure a solução mais simples para o problema. A complexidade virá quando for necessária, portanto, se a herança básica funcionar, não há necessidade de implementar um decorador ou qualquer outra coisa. Ao ampliar a estrutura, certifique-se de não ir longe demais, onde a perda supera o ganho. Você não quer acabar mantendo uma parte da estrutura sozinho.