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

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

ただいまの
回答率

87.91%

Laravel5のforeachでキーが重複する時、最初の1度だけ実行してbreakする方法を教えてください

解決済

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 3,867

score 106

 環境

Laravel 5.3.10
PHP 5.6.22
CentOS 6.8
Postgresql 9.5.3

http://codepen.io/qwe001/pen/MbYPZb

 していること

都道府県をDBから取得し、それとは別のDBにあるレコードの都道府県を比較、
都道府県の文字列が一致する場合、同じ都道府県のレコードを一覧表示

 したいこと

都道府県が一致する場合、なので、都道府県テーブルとアイテムテーブルは一対多の関係。
当然ながら、一つの都道府県に複数のアイテムがくっついている。
そこで、一番最初のレコードの上に見出しとして都道府県名を1つだけ表示したい。

 現在の実装

同じキーが二度続いたらbreakするような記述がしたいのですが、方法が思いつかず困っています。
現在のコードは以下のとおりです。

<table class="items">
    <tbody>
        @foreach($items as $item)
            @foreach($prefs as $pref)
                @if($pref -> name == $item -> pref) // DB上の都道府県名($pref -> name)とアイテムの都道府県($item -> pref)が一致した時
                    // ここに何か条件分岐を入れたいです。アイテムの都道府県が重複する場合、breakする、みたいな
                    <tr class="area"><th colspan="2">{{ $pref -> name }}</th></tr> // <tr class="area"><th colspan="2">東京都</th></tr>
                @endif
            @endforeach
            <tr>
                <th><a href="{{ $item -> id }}">{{ $item -> name }}</a></th> // <th><a href="1">●●●</a></th>
                <td>{{ $item -> pref }} {{ $item -> address }}</td> // <td>東京都 渋谷区</td>
            </tr>
        @endforeach
    </tbody>
</table>

生成されるHTMLタグは以下のサイトで公開しています。

http://codepen.io/qwe001/pen/MbYPZb

サイト上にあるように、見出しにあたる都道府県の名前は1度以上は登場させたくないので、
何かのIF文あるいはCOUNT文でforeachを1度だけ繰り返してbreakする、みたいなものを想像しているのですが、
その条件式として適切なものが思い浮かばずに困ったため、今回質問した次第です。何卒ご教授をお願いいたします。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 3

checkベストアンサー

+1

もっとも良い方法は Archsted様の回答の通りですが、
一応、PHPコードだけで解決する方法を提示させていただきます。
(ralavelは不勉強な上に手元に実行環境が無いため、正常に動作するかは怪しいですが(^^;)

もし、現状、コントローラ側で以下のようにビューにデータを送っている場合、

return view('XXX', array('items' => $items, 'prefs' => $prefs));

送信するデータを以下のように変更すると、

$itemsPerPref = array();
foreach ($prefs as $pref) {
    $itemsPerPref[$pref->name] = array();
    foreach ($items as $item) {
        if ($item->pref === $pref->name) {
           $itemsPerPref[$pref->name][] = $item;
        }
    }
}

return view('XXX', array('itemsPerPref' => $itemsPerPref));

テンプレート側では以下のようにすると、望む形に表示できると思います。

@foreach($itemsPerPref as $pref => $items)
    <tr class="area"><th colspan="2">{{ $pref }}</th></tr>
    @foreach($items as $item)
        <tr>
            <th><a href="{{ $item -> id }}">{{ $item -> name }}</a></th>
            <td>{{ $item -> pref }} {{ $item -> address }}</td>
        </tr>
    @endforeach
@endforeach

ただし、$itemsの要素数が膨大だと、このコードの実行速度は惨憺たる結果になります。
なぜなら、この方法ではコントローラとビューのループが合計(($prefsの要素数 + 1) × $itemsの要素数)回、回るためです。

繰り返しになりますが、Archsted様の回答のように、DBからデータを取得する方法から見直すことをお勧めします。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/11/14 14:45

    回答ありがとうございます。返信が遅くなり申し訳ありません。こちらの方法で期待通りのものが表示されました!ありがとうございます。
    ですが、話し合いの結果都道府県の見出しは必要なくなったのでこちらの実装も不要となりました。。ただ、DBのデータ取得を見直す良い機会となり勉強になりました。ありがとうございます。またよろしくお願いいたします。

    キャンセル

+1

laravelはまだ使ったことがないのでアルゴリズムだけでも手助けになればなと思い回答させて頂きます。

空の配列を作成し、1度だけ実行したい部分に入ったら、
最初に作成した空の配列に都道府県をpushします。

if文で「in_array」等を用いて一度入れたか都道府県チェックする。

などはいかがでしょうか。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

+1

仮に都道府県テーブルをprefsテーブル、アイテムテーブルをitemsテーブルとし、
それぞれに対応するPrefモデルとItemモデルを作るとします。

Prefモデルの中に、Itemモデルに対する1:多のリレーションを作ります。
itemsテーブルにpref_idにあたるカラムがあればいいのですが、どうやら無さそうに思えるので
(あんまりいい方法ではありませんが)お互いのテーブルのname列を結合条件とします。

public function items()
{
    return $this->hasMany('App\Item', 'name', 'name');
}

モデルの定義をしたあとは

$prefs = Pref::with('items')->get();

のように都道府県レコードをアイテムレコードと一緒に取得し、まとめてviewに送れば、

foreach ($prefs as $pref) {
    // prefの表示とか

    foreach ($pref->items as $item) {
        // itemの表示とか

    }

}

こんな感じで、外側のループは都道府県の回数、内側のループはその都道府県のアイテムで回せるので
重複して都道府県の出力が出ないようにできると思います。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

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

関連した質問

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

  • トップ
  • PHPに関する質問
  • Laravel5のforeachでキーが重複する時、最初の1度だけ実行してbreakする方法を教えてください