【Laravel】リレーション先のリレーションのデータの表示の仕方

##0.はじめに

本記事ではLaravel 5.8 での開発で得たものを取り上げております。

なぜ本記事を作成しようかと思ったのか、

Laravelの学習で ORM を用いてECサイトの注文履歴の表示画面を作っていた時に、
「リレーション先のさらに先」の情報を取得して表示させる必要がありましたが、

「データベースからの情報取得方法」を書いた記事は多く見つけられるものの、
「取得したデータの "表示" の仕方」についての記事はあまり見つけられなかったからです。
(探し方が良くなかったのかもしれませんが^^;)

「dd()で取得したレコードの確認までは出来ているが、表示させるのにエラーが出てうまくいかない！」
自分はこれで時間をだいぶ消費してしまいましたので、他の初学者がよりスムーズに開発の学習が行えるよう記事を作成いたしました。

おかしい所がありましたら、ぜひご指摘いただければと思います。

##1.注文履歴表示画面に関わる情報

###・ER図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/950917/6879e541-9919-b2ff-6d02-1edfa695db92.png)


###・各モデル

```PHP:Order.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Order extends Model
{
    protected $table = 't_orders';

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

    public function orderDetails()
    {
        return $this->hasMany('App\OrderDetail' , 'order_id');
    }
}
```
```PHP:OrderDetail.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class OrderDetail extends Model
{
    protected $table = 't_order_details';

    public $timestamps = false;
    
    protected $fillable = [
        'product_id',
        'order_id',
        'shipment_status_id',
        'order_quantity',
    ];

    public function order()
    {
        return $this->belongsTo('App\Order','order_id');
    }

    public function shipmentStatuses()
    {
        return $this->belongsTo('App\ShipmentStatus', 'shipment_status_id');
    }

    public function products()
    {
        return $this->belongsTo('App\Product', 'id');
    }
}
```
```PHP:ShipmentStatus.php
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class ShipmentStatus extends Model
{
    protected $table = 'm_shipment_statuses';

    protected $fillable = [
        'shipment_status_name',
    ];

    public function orderDetails()
    {
        return $this->hasMany('App\OrderDetail','id');
    }
}
```
```PHP:User.php
<?php

namespace App;

use Illuminate\Notifications\Notifiable;
use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Foundation\Auth\User as Authenticatable;

class User extends Authenticatable
{
    use Notifiable;
    protected $table = 'm_users';
    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'last_name',
        'first_name',
        'zipcode',
        'prefecture',
        'municipality',
        'address',
        'apartments',
        'email',
        'phone_number',
        'password',
        'company_name'
    ];

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

    /**
     * The attributes that should be cast to native types.
     *
     * @var array
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    public function orders()
    {
        return $this->hasMany('App\Order');
    }

    public function products()
    {
        return $this->hasMany('App\Product', 'user_id');
    }
}
```

**※Laravelの認証機能を使用しています。
(この機能を利用してログイン中のユーザーIDを取得し注文情報の検索を行います。)**

###・コントローラー

```PHP:OrderController
class OrderController extends Controller
{
    public function getHistory (Request $request)
    {
        //ログイン中のユーザーIDをもとに注文情報を取得する
        $orderInformations = Order::where('user_id', Auth::id())
            ->with(['user', 'orderDetails.shipmentStatuses'])
            ->orderBy('order_date', 'desc')
            ->paginate(3);
```

###・現在データベースに収容されている情報(各seederより抜粋)

```PHP:t_ordersテーブル
        DB::table('t_orders')->insert([
            [
                'user_id' => 1,
                'order_number' => 1,
                'order_date' => date('2020-10-22 13:55:19'),
            ],
            [
                'user_id' => 1,
                'order_number' => 2,
                'order_date' => date('2020-05-03 16:32:52'),
            ],
```
```PHP:t_order_detailsテーブル
        DB::table('t_order_details')->insert([
            [
                'products_id' => 1,
                'order_id' => 1,
                'shipment_status_id' => 2,
                'order_quantity' => 1,
                'shipment_date' => date('2020-10-23 20:55:19'),
            ],
            [
                'products_id' => 2,
                'order_id' => 1,
                'shipment_status_id' => 2,
                'order_quantity' => 2,
                'shipment_date' => date('2020-10-23 20:55:19'),
            ],
            [
                'products_id' => 3,
                'order_id' => 2,
                'shipment_status_id' => 1,
                'order_quantity' => 3,
                'shipment_date' => date('Y-m-d H:i:s'),
            ],
```
```PHP:m_shipment_statusesテーブル
        DB::table('m_shipment_statuses')->insert([
            [
                'shipment_status_name' => '準備中',
            ],
            [
                'shipment_status_name' => '発送済',
            ],
            [
                'shipment_status_name' => '入荷待ち',
            ],
            [
                'shipment_status_name' => 'キャンセル',
            ],
```
```PHP:m_usersテーブル
        DB::table('m_users')->insert([
            [
                'password' => bcrypt('sample1'),
                'last_name' => '太郎',
                'first_name' => '田中',
                'zipcode' => '1200003',
                'prefecture' => '東京都',
                'municipality' => '足立区',
                'address' => '1-1-1',
                'apartments' => '101号室',
                'email' => 'sample1@sample.com',
                'phone_number' => '11111111111',
                'user_classification_id' => '2',
                'company_name' => '株式会社A',
                'delete_flag' => '0',
            ],
```

##2.コントローラーからデータベース(以下DB)へ情報を要求

```PHP:orderController.php
class OrdersController extends Controller
{
    public function getHistory (Request $request)
    {
        //ログイン中のユーザーIDをもとに注文情報を取得する
        $orderInformations = Order::where('user_id', Auth::id())
                ->with(['user', 'orderDetails.shipmentStatuses'])
                ->orderBy('order_date', 'desc')
                ->paginate(3);
```
 
まずは、ログイン中のユーザーの注文情報があるか、注文/t_ordersテーブル の検索を行います。
検索でログイン中のIDを取得してt_ordersテーブルの user_id カラムを検索します。
その際に with() を用いて情報を一括で取得しペジネーションの形にします。

##3.取得できたデータを表示するには

データの表示について解説を行っていきます。

今回表示する注文履歴のデータは、

田中 太郎 さんが
2021-03-18 16:32:52 に商品を１個、
2020-10-23 20:55:19 に商品を２個 購入した履歴です。
最後発送日と、【 発送済 】という状態の表示まで行います。

まず、dd()を行って $orderInformations の中身を見てみましょう。
![スクリーンショット 2021-03-19 124329.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/950917/d94f88f1-9d19-5957-9f7c-6e0954e8a26a.jpeg)
``\#items:`` に値が入ってきました。
データベースからの情報の取得は成功です。

それを開いていくと..
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/950917/ea05acea-c753-3aec-c668-5a2b8a54f1d4.png)
``\#attributes:``の中にt_ordersテーブルの情報が入ってきています。
注文の履歴が２個あるので、配列で２つ並んでますね。

では例としてorder_dateを表示させます。

表示するためには一度$orderInformationsを foreach する必要があります。

```php:order_history.blade.php
@foreach ($orderInformations as $order)
    注文日時:{{ $order->order_date }}<br>
@endforeach
```
**▼ 結果**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/950917/19d364ba-8aed-4745-2367-a79850124c19.png)
表示が出来ました。

では foreach すると何が変わるのでしょうか？

``$order``の中がどうなっているのか dd() してみます。

```php:order_history.blade.php
@foreach ($orderInformations as $order)
    注文日時:{{ $order->order_date }}<br>
@endforeach

@php dd($order) @endphp
```
**▼ 結果**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/950917/3a405694-cfa7-2b7a-18e8-b9f05345d3e5.png)
``\#items:``の中身だけになり、データがモデルオブジェクトになりました。

```php
App\Order
```
この状態なら \#attributes から取り出すことができます。
それで以下の形になります。

```php
{{ $order->( #attributes の取り出したいカラム名) }}
```

では次の階層の表示に移っていきます。

m_users テーブルと、t_order_detailsテーブル の情報です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/950917/01b1c936-721e-93b9-9e73-50d4f3d8b6ea.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/950917/f05f30c6-60dc-0f2a-36ed-854f898f1307.png)

両テーブルの情報は``\#relations:``の中に入っています。
ですが、m_users テーブルと、t_order_detailsテーブルでは入り方に違いがあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/950917/875c7c6e-0b2e-989b-d177-0c0d3a271c6e.png)

``"user"``は``App\User``であるのに対し、``"orderDetails"``は``illuminat…\Collection``となっています。

上記から、

``"user"`` = **m_users テーブルの情報** は、``$order`` を ``foreach``せずとも表示することができる。

``"orderDetails"`` = **t_order_details テーブルの情報** は、``$order`` を ``foreach``しないと表示が出来ない。

と、表示するのに``foreach``の要否の違いが出ます。

では、先に手間の少ない**m_users テーブルの情報**の情報の表示から解説を行います。
今回は、ユーザーの氏名を表示してみます。

bladeでの表示方法は以下の様になります。

```php:order_history.blade.php
@foreach ($orderInformations as $order)
    注文日時:{{ $order->order_date }}<br>
@endforeach

{{ $order->user->first_name }} {{ $order->user->last_name }}
```
**▼ 結果**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/950917/b2ef4b68-6f4d-e1e2-9a5b-db5bf5a40cd5.png)
上記の方法で表示が行えます。
``\#relations:``の中に入っている情報で、このように``foreach``しなくて良いのは``App\User``のパターンです。
モデルの繋がりからいくと**親テーブルの情報**を引き出すときです。(belongsToで引っ張ってきた情報)

では、次に**t_order_details テーブルの情報**の表示の解説です。
既にやり方が浮かんできている方も多いと思います。
発送日(shipment_date)の表示を行っていきます。

```php:order_history.blade.php
@foreach ($orderInformations as $order)
    注文日時:{{$order->order_date}}<br>
@endforeach
{{ $order->user->first_name }} {{ $order->user->last_name }}<br>
@foreach ($orderInformations as $order)
@foreach ($order->orderDetails as $orderDetail)
    発送日:{{ $orderDetail->shipment_date }}<br>
@endforeach
@endforeach
```
**▼ 結果**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/950917/6b8709d7-0d39-700c-c2ab-fb36bbf1b570.png)
表示が出来ました！
``\#relations:``のなかに入っている情報を表示したくて``foreach``を行う際は、
**「``$order``の中の``orderDetails``を``foreach``」** = **``foreach ($order->orderDetails as $orderDetail)``**
というように**``$order``の中の``foreach``するものを指定してあげます。**

これが一番最初に行った ``$orderInformations``の``foreach``との違いです。

私はここでだいぶ時間を消費してしまいました^^;

ではでは、もうひとつ深い階層の表示を行ってみましょう。
次は **``m_shipment_statuses`` テーブルの情報**です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/950917/4a83325d-33bb-a3ea-0de3-3d745f30937c.png)

これまでと同じようにdd()の結果から表示のさせ方を確認していきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/950917/a8719473-26ff-20e9-ef13-afe4e4107ebf.png)

今回の``m_shipment_statuses`` テーブルの情報の場合、

```php
App\shipmentStatuses
```
モデルオブジェクトになっているので``foreach``は不要です。

では表示していきましょう。

```php:order_history.blade.php
@foreach ($orderInformations as $order)
    注文日時:{{$order->order_date}}<br>
@endforeach
{{ $order->user->first_name }} {{ $order->user->last_name }}<br>
@foreach ($orderInformations as $order)
@foreach ($order->orderDetails as $orderDetail)
    発送日:{{ $orderDetail->shipment_date }}【 {{ $orderDetail->shipmentStatuses->shipment_status_name }} 】<br>
@endforeach
@endforeach
```
**▼ 結果**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/950917/1cd4c202-a94e-3d52-a1c0-f429eb8a2f4b.png)

##まとめ

```php
illuminat…\Collection
```
**でコレクションオブジェクトになっていれば、``要foreach``**

```php
App\〇〇
```
**でモデルオブジェクトになっていれば、``foreach不要``**


以上となります。

最後までご覧頂き、誠にありがとうございました。