Want to get more articles like this one? Join my newsletter

Using static factory methods with Laravel models

One limitation of the PHP language is the inability to create multiple constructors. In Java, for example, you can create different constructors as long as they have unique method signatures. This allows the programmer to create distinct ways to create the same object depending on the situation.

Because the PHP language lacks this feature, we solve this problem using different software design patterns. Most of the time, we just create a factory. The factory then deals with these different use-cases for creating an object.

One lesser known type of factory pattern is the static factory method pattern. This pattern allows us to address the multiple constructor problem. But it allows us to do so without needing to create a factory class which we might not need otherwise.

Now, I’ve written about how to use the static factory method pattern with WordPress before. But, as I’ve used Laravel more with client projects and building Ymir, I feel it’s worth revisiting it in that context as well. Specifically, I think it’s worth looking at how you can use it with Eloquent models.

Static factory methods are already in Eloquent

Eloquent has several ways of initializing new model objects. It has a constructor which you can use. But there are also the Model::create and Model::make methods that map to Eloquent Builder class. (There are also more use case specific methods such as Model::firstOrCreate or Model::firstOrNew.)

These methods are static factory methods themselves. The issue is that they’re very generic. They all take attributes as their parameter.

That’s because these methods will mass assign these attributes to the model when you create it. (Well, that’s unless the model has the attributes guarded.) If you don’t want to use mass assignment, you need to assign each attribute one at a time.

An excellent starting point

The great thing about these methods being generic is that we can use them as a starting point for our own methods. What we’ll do is create our own methods, which will then call either the create or make static methods. Let’s look at an example.

namespace App;
 
use Illuminate\Database\Eloquent\Model;

class BlogPost extends Model
{
    /**
     * Make a new BlogPost object from the given User object and save it to the database.
     */
    public static function createFromUser(User $author, string $title): self
    {
        return self::create([
            'user_id' => $author->id,
            'title' => $title,
        ]);
    }

    // ...
}

Above is a model called BlogPost. The class has a static factory method called createFromUser. The method has two parameters: author and title which we type hinted.

The ability to have type hints is another reason why creating custom static factory methods can be beneficial. It allows you to have increased type safety. This is something that I always value when writing code.

The rest of the static factory method just calls the generic Model::create method. We pass it an array with the attributes we want it to assign to the new BlogPost object. The array contains the ID of the user who authored the post and the title.

What about the make method?

For a lot of Laravel developers, the createFromUser method will be enough. But I tend to prefer to use the make method and delay saving the model until I’m done modifying it. So what I’d need is a makeFromUser static factory method.

Having both a createFromUser and makeFromUser method is actually quite easy to do. We can just do the same thing that Laravel does with create and make. If you look at the create and make methods in the Builder class, they look like this:

namespace Illuminate\Database\Eloquent;

class Builder
{
    // ...

    public function create(array $attributes = [])
    {
        return tap($this->newModelInstance($attributes), function ($instance) {
            $instance->save();
        });
    }

    // ...

    public function make(array $attributes = [])
    {
        return $this->newModelInstance($attributes);
    }

    // ...
}

Basically, the only difference between both methods is the use of the tap helper function to save the model we made. That’s it. So we can just do the same thing when building our createFromUser and makeFromUser methods.

namespace App;
 
use Illuminate\Database\Eloquent\Model;

class BlogPost extends Model
{
    /**
     * Make a new BlogPost object from the given User object and save it to the database.
     */
    public static function createFromUser(User $author, string $title): self
    {
        return tap(self::makeFromUser($author, $title))->save()
    }

    /**
     * Make a new BlogPost object from the given User object.
     */
    public static function makeFromUser(User $author, string $title): self
    {
        return self::make([
            'user_id' => $author->id,
            'title' => $title,
        ]);
    }

    // ...
}

Normally, the save method returns a boolean value. So we’d have to break everything into multiple lines of code. By using tap helper function, we can use the save method, but still return the new BlogPost post object.

Another example: Form Requests

Static factory methods are great with other Eloquent models. But they also synergize well with other Laravel features. One of those is form requests.

Form requests are a cool feature that let you create custom Request classes. These custom Request classes are convenient for storing all your validation code. But you can also use them to offer helper methods for interacting with an HTTP request.

<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class CreateBlogPostRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     */
    public function authorize(): bool
    {
        return true;
    }

    /**
     * Determine if the user is authorized to make this request.
     */
    public function getTitle(): string
    {
        return $this->get('title');
    }

    /**
     * Get the validation rules that apply to the request.
     */
    public function rules(): array
    {
        return [
            'title' => 'required|string',
        ];
    }
}

So above is the CreateBlogPostRequest class which we can use to create our BlogPost objects. It has three methods: authorize, getTitle and rules. The authorize method determines if the current user can process the request. We just return true which means any user can process this request.

Meanwhile, the getTitle method just returns the title value from the request. This is why the rules method, which returns the validation rules to use with the request, has validation rules for title. We need to make sure it’s always present and that it’s a string.

Creating the static factory method for the form request

Now, we can just update our BlogPost class. We’ll add a static factory method that takes a CreateBlogPostRequest object as an argument. You can see the modified class below.

namespace App;
 
use App\Http\Requests\CreateBlogPostRequest;
use Illuminate\Database\Eloquent\Model;

class BlogPost extends Model
{
    /**
     * Make a new BlogPost object from the given form request object and save it to the database.
     */
    public static function createFromRequest(CreateBlogPostRequest $request): self
    {
        return tap(self::makeFromRequest($request))->save()
    }

    /**
     * Make a new BlogPost object from the given form request object.
     */
    public static function makeFromRequest(CreateBlogPostRequest $request): self
    {
        return self::makeFromUser($request->user(), $request->getTitle());
    }

    // ...
}

We added two new static factory methods: makeFromRequest and createFromRequest. These two methods behave the same as our previous two static factory methods. The create method calls the make method and saves the model using the tap function.

The interesting thing is what we do in the makeFromRequest method. We use the returned values from the CreateBlogPostRequest methods and pass them to the makeFromUser method. This lets us leverage our code from the other static factory method while offering an alternative way to create a BlogPost object.

When should you use a traditional factory?

As you can see, static factory methods are often more than sufficient for your model creation needs. That said, there are cases where I’ve had to use a traditional factory class combined with static factory methods. A common one is if your model has a one-to-many relationship and you need to create both the parent and child models at the same time.

namespace App;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Database extends Model
{
    // ...

    public function users(): HasMany
    {
        return $this->hasMany(DatabaseUser::class, 'database_id');
    }

    // ...
}

To demonstrate this, let’s take the Database model shown above. It has a one-to-many relationship with the DatabaseUser model. When creating a Database object, you’ll want to at least add one DatabaseUser for the admin user.

Now, you could create a static factory method in the Database class that also creates the DatabaseUser object. But I feel like it defeats the purpose of the static factory method which is to be a custom constructor. This is really a situation where you’d want a factory class like this one.

namespace App\Factories;
 
use App\Database;
use App\DatabaseUser;

class DatabaseFactory
{
    public function create(string $name, string $username, string $password): Database
    {
        $database = Database::create([
            'name' => $name,
        ]);

        $database->users()->create([
            'username' => $username,
            'password' => $password,
        ]);

        return $database;
    }
}

So the DatabaseFactory class above has a single method called create. You pass it three arguments: name, username and password. We then use these three arguments to create our Database and DatabaseUser objects in the database.

The method starts by creating the Database object. We then call the users method which returns the HasMany relationship object. We then call its create method which will create the DatabaseUser and associate it with the Database object.

We finish by returning the created Database object. And like that, our create method created two objects in the database at once. This would have made less sense as a static factory method in the Database class.

Combining with static factory methods

That said, this doesn’t mean that you can’t use static factory methods with a factory class. Both factory patterns serve different purposes, so there’s nothing wrong with using them both at the same time.

namespace App\Factories;
 
use App\Database;
use App\DatabaseUser;

class DatabaseFactory
{
    public function create(string $name, string $username, string $password): Database
    {
        $database = Database::createFromName($name);

        DatabaseUser::createFromDatabase($database, $username, $password);

        return $database;
    }
}

Here’s the DatabaseFactory class updated to use static factory methods. We create the Database object using the createFromName method. We then pass it to the createFromDatabase static factory method to create the DatabaseUser object.

Bringing a bit more structure to your Eloquent models

So that wraps up our look at static factory methods. Like all software patterns, you don’t want to use them for every problem you encounter. But, with Eloquent, they’ve been an invaluable tool to bring some structure to creating model objects.

Creative Commons License