2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Laravel の Eloquent ORM で update() 実行時にミューテータ(setXxxAttribute())が動かない

2
Last updated at Posted at 2018-06-05

確認環境

  • Laravel 5.6
  • PHP 7.1

先に結論

hasOne 等の Relationship を利用しているとき、

  • $user->contact()->update([/*...*/]) のような形だとミューテータは動かない
  • $user->contact->update(/*...*/) のような形だとミューテータは動く

事象

例として、ユーザ(user)と連絡先(contact) の1対1関係があり、電話番号は永続化の際にミューテータを介して暗号化する、というケースを考える。

app/User.php
<php

namespace App;

use Illuminate\Database\Eloquent\Model as Eloquent;

class User extends Eloquent
{
    public function contact()
    {
        $this->hasOne('App\Contact');
    }
}
app/Contact.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model as Eloquent;

class Contact extends Eloquent
{
    protected $fillable = [
        'user_id',
        'telephone_number',
    ];

    // 電話番号は暗号化して保存したい

    public function setTelephoneNumberAttribute($value)
    {
        $this->attributes['telephone_number'] = encrypt($value);
    }

    public function getTelephoneNumberAttribute($value)
    {
        return decrypt($value);
    }
}

このとき $user->contact()->update() による更新ではミューテータによる暗号化が行われない。

tinker
>>> $user = App\User::first();
=> App\User {
    id: 1
}

>>> $user->contact()->create(['telephone_number' => '00-0000-0000'])
=> App\Contact {
    id: 1,
    user_id: 1,
    // 暗号化されている
    telephone_number: "eyJpdiI6ImpKdGZLMXhvZTRGdzRwekNGM3lXN0E9PSIsInZhbHVlIjoiOTJwMTQxY1JHRk5ueWYrMGRBVGNVNVhtZzJFMTk2a0l3ajlkTGRQT29wZz0iLCJtYWMiOiJlNWM4ZTJiZjFlMTg4MWRjOTMzMWVkZDIwNTU2NmY4OTIwMzU0YzM4NGIxMzYyNjAxYTMxM2MzYTlhYzJmZmNhIn0="
}

>>> $user->contact()->update(['telephone_number' => '11-1111-1111'])
=> 1
>>> $user->contact
=> App\Contact {
    id: 1,
    user_id: 1,
    // 暗号化されていない!
    telephone_number: "11-1111-1111"
}

ちなみに、attribute を再代入して save() する場合はミューテータによる暗号化が行われる。

tinker(続き)
>>> $user->contact->telephone_number = '11-1111-1111'
=> '11-1111-1111'
>>> $user->contact->save()
=> App\Contact {
    id: 1,
    user_id: 1,
    // 暗号化されている
    telephone_number: "eyJpdiI6IkhuQVo0MlpKajZoV0MxWmUxUDNlSEE9PSIsInZhbHVlIjoiMEo2TjNxN3AyREpQK2xhVVFqNWtFeGRyU01Feng2YVROdTBYWTFRSWFLdz0iLCJtYWMiOiI2YjY2NWFjNTc5YzBmZDdkZjYwZGM4YjdiODMyYmFhYmFhMGZmZjI1NTQxNjE5N2VlOGI2YWVmOWQ4MmZkMWMwIn0="
}

さらに調べた結果、$user->contact->update() (※)だとミューテータによる暗号化が行われることがわかった。
contact() の有無の違い

tinker(続き)
>>> $user->contact->update(['telephone_number' => '22-2222-2222'])
=> 1
>>> $user->contact
=> App\Contact {
    id: 1,
    user_id: 1,
    // 暗号化されている
    telephone_number: "eyJpdiI6InlxNzFVMk9lWXJsODU4VzJpUG1QUXc9PSIsInZhbHVlIjoiTHNmU3BSQjdJR2s1SVc3NCt0MXRKVURPeGtQMmJVMXpLTFpZbUdxWjg0Zz0iLCJtYWMiOiI0MDk0OTFhODlkMWMxYjVkNTVjMDA2ZTI3MzljMGMyNDRmMjUxMDVjZGM3ODViNmIxMTdiNWI2ZTQxNWY0MmJkIn0="
}

なぜそうなるか?

$user->contact()$user->contact で返ってくるオブジェクトが異なる。

tinker
>>> $user->contact()
=> Illuminate\Database\Eloquent\Relations\HasOne {}
>>> $user->contact
=> App\Contact {
    id: 1,
    user_id: 1,
    telephone_number: "eyJpdiI6InlxNzFVMk9lWXJsODU4VzJpUG1QUXc9PSIsInZhbHVlIjoiTHNmU3BSQjdJR2s1SVc3NCt0MXRKVURPeGtQMmJVMXpLTFpZbUdxWjg0Zz0iLCJtYWMiOiI0MDk0OTFhODlkMWMxYjVkNTVjMDA2ZTI3MzljMGMyNDRmMjUxMDVjZGM3ODViNmIxMTdiNWI2ZTQxNWY0MmJkIn0="
}

前者は Query Builder に対する操作、後者は Eloquent ORM (App\Contact) に対する操作となるため、このような差が起こる。

知らないと嫌なタイミングで引っかかる罠だ。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?