Summer Sale! All accounts are 50% off this week.

PHLAK's avatar
Level 9

Mixed model relationship/attribute casing (camelCase vs snake_case) is bugging me

When using camelCase for model methods and having a multi-word relationship method names it bugs me that I have mixed casing between relationships and attributes.

For example, given the following model:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    public function creditCard()
    {
        return $this->hasOne(CreditCard::class);
    }

    public function getFullNameAttribute()
    {
        return $this->first_name . ' ' . $this->last_name;
    }
}

When accessing the various relationships/attributes of the model I get mixed casing like this:

<?php

// Get the user's full name
$user->full_name;

// Get the user's credit card info
$user->creditCard->number;
$user->creditCard->expiration_date; // This is especially annoying

I would rather all the properties have matched casing like so:

<?php

$user->credit_card->number;
$user->credit_card->expiration_date; // Especially here

The obvious solution is to just use snake_case for the relationship method names but then my model method casing will be mixed. Is there a simple/proper solution to this problem?

0 likes
6 replies
Snapey's avatar

In the case of getFullNameAttribute, both full_name and fullName work?

orrd's avatar

The standard convention in PHP is camelcase for class methods and snake case is common for database values, so there is some funny-ness that has to happen when there are method names that are based on database names. Other frameworks have similar situations (including Rails).

Your options are pretty much either to get used to it and accept it, or name your database fields in camelcase if you prefer.

PHLAK's avatar
Level 9

@Snapey Yes, both snake_case and camelCase works calling accessors. That's not the problem though.

@orrd My database fields ARE snake_case. The problem is that when adhearing to PHP standards of camelCase method names for relations it causes inconsistencies (i.e. $user->creditCard->expiration_date). But if I snake_case the method name my methods are then inconsistent.

It's a minor annoyance and perhapse I'm being a bit OCD, I know. Maybe I should just deal with it.

chrisreiduk's avatar

I know this is an old thread, but you can access your relationships with snake case. So although the relationship is declared with a method named creditCard, you can access the relationship with $user->credit_card->expiration_date. Hope it helps someone.

Hupernikao's avatar

Was this changed in a later version of laravel? On laravel 8 when I declare my relationships with camel case names I can only access the relationship using camel case. If I try to access the relationship with snake case it returns null.

class Baz extends Model
{
    public function fooBar()
    {
        return $this->belongsTo(FooBar::class);
    }
}
//returns null
$res1 = $baz->foo_bar

//returns Instance of FooBar
$res2 = $baz->fooBar
pierce's avatar

this is quite a nuisance tbh, and something which i struggled with today here's my workaround so far, i dont see why this isnt applied similarly on the base eloquent model tbh

create a new trait as below and apply it to your models as needed and you will be able to do both $thing->fooBar and $thing->foo_bar to get the relationship Collection

<?php

namespace App\Traits;

trait SnakeCaseRelations
{

    public function __get($property_name): mixed
    {
        // check if there is a relation matching with camelCase
        // and return it
        $camel_property_name = $this->snakeToCamelCase($property_name);
        $relations = $this->getRelations();
        if (array_key_exists($camel_property_name, $relations)) {
            return $this->{$camel_property_name}();
        }
        return null;
    }

    private function snakeToCamelCase($string) {
        $words = explode('_', $string);
        $camelCase = '';
        
        foreach ($words as $index => $word) {
            if ($index === 0) {
                $camelCase .= $word;
            } else {
                $camelCase .= ucfirst($word);
            }
        }
        
        return $camelCase;
    }
}
1 like

Please or to participate in this conversation.