九保すこひ@フリーランスエンジニア|累計300万PVのブログ運営中
さて、前回は記事内容が20,000文字を越える長編記事「Laravel・データベースからデータ取得する全実例」をお届けしましたが、今回はその続編として「DBのデータの追加/編集/削除」に焦点を当ててまとめてみました。
※実を言うとこの2つは元々ひとつの記事にするつもりでしたがあまりにも膨大になりすぎることがわかったので、急遽分割することにしました。
では、早速ひとつずつ見ていきましょう。
【環境】
Laravel 5.6
MySQL 5.7
目次
DBにデータを追加する
insert()を使ってデータ追加する
データベースにデータを追加するにはinsert()を使います。
実際の例を見てみましょう。
\DB::table('items')->insert([ 'name' => '名前' ]);
まず、\DB::table()でどのテーブルを使うかを指定し、そしてアロー演算子「->」を使ってinsert()をつなげています。
また、以下のようにEloquentモデルでも同じことが可能です。
\App\Item::insert([ 'name' => '名前' ]);
そして、insert()の引数は配列で、
- keyがカラム名
- valueが保存する内容
となっています。
では、データ追加後のテーブルを見てみましょう。
赤枠の部分が新しく追加された内容です。
ただ、見てもらえば分かるとおり「created_at」と「updated_at」がNULLになっています。
ここがinsert()を使う時の注意点です。つまり、insert()を使ってデータ追加する場合、以下のように明示的に日付データを追加してあげないとNULLになってしまうのです。
\App\Item::insert([ 'name' => '名前', 'created_at' => now(), 'updated_at' => now() ]);
そのため、私の場合はinsert()よりも次で紹介するsave()を使ってデータ追加することの方が多いです。
しかも、もうひとつ重要なことがあります。insert()は、イベントの自動実行ができないということです。
例えば、以下のようにモデル内にデータ追加後に「ItemAdded」というイベントを実行するように設定していたとしても、insert()の場合は無視されてしまうのです。
<?php namespace App; use App\Events\ItemAdded; use Illuminate\Database\Eloquent\Model; class Item extends Model { protected $dispatchesEvents = [ 'created' => ItemAdded::class ];
ただし、以下のように手動でイベントを実行することもできます。(ネームスペースに気をつけてください)
event(new ItemAdded);
なお、insert()と似たメソッドにinsertGetId()もあります。こちらは、以下のように追加したデータのIDを取得する場合に使用します。
$new_id = \App\Item::insertGetId([ 'name' => '名前', 'created_at' => now(), 'updated_at' => now() ]);
save()でデータ追加する
前の項目で紹介したinsert()では以下の2点に注意が必要でした。
- 「created_at」と「updated_at」を明示的に指定しないとNULLになる。
- イベントが実行できない。
これを解決してくれるのがsave()を使ったデータ追加です。
実際の例を見てみましょう。
$item = new \App\Item; $item->name = '名前'; $item->save();
モデルのインスタンスを作成し「name」などデータ格納。そしてsave()を呼ぶ。
このやり方だと、自動で「created_at」「updated_at」に日付が入力されますし(つまり忘れることがなくなる)、モデル内の「$dispatchesEvents」で設定したイベントも自動的に実行されることになります。そのため、どうしても配列を使ってデータ追加したい場合やそもそもイベントを実行したくない場合でない限り、insert()よりsave()を使うのをおすすめします。
追加後のテーブルはこうなります。
ちなみに、save()には引数を入れることができて、以下のようにすることで「touch(コードの下で説明)」を無効にすることができます。
$item->save([ 'touch' => false ]);
※この場合の「touch」とは、例えばブログ記事の「Post」テーブルとコメント「Comment」テーブルの関係を考えると分かりやすいです。もしある記事にコメントが投稿されたら、その記事データ(つまり親テーブル)の「updated_at」も最新の日付にしたいですよね。これがtouchです。ただし、「Comment」内に以下のようにbelongsToやbelongsManyなどリレーション、そして$touchesが設定されている必要があります。
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Comment extends Model { protected $touches = ['post']; // Relationship public function post() { return $this->belongsTo('App\Post'); } }
データが見つからない場合を考慮したデータ追加
firstOrCreate()でデータ追加
例えば、「もしデータがあれば取得するけど、ない場合はデータ追加したい」場合にはfirstOrCreate()が使えます。
実際の例を見てみましょう。
$item = \App\Item::firstOrCreate(['name' => '鈴木']);
この場合、「name」に「鈴木」がすでに存在している場合は何も追加せずデータ取得しますが、もし見つからない場合は新しくデータを追加してそのデータを$itemに格納します。
また、引数を増やすことで条件を複数指定することもできます。
$item = \App\Item::firstOrCreate( ['name' => '新しい名前'], ['deleted_at' => null] );
firstOrNew()でデータ追加
前項目のfirstOrCreate()と似ていますが、こちらはsave()を実行するまでデータ追加されないのが特徴です。
$item = \App\Item::firstOrNew(['name' => '新しい名前']); $item->save(); // ここで保存される
つまり、以下のように後から違うカラムのデータを追加したい場合に使うといいでしょう。
$item = \App\Item::firstOrNew(['name' => '新しい名前']); $item->product_code = 'xxx'; $item->save();
DBのデータを変更する
すでにデータベースに存在しているデータを変更する場合はupdate()を使います。
例えば、IDが「1」の「name」カラムを変更する場合を見てみましょう。
\DB::table('items') ->where('id', 1) ->update([ 'name' => '名前xxx2' ]);
ここで重要なのは、where()でID番号が指定されているところです。
insert()の場合は、引数に「id」が必要ありませんでしたがupdate()の場合はどのデータを変更するかを指定しないと行けないので(エラーになります)whereは忘れずに記述しましょう。
もちろん、where()は以下のように複数データにも使うことができて、その場合は該当する全データが変更されます。
\DB::table('items') ->where('id', '<', 5) // idが5より少ないデータ全てを変更 ->update([ 'name' => '新しい名前' ]);
もちろん、Eloquentモデルでも同じことができます。
\App\Item::where('id', 1)->update(['name' => '新しい名前']);
ただし、update()もinsert()と同じく、
- updated_atが自動で更新されない
- イベントが自動で実行されない
といったデメリットがあります。
save()でデータを変更する
もしupdate()のデメリットがが気になる場合は以下のようにsave()を使ったコードで対応しましょう。
save()で複数データを変更する
$items = \App\Item::where('id', '<', 5)->get(); foreach ($items as $item) { $item->name = '新しい名前'; $item->save(); }
save()で1つのデータだけを変更する
$item = \App\Item::where('id', 1)->first(); $item->name = '新しい名前'; $item->save();
これなら、「updated_at」は自動的に保存日時が更新されますし、イベントも実行されます。
JSONカラムの変更をする
以下のようなJSONカラム(MySQLは5.7.8から有効)の場合は以下のようにアロー演算子「->」を使ってデータ変更ができます。
\App\User::where('id', 1)->update(['network->os' => 'Ubuntu']);
実行後のテーブルはこうなります。
数字データを操作する
increment()で数字データを増やす
increment()を使えば、数字データを簡単に増やすことができます。
例えば、注文数を増やすコードを見てみましょう。
\App\Item::increment('ordered_count');
こうすることで、「ordered_count」が+1されることになります。つまり元の数字が「10」だったとしたら、「11」になるわけですね。
また、足したい数字を指定することもできます。
\App\Item::increment('ordered_count', 5);
ちなみに、その他のカラム内容を同時に変更することもできます。
\App\User::increment('ordered_count', 5, ['name' => '新しい名前']);
decrement()で数字データを減らす
increment()の逆でdecrement()は数字データを減らす役割をします。
\App\Item::decrement('ordered_count');
こうすることで、「ordered_count」が-1されます。例えば元の数字が「10」だったとしたら、「9」になるわけですね。
もちろん、こちらも特定の数字を指定することができます。
\App\Item::decrement('ordered_count', 5);
そして、こちらも他のカラムを一緒に変更することができます。
\App\User::decrement('ordered_count', 5, ['name' => '新しい名前']);
データが見つからない場合を考慮した変更
もしすでにデータが存在している場合はデータを変更し、ないならデータ追加したい場合、updateOrCreate()が使えます。
例を見てみましょう。
$item = \App\Item::updateOrCreate( ['name' => '鈴木'], ['name' => '佐藤'] );
この場合の意味は、
- 「name」カラムが「鈴木」になっているデータを見つけたら、そのデータの「name」を「佐藤」へ変更する。
- でも、もしデータがないなら「佐藤」というデータを新規追加する
となります。
なお、検索条件を増やしたい場合は、以下のように第1引数の中身を増やしてください。
$item = \App\Item::updateOrCreate( ['name' => '元の名前', 'deleted_at' => null], ['name' => '変更された名前'] );
DBのデータを削除する
delete()でデータを削除する
すべてのデータを削除する
データベースからすでに存在しているデータを削除するにはdelete()を使います。
実際の例を見てみましょう。
\DB::table('items')->delete();
この例は「items」テーブルの全データを削除します。
ちなみに、特別な理由がない場合はテーブルを初期化したい場合、truncate()を使うことをおすすめします。
\DB::table('items')->truncate();
なぜなら、truncate()はidのインクリメント(自動採番)を1に戻してくれるからです。つまり、delete()で全データを消した場合、その後データ追加したらidが「1」から始まらないという可能性がでてきます。
特定のデータだけ削除する
特定のデータだけを削除したい場合は、以下のようにwhere()を使いましょう。
\App\Item::where('id', 2)->delete();
destroy()でIDを使ってデータ削除する
destroy()を使えば、idを使ってデータ削除が可能です。find()の削除バージョンですね。
では、実際の例を見てみましょう。
$id = 1; \App\Item::destroy($id);
ID番号を引数として入れるだけで削除ができるので、コードがとても短くなりますね。また、ID番号の配列を引数として指定することで、複数データの削除も可能です。
\App\Item::destroy([2, 3]);
なお、destroy()のいいところはdelete()と同じく、削除イベントが自動で実行されるところです。しかも、上記のような複数削除にも対応しているのでイベントまわりをきちんと実行したい場合はdestroy()も選択肢のひとつになるでしょう。
削除イベントを実行するには
もし、モデルに以下のような「削除イベント」を追加していたとしても、get()やfirst()などでデータ取得する前にdelete()を実行してしまったら削除イベントは実行されません。
<?php namespace App; use App\Events\ItemAdded; use App\Events\ItemDeleted; use Illuminate\Database\Eloquent\Model; class Item extends Model { protected $dispatchesEvents = [ 'deleted' => ItemDeleted::class, ];
削除イベントを実行したい場合は以下のようにget()やfirst()でデータ取得しておくか、destroy()でデータ削除する必要があります。
複数の削除イベントを実行する場合
$items = \App\Item::get(); foreach ($items as $item) { $item->delete(); }
ひとつだけ削除イベントを実行する場合
$item = \App\Item::first(); $item->delete();
ソフトデリート(論理削除)を実行する
LaravelにはEloquentモデルにソフトデリート機能が備わっています。ソフトデリートは万が一なにかの手違いでデータ削除してしまっても、物理的にデータがなくなるわけではないのでいつでも復活が可能です。そのため、もしデータが特に大事な場合はソフトデリートの利用を検討しましょう。
ソフトデリートの使い方
まずテーブルに削除されているかどうかをチェックする日付カラムが必要になります。
そこで、マイグレーション内に以下のようにsoftDeletes()を指定することで専用の「deleted_at」というカラムを追加することができます。
Schema::create('items', function (Blueprint $table) { $table->increments('id'); $table->string('name'); $table->softDeletes(); $table->timestamps(); });
これでマイグレーションを実行すると、DBのテーブルは以下のようになります。
なお、カラムの中身は、削除されたらその日時が保存され、削除されていない場合はNULLになります。
次に、Eloquentモデル内にソフトデリートを使う設定を追加します。必要となるのは「SoftDeletes」トレイトを読み込むことだけです。
<?php namespace App; use App\Events\ItemAdded; use App\Events\ItemDeleted; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; class Item extends Model { use SoftDeletes;
これで準備は完了です。
では、実際に以下のコードを実行してデータを削除し、テーブル内を見てみましょう。
\App\Item::destroy(1);
うまく削除された日時が追加されました。これで、以下のように通常通りget()などでデータ取得してもID番号「1」は含まれないようになります。
$items = \App\Item::get();
ソフトデリートしたデータの取得
ソフトデリートされたデータを「含めて」データ取得したい場合は、withTrashed()、ソフトデリートされたデータ「だけ」取得したい場合はonlyTrashed()を使いましょう。
$items = \App\Item::withTrashed()->get();
$items = \App\Item::onlyTrashed()->get();
ソフトデリートを解除する
ソフトデリートしたデータを元に戻すには、restore()を使います。
$items = \App\Item::withTrashed() ->where('id', 1) ->restore();
なお、where()を忘れてしまうと全データが復活してしまうので注意してください。
\App\Item::withTrashed()->restore();
ソフトデリートを無視して物理削除する
例えば、ソフトデリートが使えるようにしているけれども物理的にデータを削除したい(つまりDBから消したい)場合はforceDelete()を使います。
\App\Item::where('id', 1)->forceDelete();
※この場合、もちろんデータの復活はできません。
おわりに
ということで、今回はLaravelでDB操作をする方法をまとめてみました。
改めてこういう基本的な内容を確認すると、いかにLaravelの完成度が高いかを実感します。
ぜひみなさんもご活用ください。
ではでは!