Get "PHP 8 in a Nuthshell" (Now with PHP 8.4)
Amit Merchant

Amit Merchant

A blog on PHP, JavaScript, and more

Using Generics in Laravel Eloquent

Generics in programming are a feature that allows you to define functions, classes, and data structures with placeholder types. This enables you to write more flexible and reusable code.

Generics are commonly used in statically typed languages like Java, C#, and C++. Dynamic languages like Python also support it.

Why generics?

Using generics can have multiple benefits:

  • Type Safety: Generics ensure that the relationships return the correct type of models, reducing the risk of runtime errors due to incorrect model types.
  • Better IDE Support: IDEs can provide better autocompletion and type checking when generics are used, making development easier and reducing the likelihood of errors.
  • Improved Readability: The use of generics makes the code more self-documenting. It is clear from the method signatures what type of relationships and models are involved.

Here’s what a typical generic function in Java looks like.

public <T> T getFirstElement(List<T> list) {
    return list.get(0);
}

In this example, the getFirstElement function takes a List of type T and returns an element of type T. The T is a placeholder type that can be replaced with any other type when the function is called.

Using Generics in Laravel

When it comes to PHP, generics still seems to be a far-fetched dream but that doesn’t stop us from using them thanks to tools like PHPStan or PSalm which lets you add static type checking to your PHP code.

We can define generics as comments or annotations to provide type hints to the IDE and tools like PHPStan can use them to perform static analysis.

In the recent Laravel release, a community member has added the support to use generics in Eloquent models. The role of generics here is to add the ability to specify the types that a class or method will work with.

Generics provide a mechanism for type hinting and better code readability, especially when dealing with relationships in Eloquent models.

Take the following for example.

class User extends Model
{
    /** @return HasOne<Address, $this> */
    public function address(): HasOne
    {
        return $this->hasOne(Address::class);
    }

    /** @return HasMany<Post, $this> */
    public function posts(): HasMany
    {
        return $this->hasMany(Post::class);
    }
}

Explanation

As you can tell, we can now specify the return types of the relationships but on top of that, we can also specify the type of the model that the relationship is related to.

  • HasOne<Address, $this>: This indicates that the address method returns a HasOne relationship where the related model is Address and the parent model is the current User instance.

  • HasMany<Post, $this>: This indicates that the posts method returns a HasMany relationship where the related model is Post and the parent model is the current User instance.

The same goes for other methods like belongsTo, hasManyThrough, belongsToMany, etc.

Now, if there are inconsistencies in the defined types, Larastan (a PHPStan wrapper for Laravel), for instance, will throw a detailed error message that points out the issue.

Take the following for example.

/** @return HasMany<Comment, $this> */
public function posts(): HasMany
{
    return $this->hasMany(Post::class);
}

In this example, the posts method specifies a generic type HasMany<Comment, $this>, but it returns a relationship with Post::class.

Running Larastan (using the command ./vendor/bin/phpstan analyse) will produce the following error message.

  Line   app/Models/User.php
 ------ ----------------------------------------------------------------------------
  10     Method App\Models\User::posts() should return 
         App\Models\Relations\HasMany<App\Models\Comment, App\Models\User> 
         but returns App\Models\Relations\HasMany<App\Models\Post, App\Models\User>.
 ------ ----------------------------------------------------------------------------

 [ERROR] Found 1 error

That’s how generics can help us write more robust and maintainable code in Laravel and save us from ugly runtime errors.

Learn the fundamentals of PHP 8 (including 8.1, 8.2, and 8.3), the latest version of PHP, and how to use it today with my new book PHP 8 in a Nutshell. It's a no-fluff and easy-to-digest guide to the latest features and nitty-gritty details of PHP 8. So, if you're looking for a quick and easy way to PHP 8, this is the book for you.

Like this article?

Buy me a coffee

👋 Hi there! I'm Amit. I write articles about all things web development. You can become a sponsor on my blog to help me continue my writing journey and get your brand in front of thousands of eyes.

Comments?