🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Laravel 5

Laravel 5は、PHPフレームワークLaravelの最新バージョンで、2014年11月に発表予定です。ディレクトリ構造がが現行版より大幅に変更されるほか、メソッドインジェクションやFormRequestの利用が可能になります。

Q&A

解決済

2回答

2628閲覧

PHP/Laravelで多対多リレーションの実装をしたい

kyontama

総合スコア7

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Laravel 5

Laravel 5は、PHPフレームワークLaravelの最新バージョンで、2014年11月に発表予定です。ディレクトリ構造がが現行版より大幅に変更されるほか、メソッドインジェクションやFormRequestの利用が可能になります。

0グッド

1クリップ

投稿2020/01/03 01:05

前提・実現したいこと

PHP(Laravel)でレシピサイトを作っています。
ログイン済ユーザーがレシピに対してコメントを投稿できる機能を実装中です。

前提として、
1、ユーザー情報があるUsersテーブル
2、レシピ情報があるRecipesテーブル
3、レシピに対してユーザーごとにコメントを格納するCommentsテーブル(中間テーブル)
を用意しています。

実現したいのは
・各レシピに対して、ログイン済ユーザーであればコメントを投稿できる。
・コメントの一覧はログイン状態に関係なく閲覧できる
・レシピはどのユーザーがコメントを投稿したか紐づけできる
という3つの処理ですが、現状レシピ情報画面(show.blade.php)を表示すると、下記のエラーになります。

発生している問題・エラーメッセージ

上記機能を実装するために、どういった手順で修正していくのがよろしいでしょうか。
ご教授いただきたいです、よろしくお願いします。

ErrorException (E_ERROR) Trying to get property 'id' of non-object (View: /home/ec2-user/environment/recipe-site/resources/views/recipes/show.blade.php)

該当のソースコード

users_table

php

1<?php 2 3use Illuminate\Support\Facades\Schema; 4use Illuminate\Database\Schema\Blueprint; 5use Illuminate\Database\Migrations\Migration; 6 7class CreateUsersTable extends Migration 8{ 9 public function up() 10 { 11 Schema::create('users', function (Blueprint $table) { 12 $table->increments('id'); 13 $table->string('name'); 14 $table->string('email')->unique(); 15 $table->string('password'); 16 $table->rememberToken(); 17 $table->timestamps(); 18 }); 19 } 20 /*以下略*/ 21

recipes_table

php

1<?php 2 3use Illuminate\Support\Facades\Schema; 4use Illuminate\Database\Schema\Blueprint; 5use Illuminate\Database\Migrations\Migration; 6 7class CreateRecipesTable extends Migration 8{ 9 public function up() 10 { 11 Schema::create('recipes', function (Blueprint $table) { 12 $table->increments('id'); 13 $table->integer('user_id')->unsigned()->index(); 14 $table->timestamps(); 15 16 //外部キー 17 $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); 18 }); 19 } 20 /*以下略*/ 21

user_comments.table

php

1<?php 2 3use Illuminate\Support\Facades\Schema; 4use Illuminate\Database\Schema\Blueprint; 5use Illuminate\Database\Migrations\Migration; 6 7class CreateUserCommentsTable extends Migration 8{ 9 10 public function up() 11 { 12 Schema::create('user_comments', function (Blueprint $table) { 13 $table->increments('id'); 14 $table->integer('recipe_id')->unsigned()->index(); 15 $table->integer('user_id')->unsigned()->index(); 16 $table->string('content'); 17 $table->timestamps(); 18 19 //外部キー 20 $table->foreign('recipe_id')->references('id')->on('recipes')->onDelete('cascade'); 21 $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade'); 22 }); 23 } 24 /*以下略*/

web.php

php

1<?php 2 3//レシピの詳細画面表示 4Route::resource('recipes', 'RecipesController',['only' => ['show']]); 5 6//コメントの表示機能 7Route::get('comments','CommentsController@index'); 8 9//ログイン認証を必要とするルーティンググループ 10Route::group( ['middleware' => ['auth']], function(){ 11 12 //コメント投稿のルーティング 13 Route::resource('comments','CommentsController',['only' => ['store']]); 14 15}); 16

User.php

php

1<?php 2 3namespace App; 4 5use Illuminate\Notifications\Notifiable; 6use Illuminate\Foundation\Auth\User as Authenticatable; 7 8class User extends Authenticatable 9{ 10 //UserのRecipeを取得する 11 public function recipes() 12 { 13 return $this->hasMany(Recipe::class); 14 } 15 16 //UserのCommentを取得する 17 public function comments() 18 { 19 return $this->belongsToMany(Comment::class, 'user_comments', 'user_id', 'recipe_id')->withTimestamps(); 20 } 21 22}

Recipe.php

php

1<?php 2 3namespace App; 4 5use Illuminate\Database\Eloquent\Model; 6 7class Recipe extends Model 8{ 9 10 //RecipeがどのUserに紐づいているか 11 public function user() 12 { 13 return $this->belongsTo(User::class); 14 } 15 16 public function comments() 17 { 18 return $this->belongsToMany(Comment::class, 'user_comments', 'user_id', 'recipe_id') 19 ->withTimestamps(); 20 } 21} 22

RecipesController.php

php

1<?php 2 3namespace App\Http\Controllers; 4 5use Illuminate\Http\Request; 6 7use App\Recipe;//追加 8 9class RecipesController extends Controller 10{ 11 public function show($id) 12 { 13 //レシピデータを取得 14 $recipe = Recipe::find($id); 15 //ログイン済ユーザー 16 $user = \Auth::user(); 17 18 return view('recipes.show', [ 19 'recipe' => $recipe, 20 'user' => $user, 21 ]); 22 } 23

CommentsController.php

php

1<?php 2 3namespace App\Http\Controllers; 4 5use Illuminate\Http\Request; 6use App\Recipe; 7use App\User; 8 9class CommentsController extends Controller 10{ 11 12 //コメントの一覧表示処理 13 public function index() 14 { 15 16 $data = []; 17 18 //ログイン済かどうか 19 if(\Auth::check()){ 20 //ログイン済ユーザー 21 $user = \Auth::user(); 22 //ログインユーザーごとのコメントを格納 23 $comments = $user->comments()->orderBy('created_at', 'desc'); 24 25 $data = [ 26 'user' => $user, 27 'comments' => $comments, 28 ]; 29 } 30 //コメントをレシピ詳細画面に渡す 31 return view('recipes.show', $data); 32 33 } 34 //コメントの投稿処理 35 public function store(Request $request) 36 { 37 38 $request->user()->comments()->create([ 39 'content' => $request->content, 40 ]); 41 42 return back(); 43 }

show.blade.php

php

1@extends('layouts.app') 2 3@section('content') 4 <h1>レシピID{{ $recipe->id }}の詳細ページ</h1> 5 <!--ログインユーザーのみ投稿可能--> 6 @if(Auth::id() == $user->id) 7 {!! Form::open(['route' => 'comments.store']) !!} 8 <div class="form-group"> 9 {!! Form::textarea('content',['class' => 'form-control']) !!} 10 {!! Form::submit('投稿', ['class' => 'btn btn-primary btn-block']) !!} 11 </div> 12 {!! Form::close() !!} 13 @endif 14 <!--投稿の表示処理--> 15 @if(count($comments) > 0) 16 @foreach($comments as $comment) 17 <p>{!! $comment->content !!}</p> 18 @endforeach 19 @endif 20@endsection

試したこと

多対多リレーションはこちらを参考にしました。
https://readouble.com/laravel/5.5/ja/eloquent-relationships.html#updating-many-to-many-relationships

補足情報(FW/ツールのバージョンなど)

PHP 7.2.22
Laravel 5.5

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

エラーメッセージについて

@if(Auth::id() == $user->id)

となるとおもいますが、ログインしてなければ $user は null なのでエラーとなる。

@if(Auth::user())

やら

@if(Auth::user() != null)

でログイン判定すべきかと


他対他

create table roles ( id int /*略*/ ) create table users ( id int /*略*/ ) create table user_roles ( user_id int, role_id int ./*略*/ )

という中間テーブルをはさむ形式が[他対他] の形式となります。

モデル側は「公式ドキュメント」に書かれてる通りの設定になります。

マイグレーション側は以下のような形式になるかと

Schema::create('roles', function (Blueprint $table) { $table->increments('id')/*略*/
Schema::create('users', function (Blueprint $table) { $table->increments('id')/*略*/
Schema::create('user_roles', function (Blueprint $table) { $table->integer('user_id')/*略*/ $table->integer('role_id')/*略*/ $table->foreign('user_id')/*略*/ $table->foreign('role_id')/*略*/

投稿2020/01/03 01:46

編集2020/01/03 03:19
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/01/03 02:03

recipes に対して、comments は多対多じゃなくて、一対多であるはず...。
退会済みユーザー

退会済みユーザー

2020/01/03 03:10

1つのレシピに対して複数のユーザーはコメントできる(1v多) 1人のユーザーは複数のレシピに対してコメントできる(1v多) なぜか ORMでは上記を ManyToMany (多対多)という 用語にあやまりがあるから仕方がないんだよね・・・・ 一人のユーザーは一つのレシピにたいして一つのコメントをできるなので 1:1:1 なはずなんだけど
kyontama

2020/01/08 13:03

>Kosuke_Shibuyaさん 一対多と多対多を混在してました。ご指摘ありがとうございます! >asahina1979さん 「一人のユーザーは一つのレシピにたいして一つのコメントをできる」という考えだから、1対多ということですね。 ありがとうございます。 また、体調不良で寝込んでました。返信が遅くなり大変申し訳ありません。
guest

0

自分ならこうしますという形で、回答します。

ER

イメージ説明

migrations

php

1<?php 2use Illuminate\Database\Migrations\Migration; 3use Illuminate\Database\Schema\Blueprint; 4use Illuminate\Support\Facades\Schema; 5class CreateRecipesTable extends Migration 6{ 7 /** 8 * Run the migrations. 9 * 10 * @return void 11 */ 12 public function up() 13 { 14 Schema::create('recipes', function (Blueprint $table) { 15 $table->bigIncrements('id'); 16 $table->string('name'); 17 $table->unsignedBigInteger('created_by'); 18 $table->timestamps(); 19 $table->foreign('created_by')->references('id')->on('users'); 20 }); 21 } 22 /** 23 * Reverse the migrations. 24 * 25 * @return void 26 */ 27 public function down() 28 { 29 Schema::dropIfExists('recipes'); 30 } 31}

php

1<?php 2use Illuminate\Database\Migrations\Migration; 3use Illuminate\Database\Schema\Blueprint; 4use Illuminate\Support\Facades\Schema; 5class CreateCommentsTable extends Migration 6{ 7 /** 8 * Run the migrations. 9 * 10 * @return void 11 */ 12 public function up() 13 { 14 Schema::create('comments', function (Blueprint $table) { 15 $table->bigIncrements('id'); 16 $table->unsignedBigInteger('recipe_id'); 17 $table->string('content'); 18 $table->unsignedBigInteger('created_by'); 19 $table->timestamps(); 20 $table->foreign('recipe_id')->references('id')->on('recipes'); 21 $table->foreign('created_by')->references('id')->on('users'); 22 }); 23 } 24 /** 25 * Reverse the migrations. 26 * 27 * @return void 28 */ 29 public function down() 30 { 31 Schema::dropIfExists('comments'); 32 } 33}

models

php

1<?php 2namespace App; 3use Illuminate\Database\Eloquent\Model; 4use Illuminate\Database\Eloquent\Relations\BelongsTo; 5use Illuminate\Database\Eloquent\Relations\HasMany; 6class Recipe extends Model 7{ 8 protected $fillable = [ 9 'name', 10 'createdBy' 11 ]; 12 /** 13 * @return HasMany 14 */ 15 public function comments() 16 { 17 return $this->hasMany(Comment::class); 18 } 19 /** 20 * @return BelongsTo 21 */ 22 public function createdBy() 23 { 24 return $this->belongsTo(User::class, 'created_by'); 25 } 26}

php

1<?php 2namespace App; 3use Illuminate\Database\Eloquent\Model; 4use Illuminate\Database\Eloquent\Relations\BelongsTo; 5class Comment extends Model 6{ 7 protected $fillable = [ 8 'content', 9 'createdBy' 10 ]; 11 /** 12 * @return BelongsTo 13 */ 14 public function recipe() 15 { 16 return $this->belongsTo(Recipe::class); 17 } 18 /** 19 * @return BelongsTo 20 */ 21 public function createdBy() 22 { 23 return $this->belongsTo(User::class, 'created_by'); 24 } 25}

controllers

php

1<?php 2namespace App\Http\Controllers; 3use App\Recipe; 4use Illuminate\View\View; 5class RecipeController extends Controller 6{ 7 /** 8 * @return View 9 */ 10 public function index() 11 { 12 $recipes = Recipe::query() 13 ->with(['comments', 'createdBy']) 14 ->paginate(); 15 return view('recipes.index', compact('recipes')); 16 } 17 /** 18 * @param Recipe $recipe 19 * @return View 20 */ 21 public function show(Recipe $recipe) 22 { 23 return view('recipes.show', compact('recipe')); 24 } 25}

php

1<?php 2namespace App\Http\Controllers; 3use App\Comment; 4use App\Recipe; 5use Illuminate\Http\RedirectResponse; 6use Illuminate\Http\Request; 7use Illuminate\Support\Facades\Auth; 8class CommentController extends Controller 9{ 10 /** 11 * @param Request $request 12 * @param Recipe $recipe 13 * @return RedirectResponse 14 */ 15 public function store(Request $request, Recipe $recipe) 16 { 17 $recipe->comments()->save(new Comment([ 18 'content' => $request->get('content'), 19 'created_by' => Auth::id() 20 ])); 21 return redirect()->route('recipes', compact('recipe')); 22 } 23}

routing

php

1Route::resource('recipes', 'RecipeController'); 2Route::post('comments/{recipe}', 'CommentController@store')->name('comments.store');

blade

php

1@section('content') 2 <h1>レシピID{{ $recipe->id }}の詳細ページ</h1> 3 @auth() 4 {!! Form::open(['route' => 'comments.store']) !!} 5 <div class="form-group"> 6 {!! Form::textarea('content',['class' => 'form-control']) !!} 7 {!! Form::submit('投稿', ['class' => 'btn btn-primary btn-block']) !!} 8 </div> 9 {!! Form::close() !!} 10 @endauth 11 @foreach($recipe->comments as $comment) 12 <p>{{ $comment->content }}</p> 13 @endforeach 14@endsection

投稿2020/01/03 06:26

編集2020/01/08 13:39
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/01/06 04:59

質問者はノーリアクションかよ
kyontama

2020/01/08 12:58

>Kosuke_Shibuyaさん すいません、インフルで寝込んでました。 大変申し訳ありません。
kyontama

2020/01/08 13:46

>Kosuke_Shibuyaさん 再度回答いただき、誠にありがとうございます。 テーブル間の関係を正しく理解ぜず、多対多で考えてしまったのがミスのそもそもの原因でした。 Kosuke_ShibuyaさんのER図の通りにテーブル設定をしなおしたところ、非常にシンプルになりました。 改めて感謝申し上げます。 また病気で寝込んでいたといえど、いただいた回答にリアクションできずに申し訳ありませんでした。
退会済みユーザー

退会済みユーザー

2020/01/08 13:49 編集

いえいえ。 ここ最近、回答寄せてもリアクションしない人が増えて、このサイトに対しては良い感情を持っていないんですよね・・・。 病気なら仕方ないなと。お大事に。
kyontama

2020/01/08 13:59

そうだったんですね・・・ たしかに回答していただいてるのに良い感情は持てなくなりますよね。 私も今後は気を付けたいと思います、体調のお気遣いまでありがとうございました!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問