概要
特定のユーザーの情報とともに、そのユーザーに紐づく本の情報を取得する方法を色々試してみました。
フレームワークだとかモデルでのデータベース操作に関してはLaravelが初めてですので、間違っている点があればコメント欄でバンバン指摘待ってます。
実装
前提
今回はLaravelの標準で用意されているUserモデルに、追加で実装したBookモデルを関連づけています。
Userモデルのidに、Bookモデルのuser_idが外部キーとして紐づいています。
最終的な出力結果として、ユーザー情報の中に、紐づいている本の情報が内包しているような形を目指しました。
準備
まずBookテーブルの作成です。
class CreateBookTable extends Migration
{
public function up()
{
Schema::create('book', function (Blueprint $table) {
$table->increments('id'); // 本ID
$table->integer('user_id')->unsigned(); // 登録者ID
$table->foreign('user_id')->references('id')->on('users'); // 紐付け
$table->string('name'); // 本の名前
$table->timestamps(); // 登録、更新日時
$table->softDeletes(); // ソフトデリート
});
}
}
次にUserモデルとBookモデルに、1対nのリレーションを追加しておきます。
class User extends Model implements AuthenticatableContract,
AuthorizableContract,
CanResetPasswordContract
{
public function book()
{
return $this->hasMany('App\Book');
}
}
class Book extends Model
{
protected $table = 'book';
public function user()
{
return $this->belongsTo('App\User');
}
}
テーブル内のサンプルデータです。
usersテーブル
+----+---------------+-------------------+---------------------+---------------------+
| id | name | email | created_at | updated_at |
+----+---------------+-------------------+---------------------+---------------------+
| 1 | ユーザー1 | user1@example.com | 2015-09-21 22:35:20 | 2015-09-21 22:35:20 |
+----+---------------+-------------------+---------------------+---------------------+
bookテーブル
+----+---------+----------------------+---------------------+---------------------+------------+
| id | user_id | name | created_at | updated_at | deleted_at |
+----+---------+----------------------+---------------------+---------------------+------------+
| 1 | 1 | ユーザー1の本1 | 2015-09-21 22:35:41 | 2015-09-21 22:35:41 | NULL |
| 2 | 1 | ユーザー1の本2 | 2015-09-21 22:35:56 | 2015-09-21 22:35:56 | NULL |
+----+---------+----------------------+---------------------+---------------------+------------+
実行したSQLを確認するためのコードの追加と、事前にログインしているユーザーIDを取得しておきます。
DB::listen(function ($sql, $bind) {
var_dump($sql);
var_dump($bind);
});
$user_id = Auth::id();
// 上記のSQL
string 'select * from `users` where `users`.`id` = ? limit 1' (length=52)
array (size=1)
0 => int 1
各取得コード、結果
色々な取得の形を試すために下記の5つのコードを用意しました。
$test1 = User::with('book')
->find($user_id)
->toArray();
$test2 = Auth::user()
->with('book')
->find($user_id)
->toArray();
$test3 = User::where('id', $user_id)
->first()
->book
->toArray();
$test4 = User::find($user_id)
->book
->toArray();
$test5 = User::find($user_id)
->book()
->with('user')
->join('users', 'users.id', '=', 'book.user_id')
->get(['users.*', 'book.*'])
->toArray();
それぞれ、コード、発行されるSQL、取得した配列のvar_dumpでの出力結果です。
テスト1
私がやってみた限り一番これが短く簡単で、ユーザーの情報の中に、関連づいている本の情報が内包する正しい形で取得できています。
SQLも悪くないのではないでしょうか。
$test1 = User::with('book')
->find($user_id)
->toArray();
// SQL
string 'select * from `users` where `users`.`id` = ? limit 1' (length=52)
array (size=1)
0 => int 1
string 'select * from `book` where `book`.`deleted_at` is null and `book`.`user_id` in (?)' (length=82)
array (size=1)
0 => int 1
// 出力結果
array (size=6)
'id' => int 1
'name' => string 'ユーザー1' (length=13)
'email' => string 'user1@example.com' (length=17)
'created_at' => string '2015-09-21 22:35:20' (length=19)
'updated_at' => string '2015-09-21 22:35:20' (length=19)
'book' =>
array (size=2)
0 =>
array (size=6)
'id' => int 1
'user_id' => int 1
'name' => string 'ユーザー1の本1' (length=20)
'created_at' => string '2015-09-21 22:35:41' (length=19)
'updated_at' => string '2015-09-21 22:35:41' (length=19)
'deleted_at' => null
1 =>
array (size=6)
'id' => int 2
'user_id' => int 1
'name' => string 'ユーザー1の本2' (length=20)
'created_at' => string '2015-09-21 22:35:56' (length=19)
'updated_at' => string '2015-09-21 22:35:56' (length=19)
'deleted_at' => null
テスト2
テスト1と最初のUserクラスかAuth::user()で取得するかの違いのみです。
$test2 = Auth::user()
->with('book')
->find($user_id)
->toArray();
// SQL
string 'select * from `users` where `users`.`id` = ? limit 1' (length=52)
array (size=1)
0 => int 1
string 'select * from `book` where `book`.`deleted_at` is null and `book`.`user_id` in (?)' (length=82)
array (size=1)
0 => int 1
// 出力結果
array (size=6)
'id' => int 1
'name' => string 'ユーザー1' (length=13)
'email' => string 'user1@example.com' (length=17)
'created_at' => string '2015-09-21 22:35:20' (length=19)
'updated_at' => string '2015-09-21 22:35:20' (length=19)
'book' =>
array (size=2)
0 =>
array (size=6)
'id' => int 1
'user_id' => int 1
'name' => string 'ユーザー1の本1' (length=20)
'created_at' => string '2015-09-21 22:35:41' (length=19)
'updated_at' => string '2015-09-21 22:35:41' (length=19)
'deleted_at' => null
1 =>
array (size=6)
'id' => int 2
'user_id' => int 1
'name' => string 'ユーザー1の本2' (length=20)
'created_at' => string '2015-09-21 22:35:56' (length=19)
'updated_at' => string '2015-09-21 22:35:56' (length=19)
'deleted_at' => null
テスト3
Userモデルにwhereでログイン中のIDで絞り込んでいます。
関連する本を取得は出来るのですが、このままでは$test3にはユーザー情報が格納されませんね。
一度分割して別の変数に入れたり、配列に両方格納するといった方法ならユーザーと本の情報を保持はできますが、紐付けが面倒になりますね。
$test3 = User::where('id', $user_id)
->first()
->book
->toArray();
// SQL
string 'select * from `users` where `id` = ? limit 1' (length=44)
array (size=1)
0 => int 1
string 'select * from `book` where `book`.`deleted_at` is null and `book`.`user_id` = ? and `book`.`user_id` is not null' (length=112)
array (size=1)
0 => int 1
// 出力結果
array (size=2)
0 =>
array (size=6)
'id' => int 1
'user_id' => int 1
'name' => string 'ユーザー1の本1' (length=20)
'created_at' => string '2015-09-21 22:35:41' (length=19)
'updated_at' => string '2015-09-21 22:35:41' (length=19)
'deleted_at' => null
1 =>
array (size=6)
'id' => int 2
'user_id' => int 1
'name' => string 'ユーザー1の本2' (length=20)
'created_at' => string '2015-09-21 22:35:56' (length=19)
'updated_at' => string '2015-09-21 22:35:56' (length=19)
'deleted_at' => null
テスト
これもテスト3と同じような感じですね。
$test4 = User::find($user_id)
->book
->toArray();
// SQL
string 'select * from `users` where `users`.`id` = ? limit 1' (length=52)
array (size=1)
0 => int 1
string 'select * from `book` where `book`.`deleted_at` is null and `book`.`user_id` = ? and `book`.`user_id` is not null' (length=112)
array (size=1)
0 => int 1
// 出力結果
array (size=2)
0 =>
array (size=6)
'id' => int 1
'user_id' => int 1
'name' => string 'ユーザー1の本1' (length=20)
'created_at' => string '2015-09-21 22:35:41' (length=19)
'updated_at' => string '2015-09-21 22:35:41' (length=19)
'deleted_at' => null
1 =>
array (size=6)
'id' => int 2
'user_id' => int 1
'name' => string 'ユーザー1の本2' (length=20)
'created_at' => string '2015-09-21 22:35:56' (length=19)
'updated_at' => string '2015-09-21 22:35:56' (length=19)
'deleted_at' => null
テスト5
joinを書いています。
本にユーザー情報が関連づいている形になっていますね。
ユーザーと本は1対nになっているのでこれでは逆で何度も同じユーザー情報を本情報に格納する形になってしまい、良くないですね。
$test5 = User::find($user_id)
->book()
->with('user')
->join('users', 'users.id', '=', 'book.user_id')
->get(['users.*', 'book.*'])
->toArray();
// SQL
string 'select * from `users` where `users`.`id` = ? limit 1' (length=52)
array (size=1)
0 => int 1
string 'select `users`.*, `book`.* from `book` inner join `users` on `users`.`id` = `book`.`user_id` where `book`.`deleted_at` is null and `book`.`user_id` = ? and `book`.`user_id` is not null' (length=184)
array (size=1)
0 => int 1
string 'select * from `users` where `users`.`id` in (?)' (length=47)
array (size=1)
0 => int 1
// 出力結果
array (size=2)
0 =>
array (size=10)
'id' => int 1
'name' => string 'ユーザー1の本1' (length=20)
'email' => string 'user1@example.com' (length=17)
'password' => string '*********' (length=*)
'remember_token' => null
'created_at' => string '2015-09-21 22:35:41' (length=19)
'updated_at' => string '2015-09-21 22:35:41' (length=19)
'user_id' => int 1
'deleted_at' => null
'user' =>
array (size=5)
'id' => int 1
'name' => string 'ユーザー1' (length=13)
'email' => string 'user1@example.com' (length=17)
'created_at' => string '2015-09-21 22:35:20' (length=19)
'updated_at' => string '2015-09-21 22:35:20' (length=19)
1 =>
array (size=10)
'id' => int 2
'name' => string 'ユーザー1の本2' (length=20)
'email' => string 'user1@example.com' (length=17)
'password' => string '*********' (length=*)
'remember_token' => null
'created_at' => string '2015-09-21 22:35:56' (length=19)
'updated_at' => string '2015-09-21 22:35:56' (length=19)
'user_id' => int 1
'deleted_at' => null
'user' =>
array (size=5)
'id' => int 1
'name' => string 'ユーザー1' (length=13)
'email' => string 'user1@example.com' (length=17)
'created_at' => string '2015-09-21 22:35:20' (length=19)
'updated_at' => string '2015-09-21 22:35:20' (length=19)
終えて
まだメソッドチェーンでクエリを作っていくのには抵抗がありますが色んなメソッドがあって面白いです。
今回は出力を分かりやすいようにtoArray()を行っていますが、実コードでは下記のように扱っていくのかなと思います。
$test1 = User::with('book')
->find($user_id);
echo "<p>登録者:" . $test1->name . "</p>";
foreach($test1->book as $book) {
echo "<p>本の名前:" . $book->name . "</p>";
}