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

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

ただいまの
回答率

87.59%

Laravel5 Collectionの結合について

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 1,273

score 57

前提・実現したいこと

LaravelのCollection同士を結合
(イメージとしてはDB left join)
そのコマンドがあるかどうか?

データベース(案)

Collection出力の元となるデーターベースです。

users

id name
1 名前1
2 名前2
3 名前3

titles

id name
1 タイトル1
2 タイトル2
3 タイトル3

items

id title_id name
1 1 アイテム1
2 1 アイテム2
3 1 アイテム3
4 2 アイテム4
5 2 アイテム5
6 2 アイテム6
7 3 アイテム7
8 3 アイテム8
9 3 アイテム9

datas

id user_id title_id item_id
1 1 1 2
2 1 2 5
3 1 3 8

以上のデータベースから
datasより名前1(user_id 1)のデータを以下のように出力し

結果

$collection = collect([
    ['id' => 1, 'title' => 'タイトル1', 'value' => 2,'name' => 'アイテム2'],
    ['id' => 2, 'title' => 'タイトル2', 'value' => 5,'name' => 'アイテム5'],
    ['id' => 3, 'title' => 'タイトル3', 'value' => 8,'name' => 'アイテム8']
]);

以上の構成であればデーターベースでやれば簡単なのですが、

それをCollectionで似たようなことが、できないかというのが今回の質問の意図です。

また理由の1つに、上記のテーブルが一部jsonデータで保存されていた場合にどのようにすればいいかと方法に悩んでしまいました。
Collectionであれば以下の方法でjsonデータをcollect化できますので。。

collect(json_encode(jsonデータ))

質問

2つのCollectionを結合して1つのCollectionデータを作成を考えています。

$collection_a = collect([
    ['id' => 1, 'value' => 2],
    ['id' => 2, 'value' => 5],
    ['id' => 3, 'value' => 8],
]);

$collection_b = collect([
    ['id' => 1, 'title' => 'タイトル1',
        'items'=>[
            ['id' => 1, 'name' => 'アイテム1'],
            ['id' => 2, 'name' => 'アイテム2'],
            ['id' => 3, 'name' => 'アイテム3']
        ]
    ],
    ['id' => 2, 'title' => 'タイトル2',
        'items'=>[
            ['id' => 4, 'name' => 'アイテム4'],
            ['id' => 5, 'name' => 'アイテム5'],
            ['id' => 6, 'name' => 'アイテム6']
        ]
    ],
    ['id' => 3, 'title' => 'タイトル3',
        'items'=>[
            ['id' => 7, 'name' => 'アイテム7'],
            ['id' => 8, 'name' => 'アイテム8'],
            ['id' => 9, 'name' => 'アイテム9']
        ]
    ]
]);


以上の2つのCollectionを以下のように出力を考えています。

$collection_c = collect([
    ['id' => 1, 'title' => 'タイトル1', 'value' => 2,'name' => 'アイテム2'],
    ['id' => 2, 'title' => 'タイトル2', 'value' => 5,'name' => 'アイテム5'],
    ['id' => 3, 'title' => 'タイトル3', 'value' => 8,'name' => 'アイテム8']
]);

foreachで回す方法しかないのでしょうか?

まわりくどい方法は思いつくのですが
Laravel自体はまだ始めたばっかで適切なコマンド・方法があるかと思った次第です。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • m.ts10806

    2019/02/13 12:55

    スペルミスがかなり目に付いてしまうので修正していただければと。
    Larvel larabel ではなくLaravel

    キャンセル

  • m.ts10806

    2019/02/13 13:03

    リファレンス読んだ感じ、色々駆使すればできそうですけど
    https://readouble.com/laravel/5.7/ja/collections.html
    まだ試してないので調整してみます。

    キャンセル

  • m.ts10806

    2019/02/13 13:16 編集

    要件確認です。
    collect()されたものをマージということですよね?
    提示されている配列は仮のものとは思いますが、大目的が別にあるのでしたらそれもあった方がよいかもしれません。
    ~~~最後1行余計でした。編集削除しました。

    キャンセル

  • BlueBits

    2019/02/13 13:28

    おっしゃる通りです。あとでcollectという面で言いますと元々、collection_a,bはEloquentでDBから出力したイメージになります。なのでDB出力時にcollection_cに調整してやればとも思います。
    データベース部分も含めて全体イメージを質問に追加します。

    キャンセル

回答 2

+2

探すのはcollectionにお任せするとしても、やっぱり大元をは自分で回さざるを得なさそうな・・

$collection_c = $collection_a->map(function($_a,$_)use($collection_b){
  $_a['title'] = $_a['name'] = null;

  //コレクションBから、b.id=a.idを探す
  if($_b = $collection_b->where('id', $_a['id'])->first()){
    $_a['title']=$_b['title'];
    //Bの中のitemsもcollectionにして、そこからb.items.id = a.valueを探す
    $_items = collect($_b['items']);
    if($_b_item = $_items->where('id', $_a['value'])->first()){
      $_a['name'] = $_b_item['name'];
    }
  }
  return $_a;
});

自分でforeachの入れ子を沢山書くよりはマシなのかなーという感じでしょうかねぇ^^;

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/02/13 16:34

    すみません、解答いただいて自分で調べて解答書いてた間にアンサーいただいてました。
    結局foreachの入れ子なのでそもそも構成が甘かったようです。
    1人で開発しているので他の方の意見がお聞きできて嬉しいです。
    ありがとうございます。

    キャンセル

checkベストアンサー

+1

あまり美しくないやり方かもしれませんが。(結局foreach使ったし・・・)

$collection_c=$collection_a->map(function ($item_a,$key_a) use ($collection_b){
    $collection_b->map(function ($item_b,$key_b) use (&$item_a){
        if($item_b['id'] === $item_a['id']){
            foreach($item_b['items'] as $b_collect){
                if($b_collect['id'] === $item_a['value']){
                    $item_a['title']=$item_b['title'];
                    $item_a['name']=$b_collect['name'];
                    return false;
                }
            }
        }
    });
    return $item_a;
});
var_dump($collection_c->all());

/*

array(3) {
  [0]=>
  array(4) {
    ["id"]=>
    int(1)
    ["value"]=>
    int(2)
    ["title"]=>
    string(13) "タイトル1"
    ["name"]=>
    string(13) "アイテム2"
  }
  [1]=>
  array(4) {
    ["id"]=>
    int(2)
    ["value"]=>
    int(1)
    ["title"]=>
    string(13) "タイトル2"
    ["name"]=>
    string(13) "アイテム1"
  }
  [2]=>
  array(4) {
    ["id"]=>
    int(3)
    ["value"]=>
    int(3)
    ["title"]=>
    string(13) "タイトル3"
    ["name"]=>
    string(13) "アイテム3"
  }
}
*/

下記のような記事も参考にしてみてもいいかもしれません。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/02/13 16:20

    ありがとうございます。頂いたコードを元に見直してみました。
    $collection_c=$collection_a->map(function ($item_a,$key_a) use ($collection_b){
    $find_collection = collect($collection_b->firstWhere('id','===',$item_a['id']));
    $find_item = collect($find_collection->get('items'))->firstWhere('id','===',$item_a['value']);
    $item_a['title'] = $find_collection->get('title');
    $item_a['name'] = $find_item['name'];
    return $item_a;
    });
    $collection_c->dump();

    foreach,ifをcollectのfirstWhereに変更して見た目だけスッキリしました。
    内部処理的はよりまわりくどく同じ事をしていますのでなんとも言えませんが・・・
    構成の見直しのイメージになりました。ありがとうございます。

    キャンセル

  • 2019/02/13 16:24

    そうですね。Laravelっぽい作りになったと思います(別回答のほうが綺麗ですね。。)

    キャンセル

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

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

関連した質問

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