I was looking at a controller for a client’s project the other day, and it was a mess. They have a `Subscription` model, and you could create a new subscription from three different places: a user signing up on the front end, an admin creating one in the back end, and an incoming webhook from Stripe. The controller method was a tangle of conditionals trying to figure out where the data was coming from. It was tough to read and a nightmare to test. This is a classic sign that you need to rethink how you’re creating your objects, and it’s a perfect use case for Laravel static factory methods.
PHP has this limitation where you can’t have multiple constructors. In other languages, you could just create a few different constructors with unique method signatures to handle each case. In PHP, we have to use design patterns to solve this. Usually, that means a factory. But you don’t always need a whole separate factory class for this kind of problem.
The Problem With the “Default” Way
Now, Eloquent already gives you static factory methods right out of the box: Model::create() and Model::make(). They’re great, but they’re very generic. They take a big, untyped array of attributes. My first thought on that client project was just to build those arrays in the controller and pass them to Subscription::create(). And yeah, it worked, but it felt sloppy. You lose all type-safety, and it’s not clear what data is actually required to create the object. It’s just a blob of data. Total mess waiting to happen if someone changes a key name.
Here’s the kicker: when your object creation logic is even slightly complex, the basic ::create() method hides that complexity. It makes your controllers or services responsible for knowing exactly how to build the attribute array. That’s a leaky abstraction, and it leads to duplicated code and bugs. I got the original idea to refine this from a post over at carlalexander.ca, which breaks down the core pattern well.
Using Laravel Static Factory Methods for Clarity
Instead of passing a generic array, we can create our own static factory methods on the model itself. This lets us create highly specific, self-documenting ways to build our objects. Trust me on this, it makes the code a hundred times cleaner.
Let’s look at that Subscription model. We can add a few methods to it:
<?php
namespace App;
use App\Http\Requests\CreateSubscriptionRequest;
use Illuminate\Database\Eloquent\Model;
class Subscription extends Model
{
/**
* Create a new Subscription from a user sign-up.
*/
public static function createFromSignUp(User $user, Plan $plan): self
{
return self::create([
'user_id' => $user->id,
'plan_id' => $plan->id,
'status' => 'pending_payment',
]);
}
/**
* Create a new Subscription from a Stripe webhook.
*/
public static function createFromStripeWebhook(array $webhookPayload): self
{
$plan = Plan::where('stripe_id', $webhookPayload['plan_id'])->firstOrFail();
$user = User::where('stripe_customer_id', $webhookPayload['customer_id'])->firstOrFail();
return self::create([
'user_id' => $user->id,
'plan_id' => $plan->id,
'status' => 'active',
]);
}
}See what’s happening here? The logic for how to create a subscription from different contexts now lives with the model itself. The controller code becomes dead simple. Instead of a mess of conditionals, it just calls the right method: Subscription::createFromSignUp($user, $plan). It’s clean, it’s type-hinted, and it’s easy to understand what’s going on.
So, What’s the Point?
Using static factory methods isn’t some esoteric pattern. It’s a practical way to clean up your code and make it more robust. For Eloquent models, it’s a natural fit.
- It cleans up your controllers. All that messy object setup logic gets moved into a dedicated method on the model.
- It’s self-documenting. The method name tells you exactly what it does and what it needs.
createFromSignUpis way clearer thancreate([...]). - It improves type safety. You can (and should) type-hint the arguments, which catches a whole class of bugs before they happen.
Of course, if you find yourself needing to create a bunch of related objects at once—like a `Database`, a `DatabaseUser`, and maybe setting up credentials all at the same time—then it’s probably time to reach for a dedicated Factory Class. But for creating a single model from various inputs, static factory methods are the cleanest way to do it.
Look, this stuff gets complicated fast. If you’re tired of debugging someone else’s mess and just want your site to work, drop my team a line. We’ve probably seen it before.
Leave a Reply