質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
85.48%
Laravel

LaravelとはTaylor Otwellによって開発された、オープンソースなPHPフレームワークです。Laravelはシンプルで表現的なシンタックスを持ち合わせており、ウェブアプリケーション開発の手助けをしてくれます。

PHP

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

Eloquent

Eloquentとは、PHPフレームワークのLaravelに最初から含まれているORM(Object-relational mapping:オブジェクト関係マッピング)です。

Laravel 5

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

Q&A

解決済

1回答

3633閲覧

Laravelで自己結合した関連を双方向で取得したい

newmog2004

総合スコア12

Laravel

LaravelとはTaylor Otwellによって開発された、オープンソースなPHPフレームワークです。Laravelはシンプルで表現的なシンタックスを持ち合わせており、ウェブアプリケーション開発の手助けをしてくれます。

PHP

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

Eloquent

Eloquentとは、PHPフレームワークのLaravelに最初から含まれているORM(Object-relational mapping:オブジェクト関係マッピング)です。

Laravel 5

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

0グッド

0クリップ

投稿2019/08/05 16:48

Laravel 5.6環境での質問です。

実現したいこと

商品ページでそれに関連する別商品を表示するようなプログラムを実装したいです。

現状の実装

商品テーブルを元に、その商品同士の関連テーブルを定義しています。
下記の通り、Productモデルを関連商品を管理するテーブルで定義しています。

PHP

1Schema::create('product_relations', function (Blueprint $table) { 2 $table->unsignedBigInteger('product_id_1')->comment('商品ID_1'); 3 $table->foreign('product_id_1') 4 ->references('id')->on('products') 5 ->onUpdate('cascade') 6 ->onDelete('cascade'); 7 $table->unsignedBigInteger('product_id_2')->comment('商品ID_2'); 8 table->foreign('product_id_2') 9 ->references('id')->on('products') 10 ->onUpdate('cascade') 11 ->onDelete('cascade'); 12 $table->primary(['product_id_1', 'product_id_2']); 13 $table->timestamps(); 14});

発生している問題

Productモデルにおいて、product_relationsテーブルから
product_1をキーにproduct_2の商品と、
product_2をキーにproduct_1の商品を合わせた関連を定義したいですのですが、
SQLにてエラーが発生してしまいます。

イメージとしては下記のようなことを行いたいのですが、
モデルやテーブル設計がLaravelのガイドラインと合っていないのか、
関連の定義が誤っているのか、教えていただければ幸いです。

PHP

1public function relationProducts() { 2 return $this->belongsToMany(Product::class, 'product_relations', 'product_id_1', 'product_id_2') 3 ->unionAll($this->belongsToMany(Product::class, 'product_relations', 'product_id_2', 'product_id_1')); 4}

発生しているエラー

SQLSTATE[21000]: Cardinality violation: 1222 The used SELECT statements have a different number of columns (SQL: (select `products`.*, `product_relations`.`product_id_1` as `pivot_product_id_1`, `product_relations`.`product_id_2` as `pivot_product_id_2` from `products` inner join `product_relations` on `products`.`id` = `product_relations`.`product_id_2` where `product_relations`.`product_id_1` = 1) union all (select * from `products` inner join `product_relations` on `products`.`id` = `product_relations`.`product_id_1` where `product_relations`.`product_id_2` = 1))

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

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

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

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

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

guest

回答1

0

ベストアンサー

UNIONする時のselect文のカラム数が合ってないってエラーですね。

laravelのリレーションでやるのは無理がありそうなので中間テーブル作って処理した方が簡単で設計も綺麗なのでは?

UNIONとJOIN繰り返したら遅そうですし

どうしてもUNIONで処理したいならUNIONしたVIEW作ってそれをlaravelで中間テーブルがわりにするのが早そうです

投稿2019/08/05 17:25

mikkame

総合スコア5036

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

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

newmog2004

2019/08/05 18:36 編集

早速のご回答ありがとうございます。感謝いたします。 中間テーブルを作って・・・というのが具体的にどのような実装か イメージがわかなかったのですが、よろしければもう少し詳細に教えていただけますでしょうか。 そもそも論になりますが、このような相関する自己参照モデルのような機能を実装する場合、 (RDBにおいては)どのような実装がベストプラクティスなのでしょうか。 無理に ・content_id_1 (PK) ・content_id_2 (PK) のように双方向に作るのではなく、 ・content_id (PK) ・relation_id のようなカラム設計をして、 (content_id, relation_id) => (1, 2) と (content_id, relation_id) => (2, 1) のように、複数行を作って管理した方が(行数は2倍になりますが) Laravelとも相性が良さそうですしいいのかなとも考えており、引き続き悩んでおります。。。
mikkame

2019/08/06 02:58

> のように、複数行を作って管理した方が(行数は2倍になりますが) はい、そのような感じが無難かと思います。 現在実行したいSQLを見ると例えば10000件の商品があれば JOIN(10000件*10000件) UNION JOIN(10000件*10000件) みたいな感じの処理(JOINは結合した上でwhereで削除する)になるはずなので(実際はもっと賢くて短縮しているかもしれないが) 膨大な表を消費するので、普通に2行ずつ入ってしまう方が低コストのような気がします。 あとcascadeされてますが、これって関連商品が連鎖して消えてしまいませんか?
mikkame

2019/08/06 03:00

あと2行ずつ入れなくても、1行だけいれて 参照されている(belongsTo)と参照している(hasMany)をアプリケーション側でマージすれば1行で済むのではないでしょうか(編集画面が面倒くさそうだが、双方消して、都度どちらかを主キーに入れ直せば問題ないように見える)
newmog2004

2019/08/06 04:41

mikkame様 詳細なご回答ありがとうございます。 非常に参考になりました。 確かに下のコメントの通り、アプリケーション側でマージする方法もアリだと思いましたが、 シンプルさを取って、結局上のコメントの通り、2行ずつ処理する方針にしようかと思います。 (整合性をアプリケーションが取らなければならないのは気をつけます。。) cascadeについては、主テーブルはproductsになると思うので、 productsが消えれば商品同士の関連を自動的に消すという意図で設定しています。 そのため、商品が消えても関連商品は消えないという認識で合ってますよね?
newmog2004

2019/08/06 05:00

ご回答ありがとうございます。 上記の例で、念のため商品を削除して確認してみましたが関連する商品レコードは消えないようです。 ※関連テーブル上のレコードは消えました。 mikkame様にて回答いただいたリンクのように、 カテゴリと配下商品が1:nで結合している場合は、カテゴリが消えると商品が消えるのはおかしいので、 set nullが適切だと思われます。 とりいそぎ本題の商品同士の関連についてはアドバイス通り解決いたしましたので、 解決済みと致します。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問