#08 Laravel基礎

スコープとアクセサー

スコープとは

スコープ? Eloquentのクエリ条件を再利用可能なメソッドにする仕組み。`scopeActive()`のように定義し、`User::active()->get()`のように使う。 は、よく使うクエリ条件をEloquentモデルのメソッドとして定義する仕組みです。同じ where 条件をコントローラーのあちこちに書く代わりに、1か所にまとめられます。


ローカルスコープ

scope というプレフィックスを付けたメソッドを定義します。

<?php
// 📁 app/Models/Post.php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    // スコープ名は "scope" + パスカルケースで命名
    public function scopePublished($query)
    {
        return $query->where('status', 'published');
    }

    public function scopeRecent($query, int $days = 30)
    {
        return $query->where('created_at', '>=', now()->subDays($days));
    }
}

呼び出し方

// 📁 app/Http/Controllers/PostController.php

// published() と recent() スコープを組み合わせる
$posts = Post::published()->recent(7)->latest()->paginate(15);

scope プレフィックスを除いたメソッド名(publishedrecent)でチェーンできます。


グローバルスコープ

ある条件を常に自動で適用したい場合はグローバルスコープを使います。

php artisan make:scope ActiveUserScope
<?php
// 📁 app/Models/Scopes/ActiveUserScope.php

namespace App\Models\Scopes;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;

class ActiveUserScope implements Scope
{
    public function apply(Builder $builder, Model $model): void
    {
        $builder->where('is_active', true);
    }
}

モデルに登録します。

<?php
// 📁 app/Models/User.php

use App\Models\Scopes\ActiveUserScope;

class User extends Model
{
    protected static function booted(): void
    {
        static::addGlobalScope(new ActiveUserScope());
    }
}

これで User::all() は常に WHERE is_active = 1 が付くようになります。

グローバルスコープを外したい場合:

// このクエリだけグローバルスコープを無効化
$allUsers = User::withoutGlobalScope(ActiveUserScope::class)->get();

アクセサー

アクセサー? モデルの属性値を取得するときに自動で変換処理を行う仕組み。たとえば`full_name`属性を姓名の結合で返すようにできる。 は、モデルの属性値を取得するときに自動で変換する仕組みです。

Laravel 9以降の書き方(Attribute クラスを使う):

<?php
// 📁 app/Models/User.php

use Illuminate\Database\Eloquent\Casts\Attribute;

class User extends Model
{
    // フルネームを返す仮想属性
    protected function fullName(): Attribute
    {
        return Attribute::make(
            get: fn () => "{$this->last_name} {$this->first_name}",
        );
    }

    // メールアドレスを常に小文字で返す
    protected function email(): Attribute
    {
        return Attribute::make(
            get: fn ($value) => strtolower($value),
        );
    }
}

使い方:

$user = User::find(1);
echo $user->full_name;  // → "山田 太郎"(スネークケースでアクセス)
echo $user->email;      // → 常に小文字

ミューテーター(保存時の変換)

保存するときに変換するには set も指定します。

<?php
// 📁 app/Models/User.php

protected function password(): Attribute
{
    return Attribute::make(
        set: fn ($value) => bcrypt($value), // ← 保存前に自動でハッシュ化
    );
}
// パスワードを代入するだけで自動でハッシュ化される
$user->password = 'secret123';
$user->save();

キャスト(型の変換)

$casts プロパティを使うと、カラムの型を自動で変換できます。

<?php
// 📁 app/Models/Post.php

class Post extends Model
{
    protected $casts = [
        'published_at' => 'datetime',     // Carbon オブジェクトに変換
        'metadata'     => 'array',        // JSON ↔ 配列に自動変換
        'is_featured'  => 'boolean',      // 整数 ↔ bool に変換
    ];
}
$post->published_at->format('Y年m月d日');  // Carbon メソッドが使える
$post->metadata['key'];  // 配列としてアクセス(DBにはJSONで保存)

まとめ

  • ローカルスコープ:scopeXxx() でよく使うwhere条件を再利用できる
  • グローバルスコープ:常に適用される条件を自動化できる
  • アクセサー:取得時に属性値を変換する(Attribute::make(get: ...)
  • ミューテーター:保存時に属性値を変換する(Attribute::make(set: ...)
  • $casts:JSONや日時など型の自動変換に使う

次回はEloquentのmanyToManyリレーションとポリモーフィックリレーションを学びます。