STEP10：Laravel5.7 でリレーションしたユーザ情報も一緒に取得する

LaravelのEloquentで設定したリレーションを使ってユーザ情報も一緒に取得してみます

この連載記事で給与明細を管理する機能を追加するので
給与系のテーブルとユーザ情報を[リレーション](https://readouble.com/laravel/5.7/ja/eloquent-relationships.html)する
　※ LaravelのEloquentで設定するbelongsToやhasMany

給与系の情報を取得する際に定義したリレーションを使ってユーザ情報も一緒に取得してみます




環境設定他関連記事はこちら
[Laravel + Vue + Vuetify で業務サイト作ってみる](https://qiita.com/nobu-maple/items/d1e7170d62ab07890a7a)


給与情報の登録はCSVのアップロードで行う
１CSVファイルの1行で１人分の給与明細とする
アップロードされた給与明細情報はすべてデータベースへ保存して、データベースの情報から動的にPDFを生成して給与明細として表示を行うようにしてみます

ということでデータベースにはアップロードしたCSVの情報を保存するテーブルと、CSV内の１行毎の給与明細の詳細情報を保存するテーブルを作成





# 給与明細の詳細情報（Payslipテーブル）

毎月の個人の給与明細情報を管理するテーブル

まずはテーブルとモデルのひな形を作成

```shell
$ php artisan make:model Payslip  --migration
Model created successfully.
Created Migration: yyyy_mm_dd_hhmmss_create_payslips_table
```

ファイル名の日時部分(yyyy_mm_dd_hhmmss)は実行毎に変わると思います


## Payslips テーブルを作成

```php:database/migrations/yyyy_mm_dd_hhmmss_create_payslips_table.php
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreatePayslipsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('payslips', function (Blueprint $table) {
            $table -> increments('id');
            $table -> integer('csv_id') -> unsigned() -> comment('CSV_ID');
            $table -> integer('line') -> unsigned() -> comment('CSV行番号');
            $table -> char('ym', 6) -> comment('明細年月：yyyymm');
            $table -> char('status', 1) -> default('0') -> comment('状態：0:有効  9:削除');
            $table -> integer('user_id') -> nullable() -> unsigned() -> comment('対象者ID（内部ID）');
            $table -> string('loginid') -> comment('対象者ログインID');
            $table -> text('data') -> comment('CSV行データ');
            $table -> string('filename') -> nullable() -> comment('ファイル名');
            $table -> integer('download') -> unsigned() -> default(0) -> comment('ユーザダウンロード回数');
            $table -> string('error') -> nullable() -> comment('CSVエラー内容');
            $table -> integer('delete_user_id') -> nullable() -> unsigned() -> comment('削除操作者ID');
            $table -> timestamps();
            $table -> softDeletes();

            $table -> index('csv_id');
            $table -> index('user_id');
            $table -> index('loginid');
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('payslips');
    }
}
```


## Payslip モデルを作成

### 暗号化

給与明細のデータは暗号化してDBに保存しておきたいので保存時に自動的に暗号化するようにしておきます
これで、生データベースを抜かれたとしても暗号化されてるので安心。。かな？

でもデータを利用するときに暗号化したままだとめんどくさいので正規の手続きで（モデルを使って）取得したら自動的に複合化するようにもしておきます

この辺はgetter（アクセサ）、setter（ミューテータ）をモデルに設定しとくことで実現可能
https://readouble.com/laravel/5.7/ja/eloquent-mutators.html


### ユーザ情報とのリレーション

各明細情報は必ず？ユーザに紐付くはずなので、ユーザ情報とのリレーションを belongsTo で定義しておく
さらにユーザ情報から一番利用する「氏名」情報は明細情報と一緒に扱えるように定義もしておいて、一緒に JSON で取り出せるようにしておきます
　※ appends や visible や getNameAttribute


### 論理削除

ついでに明細は社員（利用者）が自分で削除もできるようにしたいので論理削除(SoftDeletes)を有効にしときます


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

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Crypt;

class Payslip extends Model
{
    // 論理削除有効化
    use SoftDeletes;
    protected $dates = ['deleted_at'];


    // Jsonに追加で含める
    protected $appends = ['name'];


    // カラム暗号化 - 明細情報は暗号化して保存する
    public function setDataAttribute($value)
    {
        $this->attributes['data'] = Crypt::encrypt( serialize($value) );
    }


    // カラム複合化 - 明細情報を取り出すときに複合化する
    public function getDataAttribute($value)
    {
        return unserialize( Crypt::decrypt($value) );
    }


    // ユーザの氏名を取得 -- リレーションできなかった場合は空文字を返す（CSVエラーで関連付かなかった場合）
    public function getNameAttribute()
    {
        $user = $this -> user;
        if ($user) return $user -> name;
        else       return '';
    }

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'csv_id',    // CSV_ID
        'line',      // CSV行番号
        'ym',        // 明細年月：yyyymm
        'status',    // 状態：0:有効  9:削除
        'user_id',   // 対象者ID（内部ID）
        'loginid',   // 対象者ログインID
        'data',      // CSV行データ
        'filename',  // ファイル名 - 明細のファイル名を変更する場合に指定
        'download',  // ユーザダウンロード回数
        'error',     // CSVエラー内容
    ];

    // Json に出力する項目
    protected $visible = [
        'csv_id',    // CSV_ID
        'line',      // CSV行番号
        'ym',        // 明細年月：yyyymm
        'status',    // 状態：0:有効  9:削除
        'user_id',   // 対象者ID（内部ID）
        'loginid',   // 対象者ログインID
        'data',      // CSV行データ
        'filename',  // ファイル名 - 明細のファイル名を変更する場合に指定
        'download',  // ユーザダウンロード回数
        'error',     // CSVエラー内容
        'name',      // App\User の Name
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
    ];

    public function user()
    {
        return $this -> belongsTo('App\User', 'user_id', 'id');
    }
}

```




# 給与明細の登録情報（CsvPayslipsテーブル）

給与明細の登録はCSVファイルのアップロードで行うので、アップロード情報を管理するテーブルを作成
CSVアップしたあと、管理者が「公開」操作を行うまでは社員に明細情報を見せないようにフラグも設定しておく
　－こうしとかないとアップ直後からチェックもしてない状態で社員が明細表示できるようになっちゃうから。。
　－CSV作成時点でちゃんとチェックしとけば良いのだろうけど。。



まずはテーブルとモデルのひな形を作成


```shell
$ php artisan make:model CsvPayslip  --migration
Model created successfully.
Created Migration: yyyy_mm_dd_hhmmss_create_csv_payslips_table
```

ファイル名の日時部分(yyyy_mm_dd_hhmmss)は実行毎に変わると思います


## CsvPayslips テーブルを作成

```php:database/migrations/yyyy_mm_dd_hhmmss_create_csv_payslips_table.php
<?php

use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateCsvPayslipsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('csv_payslips', function (Blueprint $table) {
            $table->increments('id');
            $table -> char('ym', 6) -> comment('明細年月：yyyymm');
            $table -> char('status', 1) -> default('0') -> comment('状態：0:非公開  1:公開');
            $table -> string('filename') -> comment('CSVファイル名');
            $table -> text('header') -> comment('CSVヘッダー');
            $table -> integer('line') -> unsigned() -> default(0) -> comment('対象者数（CSV行数）');
            $table -> integer('error') -> unsigned() -> default(0) -> comment('エラー数（CSV行数）');
            $table -> datetime('published_at') -> nullable() -> comment('公開日時');
            $table -> timestamps();
            $table -> softDeletes();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('csv_payslips');
    }
}
```


## CsvPayslip モデルを作成

給与明細のCSVアップロードファイルを管理する
こちらも同じく暗号化を組み込み
一応削除も可能としておくために論理削除(SoftDeletes)を有効にしときます


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

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Crypt;

class CsvPayslip extends Model
{
    // 論理削除有効化
    use SoftDeletes;
    protected $dates = ['deleted_at'];


    // カラム暗号化 - ヘッダー情報は暗号化して保存する
    public function setHeaderAttribute($value)
    {
        $this->attributes['header'] = Crypt::encrypt(serialize($value));
    }


    // カラム複合化
    public function getHeaderAttribute($value)
    {
        return unserialize( Crypt::decrypt($value) );
    }


    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'ym',        // 明細年月：yyyymm
        'status',    // 状態：0:非公開  1:公開
        'filename',  // CSVファイル名
        'header',    // CSVヘッダー
        'line',      // 対象者数
        'error',     // CSVエラー行数
        'published_at',  // 公開日時
    ];

    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
    ];
}
```


# ユーザモデルのほうにもリレーション定義

ユーザからは複数の明細情報にリレーションできるように hasMany を定義しときます
さらに現在の App/User は認証系の設定になっていて「モデル」になってなかったので「モデル」として使えるように定義も変更しておきます

こちらの記事が大変参考になりました
[Laravel標準の認証用UserをModelとして使いたい](https://www.tabakazu.com/posts/5)　https://www.tabakazu.com/posts/5
　というかそのままです。ありがとうございました！

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

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Authenticatable;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Foundation\Auth\Access\Authorizable;

class User extends Model implements AuthenticatableContract, AuthorizableContract
{
    use Authenticatable, Authorizable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'name',
        'loginid',   // 対象者ログインID
        'password',
        'role',
    ];


    /**
     * The attributes that should be hidden for arrays.
     *
     * @var array
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    public function payslips()
    {
        return $this -> hasMany('App\Payslip');
    }
}
```


# 明細情報の取得時に氏名も一緒に取得する

以上で設定系は完了
明細情報（Payslip）側に appends や visible や getNameAttribute を定義しているので特に意識することなく氏名情報も一緒に取得できるようになってます

### appends と visible

```php:app/Payslip.php
～～～
    protected $appends = ['name'];
～～～
    protected $visible = [
～～～
        'name',     // App\User の Name 
～～～

```

name を追加しているので JSON の取得時に一緒に取得される

### getNameAttribute

name を実際にどうやって取得するかというと User を呼び出して Name だけを返している
しかし、リレーションで使う user_id はCSVの情報から取得してくるのでCSVが間違っていてユーザが実在しないこともある
そんな時はただの空文字を返すようにしておく

$this -> user は何かというと belongsTo で定義した App\User モデル

```php:app/Payslip.php
～～～
    // ユーザの氏名を取得 -- リレーションできなかった場合は空文字を返す（CSVエラーで関連付かなかった場合）
    public function getNameAttribute()
    {
        $user = $this -> user;
        if ($user) return $user -> name;
        else       return '';
    }
～～～
```

ここまでやっているので、実際に使う際は

```php
use App\Payslip;

～～
    $Payslips = Payslip::where($where) -> get();

    return ['data' => $Payslips];

```

こんな感じで利用すると、ブラウザ側ではPayslipテーブルでは実際には持っていない氏名（name）も入った状態でデータを受け取れる







---


以上




今回のソースはこちら
https://github.com/u9m31/u9m31/tree/step10