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

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

ただいまの
回答率

89.10%

Eloquentにて、n×mのオブジェクト数カウントを行う方法

解決済

回答 3

投稿

  • 評価
  • クリップ 0
  • VIEW 594

riz

score 25

前提・実現したいこと

「商品」と「店」のテーブルを、中間テーブルを通して多対多でつないでいます。
(厳密にはhasmany<-blongsto->hasmany)

このとき、商品名で検索した際に、商品テーブルでヒットした商品名群(n個)に対応する支店名群(n*m個)の数を数えたいです。

発生している問題

以下のような配列があったとき、Laravelで「branch」の数をカウントするのに簡単な記法などはありますでしょうか。

[{"id":2,
 "name":"ITEM",
 "middle":[{"id":2,
            "shop":{"id":2,
                    "name":"SHOP",
                    "branch":null},
           {"id":3,
            "shop":{"id":4,
                    "name":"\u30bd\u30d5\u30c8\u30d0\u30f3\u30af",
                    "branch":"BRANCH"}]}]
[{"id":3,
 "name":"ITEM2",
 "middle":[{"id":2,
            "shop":{"id":2,
                    "name":"SHOP",
                    "branch":null}]}]

該当のソースコード

コントローラー

$query = $request->input('q');

        if ( $query ){
            $items = Item::with('middle.shop', 'middle.information')
                -> where('name', 'LIKE', "%$query%")
                -> get();

            if ( $items ) {
                $count = //ここの書き方が分からない
            } else {
                $count = 0;
            }
            return view('result', ['query' => $query, 'count' => $count, 'items' => $items]);

モデル(商品)

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

モデル(中間テーブル)

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

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

試したこと

foreachでオブジェクトをゴリゴリ回す方法は思いついたのですが、もっとシンプルに書きたいです。

適当に$items->middle->shop->branchなど書いてみたのですが、クエリに対応している商品が単一でないので、やはりうまく動かせませんでした。

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

PHP Version 7.1.26
Laravel Framework 5.8.4

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

+1

パフォーマンス面はさておき,全件取得する前提なら結果をカウントするのが最善だと思います。ページネーションするなら別途カウント用クエリを発行するなど戦略は変わってきますが。

$count = $items->flatMap->middle->count();

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

check解決した方法

-1

belongstoManyにしても商品が複数ある場合のカウントはできなかったため、foreachで処理

$count = 0;
            if ( $items ) {
                foreach($items as $item){
                    foreach($item->middle as $middle){
                        $count += 1;
                    }
                }
            }

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

-1

多対多の設計がおかしいです。
https://readouble.com/laravel/5.7/ja/eloquent-relationships.html#many-to-many

中間テーブルはModelにするものではありませんよ。

Shop と Item を関連づける中間テーブルは、Laravelの規約においては、アルファベッロ順で、
item_shopテーブルを定義し、カラムは最低限以下のように定義する。

class CreateCompanyContractTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('item_shop', function (Blueprint $table) {
            $table->unsignedBigInteger('item_id');
            $table->unsignedBigInteger('shop_id');
        });
    }
}

その上で、リレーションは belongsToMany で設定。

class Item extends Model
{
    public function shops()
    {
        return $this->belongsToMany(Item::class);
    }
}
class Shop extends Model
{
    public function items()
    {
        return $this->belongsToMany(Item::class);
    }
}

コントローラー側でカウントする場合、Collectionを使ってカウント

$item->shops->count();

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/04/30 11:11

    ご回答ありがとうございます。
    このような設計にしているのは、item_shopの関係にさらに、金額やコメントをinformationというようなテーブルで付け加えたかったからです(中間テーブルに重複がでて冗長になるのを嫌った)
    参考 https://teratail.com/questions/34839

    追加の質問となってしまうのですが、この場合、
    LaravelではURL先のようなやり方ではなく、中間テーブルを冗長にしていくのが一般的に正しいやり方なのでしょうか?

    キャンセル

  • 2019/04/30 15:15

    中間テーブルのカラムを使った関係のフィルタリング
    https://readouble.com/laravel/5.7/ja/eloquent-relationships.html#many-to-many
    を参考にしてみてください。

    キャンセル

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

  • ただいまの回答率 89.10%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る