Automatically Format Laravel Database Fields with Accessors and Mutators



There’s a really neat feature in Laravel that often gets overlooked because of the feature’s complicated sounding name. We’re talking about accessors and mutators.
What exactly are accessors and mutators?
  • Accessors: Format something when retrieving from the database
  • Mutators: Format something when saving to the database
Both of these can help us save immense amounts of time (and frustration) because we know that whenever retrieving or saving information to our database, we will know it will always be saved in the correct format.
One of the most common uses for a mutator is to be sure that a user’s password is always hashed before it gets saved into the database. This ensures that you don’t have to always remember to hash a password wherever you save a user.

The Basics

For our examples, let’s say that we have a User Eloquent model. These are the fields on our User:

- first_name
- last_name
- email
- username
- password
- expires_at
- explodes_at
- gets_mad_at
For our most basic of examples, let’s say that we always want to ensure that when we grab a user, their first name is capitalized. This is so we can display it to our users and not have to worry about all lowercase names.
Here is the code for when we create a user:

$user = App\User::create([
    'first_name'  => 'chris',
    'last_name'   => 'sevilleja',
    'email'       => 'chris&64;scotch.io',
    'password'    => 'password',
    'expires_at'  => Carbon::now()->addMonth(),
    'explodes_at' => Carbon::now()->addDay(),
    'gets_mad_at' => Carbon::yesterday()
]);
Our user didn’t care to enter in their first name or last name with the proper punctuation but we still want to show their name as capitalized. If we save our user to the database with the above, we’ll need to use an accessor to format the user’s first_name and last_name when we retrieve them.

Accessors use getAttribute

An accessor will be defined on our User model:
<?php

// app/User.php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model {
    
    /**
     * Always capitalize the first name when we retrieve it
     */
    public function getFirstNameAttribute($value) {
        return ucfirst($value);
    }

    /**
     * Always capitalize the last name when we retrieve it
     */
    public function getLastNameAttribute($value) {
        return ucfirst($value);
    }

}
Very easy to do. We just define a getAttribute() method and make sure that we camel case the attribute name (first_name turns into getFirstName).
Then we use PHP’s ucfirst function to capitalize the first letter. Easy!

Mutators use setAttribute

Now let’s say we wanted to handle capitalizing a user’s name when we save them to the database so that our database is uniform in all of its name fields. Just like how we created the getAttribute method, we’ll create a setAttribute method.
The process itself will be a little different though.
<?php

// app/User.php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model {
    
    /**
     * Always capitalize the first name when we save it to the database
     */
    public function setFirstNameAttribute($value) {
        $this->attributes['first_name'] = ucfirst($value);
    }

    /**
     * Always capitalize the last name when we save it to the database
     */
    public function setLastNameAttribute($value) {
        $this->attributes['last_name'] = ucfirst($value);
    }

}
The big difference is we are adding Attribute to the naming convention here and instead of returning something (we’re not getting anything), we’ll directly call the attribute on the model using $this->attributes.
Now whenever we save to the database, we can make sure that these fields are capitalized.

Which do I use?

Now that we’ve seen accessors and mutators in action, which do you use and when? That really all depends on the scenario. Sometimes you’ll need to only use an accessor or mutator; other times you will need to use both.
An instance of just using a mutator would be hashing passwords. We’re not going to unhash a password when we retrieve it.
A time we would use both accessor and mutator is for json_encodeing and json_decodeing JSON objects in a Postgres database.
Let’s look at some more examples to get the hang of things.

Hashing Passwords

Here’s a really quick example of how we can always ensure that passwords are never saved plain text into our database.
<?php

// app/User.php

namespace App;

use Illuminate\Database\Eloquent\Model;

use Hash;

class User extends Model {
    
    /**
     * Always capitalize the first name when we save it to the database
     */
    public function setPasswordAttribute($value) {
        $this->attributes['password'] = Hash::make($value);
    }

}

Encoding and Decoding JSON Objects

Let’s say we add a field to our User model called settings and we store all their settings in a JSON field.

// get the scotchyscotch user
$user = App\User::where('username', 'scotchyscotch')->first();

// give them some settings
$user->settings = [
    'notifications' => true,
    'location'      => 'Las Vegas'
];
Now this wouldn’t save to our database since it’s an array going into a JSON field. We could json_encode the settings field but that would be tedious to remember to do it all over our application.
We would also have to remember to json_decode this settings field whenever we grabbbed a user.
Luckily we can use an accessor and a mutator to ensure this settings field is always the way we want it to be.
<?php

// app/User.php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model {
    
    /**
     * Always json_decode settings so they are usable
     */
    public function getSettingsAttribute($value) {
        return json_decode($value);

        // you could always make sure you get an array returned also
        // return json_decode($value, true);
    }

    /**
     * Always json_encode the settings when saving to the database
     */
    public function setSettingsAttribute($value) {
        $this->attributes['settings'] = json_encode($value);
    }

}

Slug-ifying Slugs

Another quick example is when we are saving an article to a database. We will need to have a slug for that article.

// the article
$title = 'What Does Water on Mars Mean?';

// the slug
$slug = 'what-does-water-on-mars-mean';
We wouldn’t want to have to have a user enter in the slug every time they saved a new article. We can use a mutator to create the slug for us!
<?php

// app/User.php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model {
    
    /**
     * Create the slug from the title
     */
    public function setSlugAttribute($value) {

        // grab the title and slugify it
        $this->attributes['slug'] = str_slug($this->title);
    }

}
We are calling $this->title and the Laravel helper method str_slug to create the slug attribute when we save our model.
We are using $this->title instead of $this->attributes['title'] because we are not actually setting the title, we just want to get it.

Always Get Date Objects

It is much easier to use Carbon, the extension of the native PHP DateTime class when dealing with dates or times.

// this is much easier
$date = Carbon::now();
$date = $date->addMonth();

// than dealing with this
$date = '2012-01-31 00:00:00';
$date = date('Y-m-d', strtotime('+1 month', $date));
The DateTime string is not the easiest thing to read or work with so we usually have to use Carbon to work with our dates. Laravel handily makes sure that all of our dates are stored in the correct format.
When saving our dates, we are allowed to pass in different dates like so:

// get the awesomedude user
$user = App\User::where('username', 'awesomedude')->first();

// all are valid ways to set the date
$user->expires_at = '2015-09-30 23:01:34';
$user->expires_at = '2015-06-26';
$user->expires_at = Carbon::now();
$user->expires_at = Carbon::tomorrow();
$user->expires_at = 1443765240; // unix timestamp

// save the user
$user->save();
How does Laravel know which fields are dates though? That can be defined on the Eloquent Model using the $dates property. By default, the created_at and updated_at fields are automatically mutated into dates.
Unlike the above accessors/mutators where we define a specific method, we can override the default dates and set more date fields to be mutated by overriding the $dates property on our model like so:
<?php 

// app/User.php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model {
    
    /**
     * The attributes that should be mutated to dates.
     *
     * @var array
     */
    protected $dates = [
        'created_at', 
        'updated_at', 
        'expires_at', 
        'gets_mad_at'
    ];

}
All of the fields above will be set to the proper date type now when saving our model.
Inversely, all of the $dates fields will be casted into Carbon instances upon retrieval.

// get awesomedudette
$user = App\User::where('username', 'awesomedudette')->first();

// find an hour before the time this user explodes at
$explodeWarning = $user->explodes_at->subHour();