Laravel 6.x エラー Property [カラム名] does not exist on this collection instance. リレーションでのエラー


制作環境

Windows 10
Laravel : 6.18.35
Laravel/ui : 1.0
Laravel-mix : 5.0.1
Bootstrap : 4.0.0
MDBootstrap : 4.19.1
chart.js : 2.9.3
XAMPP
PHP : 7.4.3
Visual Studio Code

はじめに

この記事はプログラミングをはじめたばかりの素人が、できたことをメモするのに利用しています。
内容には誤りがあるかもしれません。

独学でLaravelを勉強中なのですが、エラーでちょっとはまったので、解決方法とエラーの理由をまとめておきたいと思います。
当方は2月から2ヶ月間CodeCampのWebマスターコースを受講しました。
その最後の提出課題であるECサイトの作成を、個人的な要件を加えなら練習の為にLaravelで作成してみることにしました。
その最中に出たエラーです。

やりたい事

商品情報の入ったテーブルと、在庫数のテーブルから、全商品の情報とそれに紐付く在庫数を抽出して一覧表示する。

現在全体の形を作っているだけなので、テーブルは作成していますが中身は空です。

ここで使用するテーブル

Itemsテーブル

カラム名 項目
id 商品ID
product_name 商品名
price 価格
img 商品画像のパス
status 公開フラグ
created_at 作成日時
updated_at 更新日時

Stocksテーブル

カラム名 項目
id 在庫ID
item_id 商品ID
stock 在庫数
created_at 作成日時
updated_at 更新日時

リレーション

関係は1対1になっています。
1対1はあまり使われていないようですが、CodeCampの内容に合わせています。
正直在庫数だけ分けている事に意味を感じないのですが、練習が目的なのでそのまま続けます。

エラー発生状況

管理者側の商品一覧ページを作成中に発生。
一覧に表示する内容としては、商品名、価格、商品画像、公開フラグ、在庫数を使用します。

下記はページのイメージで、商品一覧の下にテーブルで表示予定です。

エラー発生時にやっていたこと

各モデルに以下のように記述していました。

Item.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use App\Models\Stock;
// 必ず追記
use Illuminate\Database\Eloquent\Relations\HasOne;

class Item extends Model
{

    public function stock()
    {
        return $this->hasOne(Stock::class);
        // useを使用しないなら以下の書き方でもOK
        // return $this->hasOne('App\Models\Stock');
    }

}
Stock.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use App\Models\Item;
// 必ず追記
use Illuminate\Database\Eloquent\Relations\BelongsTo;

class Stock extends Model
{
    protected $primaryKey = 'item_id';

    public function item()
    {
        return $this->belongsTo(Item::class);
    }
}

コントローラーには以下を記述(一部記述省略)。

AdminHomeController.php
use App\Models\Item;

    public function index()
    {
        $data = Item::all()->stock;
        return view('admin.home', compact('data'));
    }

この状態でphp artisan serve で確認をした所、

エラー発生。

エラーの原因

コレクションインスタンスの中にstockが存在しない。
コントローラの記述に問題あり。

AdminHomeController.php
$data = Item::all()->stock;

この記述だと、Item::all()で取得したコレクションインスタンスの中からstockを引っ張り出そうとしている。

問題解決の為やってみたことと確認したこと

記述を以下に変更してみた(1)

記述を以下に変更

AdminHomeController.php
$data = Item::all();

結果:特に問題無く表示されるが、これでは在庫数が取れないので意味なし。ただ、テーブルにはアクセスできてるよう。

tinkerで確認

結果:テーブルに値が無いのでデータは空だが、Item::all();とStock::all();とする分にはコレクションが返ってきている。

リレーションの記述方法を色々と検索

find()を使用した記述は沢山あるが、それだと一つの商品の在庫数しかとれないので問題あり。

色々検索する中でwithを使う方法を発見。
参考サイト
https://qiita.com/June8715/items/4e0a8a64d51072af326c
https://www.yoheim.net/blog.php?q=20181104

記述を以下に変更してみた(2)

AdminHomeController.php
$data = Item::with('stock')->all();

結果:この記述は、やりたい事は伝わりそうだが間違い。

解決策

記述を以下に変更。

AdminHomeController.php
$data = Item::with('stock')->get();

結果:エラー解消。
ただ、実際にデータを入れて検証していないので、不具合があるかも。
今は形だけ作成しているので、そこはおいおい確認することに。

今回のイメージ

データ取得->コレクション化->リレション先のデータ取得 = エラー!

データ取得->リレション先のデータ取得->コレクション化 = OK!