Introduction
Mutators and Accessors are methods that allow you to access and modify the values of an object’s properties. In Laravel, setters and getters are used to store and retrieve data from a database.
The Mutators and Accessors will run whenever you create or update a model using the Laravel Eloquent Model. If you use Raw Queries these mutators will not run and you have to update the values of the mutator manually.
We can create Accessor and Mutator in these two ways
- The Old Syntax (It still works in Laravel 9)
- The New Syntax
The old syntax
This method takes a value and formats it using the setPriceAttribute
method to create a value for the price. The getPriceAttribute
method is used to retrieve the value from the database and return it.
Accessor
public function setPriceAttribute($value) {
$this->attributes['price'] = $value * 100;
}
Mutator
public function getPriceAttribute() {
return $this->attributes['price'] / 100 ;
}
The new syntax
In Laravel 9 there is a new way of doing the above with just one method.
The code example below shows how to set the price attribute using the Illuminate\Database\Eloquent\Casts\Attribute
class.
Accessor & Mutator in one function
use Illuminate\Database\Eloquent\Casts\Attribute;
// ...
protected function price():Attribute
{
return Attribute::make(
get: fn ($value) => $value / 100,
set: fn ($value) => $value * 100 ;
);
}
Example
Bellow is a practical example of setting the price field in a Product table. As you might have known the payment services such as stripe use cents and you have to multiply the price to change it from dollars to cent.
We can achieve this behaviour using mutators in two ways.
The product table migration file.
// 2023_01_28_141837_create_products_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->integer('price');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('products');
}
};
For our demonstration purposes the products table is very simple it contains name,price and timestamps.
I have added a few entries to the products table and after querying it, we will get something like this.
The mutator and accessor is not applied yet.
The Database
The UI
As you can see the price value is the same in the Database and in the UI.Now let’s create a new product with the mutator and accessor. To do that simply add the bellow funtion to your model and you will be setup.
// App/Modesl/Product.php
protected function price():Attribute
{
return Attribute::make(
get: fn ($value) => $value / 100,
set: fn ($value) => $value * 100 ;
);
}
Now let’s create a new product with the name Icon Pack
and price of 20
After saving the new product since we are multipying its price by 100 in our mutator, the price would be 2000.
And same way in our accessor we are dividing the value by 100, we will get 20 back to display it in the front-end of our application.
What happens to the old records
As you remember I added two records befor creating the Accessor&Mutator, you might ask what will happen to those records ?
Since the Accessor works with all products, it will be automatically applied to the old products as well , dividing their value by 100 and will return a misleading/wrong prices to our front-end application, as it is shown in the screenshot bellow.
The price of UI/UX Components Pack
is shown as 0.10 but it is 10 in the Database.
How to fix it ?
To apply the mutator for old records you have to manually update all the records. You can do this from the application’s front-end forms or create a new migration file and update the records.
By creating migration
Warning: Make sure to run the migration before adding new products.
or set a filter migration ie. only update products created before a specific date
Create a new migration by running the bellow artisan command.
php artisan make:migration update_old_products_price
In the creaed migration file add the bellow code
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use App\Models\Product;
return new class extends Migration
{
public function up()
{
$products = Product::all()->each(function (Post $post) {
$product->price = $product->price * 100;
$product->save();
});
}
};
The above migration performs these tasks:
- Get all the products
- Multiply the price to 100 (Change it to cents)
- Save the product
The mutator and Accessor will not run if
- The field is not present in the request
- You are using raw sql queries instead of using eloquent