アクセサ / ミューテタ

記事の内容

概要

アクセサとミューテタは、Eloquentモデルの属性を自動的に変換・取得・設定する便利な機能です。

【productsテーブル】アクセサとミューテタを確認

mysql> select * from products;
+----+-----------+-------+---------------------+---------------------+
| id | name      | price | created_at          | updated_at          |
+----+-----------+-------+---------------------+---------------------+
|  1 | PRODUCT 1 |  1500 | 2024-08-24 10:42:44 | 2024-08-24 10:42:44 |
|  2 | PRODUCT 2 |  2500 | 2024-08-24 10:42:45 | 2024-08-24 10:42:45 |
|  3 | PRODUCT 3 |  1000 | 2024-08-24 10:42:45 | 2024-08-24 10:42:45 |
+----+-----------+-------+---------------------+---------------------+
3 rows in set (0.01 sec)

アクセサ

データベースから取得した属性を自動的に加工します。

Laravel9以前

【ファイル先】app/Models/Product.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    /**
     * アクセサ: priceを取得する際に自動的にフォーマット
     */
    public function getPriceAttribute($value)
    {
        // 例: "1000" -> "1,000.00"
        return number_format($value, 2);
    }
}

Laravel9以降

【ファイル先】app/Models/Product.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Casts\Attribute;

class Product extends Model
{
    /**
     * アクセサ: priceを取得する際に自動的にフォーマット
     */
    protected function price(): Attribute
    {
        // 例: "1000" -> "1,000.00"
        return Attribute::make(
            get: fn($value) => number_format($value, 2),
        );
    }
}

Laravel9以降でもLaravel9以前の指定でアクセサは使用可能

【ファイル先】app/Http/Controllers/ProductController.php

<?php

namespace App\Http\Controllers;

use App\Models\Product;

class ProductController extends Controller
{
===========================
省略
===========================
    /**
     * 商品の詳細を表示
     */
    public function show($id)
    {
        // 指定IDの商品を取得($idが2の場合)
        $product = Product::findOrFail($id);

        $price = $product->price;

        // DDでprice変数の中身をチェック
        dd($price);

        return view('products.show', compact('product'));
    }
===========================
省略
===========================
}

【price変数の中身】

"2,500.00" // app/Http/Controllers/ProductController.php:29

ミューテタ

データベースに保存する前に属性を加工します。

Laravel9以前

【ファイル先】app/Models/Product.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Product extends Model
{
    /**
     * 商品名を大文字に変換して保存
     */
    public function setNameAttribute($value)
    {
        $this->attributes['name'] = strtoupper($value);
    }
}

Laravel9以降

【ファイル先】app/Models/Product.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Casts\Attribute;

class Product extends Model
{
    /**
     * 商品名を大文字に変換して保存
     */
    protected function name(): Attribute
    {
        return Attribute::make(
            set: fn($value) => strtoupper($value)
        );
    }
}

Laravel9以降でもLaravel9以前の指定でミューテタは使用可能

【ファイル先】app/Http/Controllers/ProductController.php

<?php

namespace App\Http\Controllers;

use App\Models\Product;

class ProductController extends Controller
{
===========================
省略
===========================
    /**
     * 商品を保存
     */
    public function store(Request $request)
    {
        $validatedData = $request->validate([
            'name' => 'required|string|max:255',
            'price' => 'required|numeric',
        ]);

        // 商品をデータベースに保存(nameをproductと仮定する)
        $product = Product::create($validatedData);

        // DDでproduct変数の中身をチェック
        dd($product);

        return redirect()->route('products.index')->with('success', '商品を追加しました!');
    }
===========================
省略
===========================
}

【product変数の中身】

App\Models\Product {#318 ▼ // app/Http/Controllers/ProductController.php:47
===========================
省略
===========================
  #attributes: array:7 [▼
    "name" => "PRODUCT"
    "price" => "2000"
    "updated_at" => "2024-01-01 00:01:23"
    "created_at" => "2024-01-01 00:01:23"
    "id" => 1
  ]
 ===========================
省略
===========================
}

その他

デフォルト値設定

【ファイル先】app/Models/Product.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Support\Facades\Cache;

class Product extends Model
{
    protected $fillable = [
        'name',
        'price',
        'options',
        'status',
    ];

    // デフォルト値の設定
    protected function status(): Attribute
    {
        // 値がnullの場合にデフォルトで'pending'を返す
        return Attribute::make(
            get: fn($value) => $value ?? 'pending'
        );
    }
}

キャスト組み合わせ

【ファイル先】app/Models/Product.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Support\Facades\Cache;

class Product extends Model
{
    protected $fillable = [
        'name',
        'price',
        'options',
        'status',
    ];

    // キャストとアクセサ/ミューテタの組み合わせ
    protected function options(): Attribute
    {
        return Attribute::make(
            get: fn($value) => json_decode($value, true), // JSONを配列に変換
            set: fn($value) => json_encode($value) // 配列をJSONに変換して保存
        );
    }
}

動的表示

【ファイル先】app/Models/Product.php

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Casts\Attribute;

class Product extends Model
{
    protected $fillable = [
        'name',
        'price',
        'status',
    ];

    // 動的に割引後の価格を計算するメソッド
    protected function discountedPrice(): Attribute
    {
        // 割引率を変数として定義(例: 10%の割引 = 0.9)
        $discountRate = 0.9;

        return Attribute::make(
            // 割引後の価格を計算
            get: fn() => $this->price * $discountRate
        );
    }
}
記事の内容
閉じる