Laravelのリレーション、Eloquentについて


#Eloquentについて
データベースとモデルを紐つける機能



#1対1

##例題

今回はUsersとProfilesテーブルの連携について考えます。
ユーザーを管理するテーブルにはidとnameカラムがあります。
プロフィールを管理するテーブルにはidとuser_idとurlがあります。
1対１のリレーションでは1人のユーザーは１つのプロフィールを持っていることが前提となります。



#Userモデルの作成

```
$ php artisan make:model User -m
Model created successfully.
Created Migration: 2020_01_14_084605_create_users_table
```



```php:database/migrations/2020_01_14_084605_create_users_table
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->bigIncrements('id');
+           $table->string('name');
            $table->timestamps();
        });
    }
```

#Profileモデルの作成

```
$ php artisan make:model Profile -m
Model created successfully.
Created Migration: 2020_01_14_090207_create_profiles_table
```

```php:database/migrations/
    public function up()
    {
        Schema::create('profiles', function (Blueprint $table) {
            $table->bigIncrements('id');
+           $table->integer('user_id');
+           $table->string('url');
            $table->timestamps();
        });
    }
```

##ポイント
UserモデルにhasOneを定義

テーブル間の関係はuserテーブルのidをprofileテーブルのuser_idで紐つける。


##Userモデルに定義する

```app/User.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
  public function profile() {
    return $this->hasOne(Profile::class);
  }
}
```

#Profileモデルに定義する

```php:app/Profile.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Profile extends Model
{
    public function user() {
      return $this->belongsTo(User::class);
    }
}
```

##データベースにデータを挿入する。

###DatabaseSeeder.phpを編集

```php:database/seeds/DatabseSeeder.php
    public function run()
    {
       $this->call(UsersTableSeeder::class);
       $this->call(ProfilesTableSeeder::class);
    }
```

###UsersTableSeeder.php

```php:database/seeds/UsersTableSeeder.php
```

###ProfilesTableSeeder.php

```php:database/seeds/ProfilesTableSeeder.php
```

#tinkerで確認

```
$ php artisan tinker
Psy Shell v0.9.12 (PHP 7.3.13 — cli) by Justin Hileman
>>> $user = User::find(1);
[!] Aliasing 'User' to 'App\User' for this Tinker session.
=> App\User {#2980
     id: 1,
     name: "Enomoto Riko",
     created_at: "2020-01-14 09:13:35",
     updated_at: "2020-01-14 09:13:35",
   }
>>> $user->profile;
=> App\Profile {#2983
     id: 4,
     user_id: 1,
     url: "https://1",
     created_at: "2020-01-14 09:22:02",
     updated_at: "2020-01-14 09:22:02",
   }
>>> 
```

#1対多

例）ユーザーを管理しているuserテーブルとユーザーが注文したNoを管理しているordersテーブルで考えてみる。ユーザーはたくさんの注文をしており、注文は整数のNoで管理されている。

##Orderモデルの作成

```
php artisan make:model Order -m
Model created successfully.
Created Migration: 2020_01_23_124628_create_orders_table
```

##odersテーブルを作成

```php:database/migrations/2020_01_23_124628_create_orders_table.php
    public function up()
    {
        Schema::create('orders', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->integer('user_id');
            $table->integer('order_no');
            $table->timestamps();
        });
    }
```

```
$ php artisan migrate
Migrating: 2020_01_23_124628_create_orders_table
Migrated:  2020_01_23_124628_create_orders_table (0.03 seconds)
```


#seederファイルの作成

```
php artisan make:seeder OrdersTableSeeder  
Seeder created successfully.
```

##OrdersTableSeederの編集

```php:database/seeds/OrdersTableSeeder
    public function run()
    {
      DB::table('orders')->insert([
      ['user_id' => 3,'order_no' => '00001','created_at' => new Datetime(),'updated_at' => new Datetime()],
      ['user_id' => 3,'order_no' => '00002','created_at' => new Datetime(),'updated_at' => new Datetime()],
      ['user_id' => 4,'order_no' => '00003','created_at' => new Datetime(),'updated_at' => new Datetime()],
      ['user_id' => 2,'order_no' => '00004','created_at' => new Datetime(),'updated_at' => new Datetime()],
      ['user_id' => 1,'order_no' => '00005','created_at' => new Datetime(),'updated_at' => new Datetime()],
      ['user_id' => 3,'order_no' => '00006','created_at' => new Datetime(),'updated_at' => new Datetime()],
      ['user_id' => 5,'order_no' => '00007','created_at' => new Datetime(),'updated_at' => new Datetime()],
      ['user_id' => 1,'order_no' => '00008','created_at' => new Datetime(),'updated_at' => new Datetime()],
      ['user_id' => 4,'order_no' => '00009','created_at' => new Datetime(),'updated_at' => new Datetime()],
      ['user_id' => 5,'order_no' => '00010','created_at' => new Datetime(),'updated_at' => new Datetime()],
    ]);

    }
```

##DatabaseSeeder.phpの編集

##seederの実行


#tinkerで確認

```
$user = User::find(1);
=> App\User {#2973
     id: 1,
     name: "Enomoto",
     created_at: "2020-01-23 13:15:07",
     updated_at: "2020-01-23 13:15:07",
   }
>>> $user->order;
=> Illuminate\Database\Eloquent\Collection {#3001
     all: [
       App\Order {#2998
         id: 5,
         user_id: 1,
         order_no: "00005",
         created_at: "2020-01-23 13:15:07",
         updated_at: "2020-01-23 13:15:07",
       },
       App\Order {#2997
         id: 8,
         user_id: 1,
         order_no: "00008",
         created_at: "2020-01-23 13:15:07",
         updated_at: "2020-01-23 13:15:07",
       },
     ],
   }

```

#多対多

例）ユーザーテーブルとスキルテーブルで確認します。
スキルテーブルにはプログラミングの言語が入っています。
あるユーザは複数のプログラミングを扱えるとします。

#Skillモデルを作成します。

```
$ php artisan make:model Skill -m
Model created successfully.
Created Migration: 2020_01_24_134708_create_skills_table
```

##skillsテーブルの作成

```
    public function up()
    {
        Schema::create('skills', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('language');
            $table->timestamps();
        });
    }
```

##migration実行

```
$ php artisan migrate
Migrating: 2020_01_24_134708_create_skills_table
Migrated:  2020_01_24_134708_create_skills_table (0.03 seconds)
```

##seederファイルの作成

```
$ php artisan make:seeder SkillsTableSeeder
Seeder created successfully.
```

##seederを実行する

```php:database/seeds/DatabaseSeeder.php
    public function run()
    {
       // $this->call(UsersTableSeeder::class);
       // $this->call(ProfilesTableSeeder::class);
       // $this->call(OrdersTableSeeder::class);
       $this->call(SkillsTableSeeder::class);
    }
```

###実行

```
$ php artisan db:seed
Seeding: SkillsTableSeeder
Database seeding completed successfully.
```

#中間テーブルuser_skillの作成

##migrationファイルの作成

```
$ php artisan make:migration create_user_skill_table
Created Migration: 2020_01_25_012117_create_user_skill_table
```

##user_skillテーブルの作成

```php:database/migrations/2020_01_25_012117_create_user_skill_table
    public function up()
    {
        Schema::create('user_skill', function (Blueprint $table) {
            $table->bigInteger('user_id')->unsigned();
            $table->bigInteger('skill_id')->unsigned();
            $table->primary(['user_id','skill_id']);

            //外部キー制約
            $table->foreign('user_id')
                ->references('id')
                ->on('users')
                ->onDelete('cascade');
            //
            $table->foreign('skill_id')
                ->references('id')
                ->on('skills')
                ->onDelete('cascade');

        });
    }
```

##migrateの実行

##seeder関連

```php:database/seeds/UserSkillTableSeeder
    public function run()
    {
      DB::table('skill_user')->insert([
     ['user_id' => 1, "skill_id" => 1],
     ['user_id' => 1, "skill_id" => 3],
     ['user_id' => 2, "skill_id" => 2],
     ['user_id' => 2, "skill_id" => 3],
     ['user_id' => 3, "skill_id" => 4],
     ['user_id' => 4, "skill_id" => 5],
     ['user_id' => 5, "skill_id" => 1],
   ]);

    }
```

#モデル関連の修正

##Userモデル編集

```php:app/User.php
  public function skills() {
    return $this->belongsToMany(Skill::class);
  }
```

##Skillモデルの編集

```php:app/Skill.php
  public function users() {
    return $this->belongsToMany(User::class);
  }
```


#tinkerで確認

```
$ php artisan tinker
Psy Shell v0.9.12 (PHP 7.3.13 — cli) by Justin Hileman
>>> $skill = Skill::find(1);
[!] Aliasing 'Skill' to 'App\Skill' for this Tinker session.
=> App\Skill {#2980
     id: 1,
     language: "HTML",
     created_at: "2020-01-25 03:15:24",
     updated_at: "2020-01-25 03:15:24",
   }
>>> $skill->users;
=> Illuminate\Database\Eloquent\Collection {#2981
     all: [
       App\User {#2984
         id: 1,
         name: "Enomoto",
         created_at: "2020-01-25 03:15:24",
         updated_at: "2020-01-25 03:15:24",
         pivot: Illuminate\Database\Eloquent\Relations\Pivot {#2983
           skill_id: 1,
           user_id: 1,
         },
       },
       App\User {#2988
         id: 5,
         name: "Sugihara",
         created_at: "2020-01-25 03:15:24",
         updated_at: "2020-01-25 03:15:24",
         pivot: Illuminate\Database\Eloquent\Relations\Pivot {#2986
           skill_id: 1,
           user_id: 5,
         },
       },
     ],
   }
>>> 
```

#番外編：id以外をprimary_keyにするuserのnameで紐つける

##usersテーブルのmigrationファイルを修正

```php:database/migrations/2020_01_14_084605_create_users_table.php
    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            // $table->bigIncrements('id');
            $table->string('name')->primary();
            $table->timestamps();
        });
    }
```

##profilesテーブルのmigrationファイルを修正

```php:database/migrations/2020_01_14_090207_create_profiles_table.php
    public function up()
    {
        Schema::create('profiles', function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->string('user_name');
            $table->string('url');
            $table->timestamps();
        });
    }
```

ポイント：テーブル名_プライマリーキー→user_name


##Userモデルを編集

```php:app/User.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
  protected $primaryKey = "name";
  protected $keyType = "string";
  public $incrementing = false;

  public function profile() {
    return $this->hasOne(Profile::class);
  }
}
```
###protected $primaryKey = "name";
nameカラムをプライマリーキーに設定
###public $incrementing = false;
1づつ増えていく整数にしない場合

##データベースにデータを挿入

###usersテーブルに挿入するデータ
```php:database/seeds/UsersTableSeeder.php
    public function run()
    {
      DB::table('users')->insert([
     ['name' => 'Enomoto','created_at' => new Datetime(),'updated_at' => new Datetime()],
     ['name' => 'Amano','created_at' => new Datetime(),'updated_at' => new Datetime()],
     ['name' => 'Shimamura','created_at' => new Datetime(),'updated_at' => new Datetime()],
     ['name' => 'Hirai','created_at' => new Datetime(),'updated_at' => new Datetime()],
     ['name' => 'Sugihara','created_at' => new Datetime(),'updated_at' => new Datetime()],
   ]);
  }
}
```

###profilesテーブルにデータを挿入

```php:database/seeds/ProfilesTableSeeder.php
    public function run()
    {
      DB::table('profiles')->insert([
      ['user_name' => 'Amano','url' => "https://AmanoYasushi",'created_at' => new Datetime(),'updated_at' => new Datetime()],
      ['user_name' => 'Sugihara','url' => "https://SugiharaGensei",'created_at' => new Datetime(),'updated_at' => new Datetime()],
      ['user_name' => 'Enomoto','url' => "https://EnomotoRiko",'created_at' => new Datetime(),'updated_at' => new Datetime()],
      ['user_name' => 'Shimamura','url' => "https://ShimamuraMayu",'created_at' => new Datetime(),'updated_at' => new Datetime()],
      ['user_name' => 'Hirai','url' => "https://HiraiHana",'created_at' => new Datetime(),'updated_at' => new Datetime()],
    ]);
   }
}
```

##tinkerで確認
```
$ php artisan tinker
Psy Shell v0.9.12 (PHP 7.3.13 — cli) by Justin Hileman
>>> $user = User::find('Amano');
[!] Aliasing 'User' to 'App\User' for this Tinker session.
=> App\User {#2980
     name: "Amano",
     created_at: "2020-01-22 11:54:31",
     updated_at: "2020-01-22 11:54:31",
   }
>>> $user->profile;
=> App\Profile {#2983
     id: 1,
     user_name: "Amano",
     url: "https://AmanoYasushi",
     created_at: "2020-01-22 11:54:31",
     updated_at: "2020-01-22 11:54:31",
   }
```

#中間テーブルへの書き込みと削除について

#attach関数を使う

##$userにidが1のユーザーを代入する。

```
>>> $user = User::find(1);
=> App\User {#2998
     id: 1,
     name: "Enomoto",
     created_at: "2020-01-25 03:15:24",
     updated_at: "2020-01-25 03:15:24",
   }
```

#userとskillの中間テーブルに書き込む

- user_idが1、skill_idが１
- user_idが1、skill_idが2
- user_idが1、skill_idが3
にするために、$skillsに配列で1,2,3を渡す


```
>>> $skills = array(1,2,3);
=> [
     1,
     2,
     3,
   ]
```

##attach関数を使って、中間テーブルに書き込む

```
>>> $user->skills()->attach($skills);
=> null
```
##中間テーブルでuser_idの1と紐ついている物を出力する。

```
>>> $user->skills;
=> Illuminate\Database\Eloquent\Collection {#3016
     all: [
       App\Skill {#2990
         id: 1,
         language: "HTML",
         created_at: "2020-01-25 03:15:24",
         updated_at: "2020-01-25 03:15:24",
         pivot: Illuminate\Database\Eloquent\Relations\Pivot {#2994
           user_id: 1,
           skill_id: 1,
         },
       },
       App\Skill {#3013
         id: 2,
         language: "CSS",
         created_at: "2020-01-25 03:15:24",
         updated_at: "2020-01-25 03:15:24",
         pivot: Illuminate\Database\Eloquent\Relations\Pivot {#2991
           user_id: 1,
           skill_id: 2,
         },
       },
       App\Skill {#3031
         id: 3,
         language: "javascript",
         created_at: "2020-01-25 03:15:24",
         updated_at: "2020-01-25 03:15:24",
         pivot: Illuminate\Database\Eloquent\Relations\Pivot {#3028
           user_id: 1,
           skill_id: 3,
         },
       },
     ],
   }
>>> 
```


#detachメソッドを使って、user_idが1と紐ついているカラムを削除する

```
>>> $user->skills()->detach();
=> 5
>>> $user = user::find(1);
=> App\User {#3015
     id: 1,
     name: "Enomoto",
     created_at: "2020-01-25 03:15:24",
     updated_at: "2020-01-25 03:15:24",
   }
```
##user_idが1のものが削除されていることを確認する。

```
>>> $user->skills;
=> Illuminate\Database\Eloquent\Collection {#2987
     all: [],
   }
```


###$user = User::find('Amano');
プライマリーキーのnameをAmanoで検索
###$user->profile;
profilesテーブルからnameの"Amano"に該当する外部キーuser_nameからデータを取得