Created with Sketch.

I've got a weird fascination with using the identical operator (===) for comparisons so I am always casting all my foreign keys to integers...and that gets old real quick, so added this to my base model. Also saves me casting in my api transformers!

This trick saves me having to cast all my foreign keys so they are always cast, using Eloquent’s built in casting mechanisms, to an integer. I attempted to put this in a trait, but had little luck as I couldn’t hook into a good event that would ensure the casts are available even when ‘newing’ up an object.

Doing this allows me to do strict comparisons against foreign keys and integers, it also saves me having to cast to integers in my api transformers. Of course I could manually add my foreign keys to the casts attribute…but I’m busy that day 😎

But really, I just feel that the snozberries should taste like snozberries, and that my integers should be integers. I love Laravel because it has taught me to care about how my code feels and it doesn’t feel right to me to see my values as strings when I’m expecting an integer.

Casting in Eloquent Models

Laravel offers a really simple way to cast your attributes to different value types. The reason you may want to cast your values is that when they are retrieved and added to the model, they are simply stored as strings. Casting allows us to say “Hey Laravel, be a good friend and make is_active a boolean for me - cheers”.

To setup a your casts on your model you just add to the $casts property array. Use the attribute name as the key and the type to cast to as the value. Let’s check it out:

class User extends Eloquent
{
    protected $casts = [
        'is_active' => 'boolean'
    ];
}

Check out the available casting types.

Laravels Foreign Key Convention

Laravel’s foreign key convention is simply postfixing the database column name with _id, for example if we are working with the classic User - Post relationship, on our posts table we would create a column called user_id. By no means does Laravel enforce this convention, it just makes it easier for you if you follow it as you don’t need to override it when setting up eloquent relations in your models.

Creating Your Base Model

You will want to create a base class to inherit from Eloquent and then all your models should inherit from your base class. I personally put my base models in my /app/Models folder, like so:

<?php

namespace App\Models;

class Eloquent extends Model
{
    //
}

so now in your model classes, you extend your new eloquent base model, ensuring that all the new functionality that we add is inherited everywhere, like so:

<?php

namespace App;

use App\Models\Eloquent;

class Post extends Eloquent
{
    // your class definition...
}

Great, you can now add some goodness to your base class to use across all your models.

Settings For Foreign Key Casting

The first thing we need to do is setup some values. I use constants for the values that don’t change during runtime, but if you need to change anything on the fly, you might consider using properties instead.

class Eloquent extends Model
{
    const FOREIGN_KEY_CAST_TYPE = 'integer';

    const FOREIGN_KEY_CASTING_EXCEPTIONS = [];

    const FOREIGN_KEY_REGEX = '/.+_id$/';

    protected $foreignKeyCastsAdded = false;
}

Okay, so I think these are self explanatory, but hey, if I don’t talk about it all, I might as well just post the code right?!?

FOREIGN_KEY_CAST_TYPE: is the type of cast that is going to occur. I want all my foreign keys to be cast to integers.

FOREIGN_KEY_CASTING_EXCEPTIONS: if there are any attributes that match the foreign key RegEx but you don’t want to automatically include, you can add them to this array.

FOREIGN_KEY_REGEX: allows you to change the RegEx used to detect if an attribute is a foreign key. This RegEx basically says find any string that ends with _id, but you can adjust to suit your needs if you use another convention.

$foreignKeyCastsAdded: is just a flag that we will use to determine if we have added our casts already so we don’t need to do it repeatedly.

Making It Happen

So now we need to simply add the foreign key attributes to the $casts attribute provided by Eloquent. To do this, we are going to hook into the getCasts method:

public function getCasts()
{
    if (!$this->foreignKeyCastsAdded) {
        $this->addForeignKeyCasts();
    }

    return parent::getCasts();
}

So now when Eloquent retrieves the casts, we will first add our custom casts and then continue on as usual. Now, in the infamous words of the Joker: 🤡 “here we go”

protected function addForeignKeyCasts()
{
    foreach ($this->attributes as $key => $value) {
        if ($this->shouldCastForeignKey($key)) {
            $this->addForiegnKeyCast($key);
        }
    }

    $this->foreignKeyCastsAdded = true;
}

protected function shouldCastForeignKey($key)
{
    return $this->isForeignKey($key)
       && !$this->isForeignKeyCastingException($key);
}

protected function isForeignKey($key)
{
    return preg_match(static::FOREIGN_KEY_REGEX, $key);
}

protected function isForeignKeyCastingException($key)
{
    return in_array($key, static::FOREIGN_KEY_CASTING_EXCEPTIONS);
}

 protected function addForiegnKeyCast($key)
{
    if (!in_array($key, $this->casts)) {
        $this->casts[$key] = static::FOREIGN_KEY_CAST_TYPE;
    }
}

Again hopefully this is pretty readable, but if not, let’s step through it.

Notes

Updates

So yea, this was a cool idea and fun to put together but at the end of the day I realised it had no practical outcome, especially in a language like PHP where we can lean on its dynamic nature. Learnt a few things along to way though - so not a total waste of time!