Laravelコレクション中の文字列の有無の判定方法


Laravelにて、
Eloquentでクエリビルドして(用語の使い方が不適切かも)得られたDBのコレクションの中の特定の文字列の有無をtrue/falseで返すことについて、はまってしまったので、備忘録として記す。

最終的には、コレクションをpluck('カラム名')で絞り込んでから、contains('文字列')メソッドで判定するという方法に落ち着いた。

やりたかったこと

テーブルは、Articlesテーブル(記事テーブル)と、
お気に入り記事を格納するLikesテーブル(お気に入りテーブル)がある。

記事一覧画面において、記事ごとにお気に入りに入っているかいないかを判定したい。

判定に使うカラムは、わけあってarticle_idではなく、article_nameとしている。

Unicodeを知らなかったため、文字化けと思ってしまった。

下のようにcontrollerでDBから値を取る。

ArticleController.php

public function index(){
        $articles = Destination::paginate(20);
        $likes = Like::where('user_id', Auth::id())->get();
        return view('index', compact('articles', 'likes));
}

bladeファイルの中で、article->nameとlikes中のarticle_nameで同一のものがあるかの判定を記述すればいいのだが、そんななか、たまたま下のように打ったことで、ちょっと泥沼にはまった。

index.blade.php
{{$likes}}

下のような、HTML画面になった。

[{"id":1,"user_id":1,"article_name":"\u30c6\u30b9\u30c8","created_at":null,"updated_at":null},
{"id":2,"user_id":1,"article_name":"\u30c6\u30b9\u30c82","created_at":null,"updated_at":null},
{"id":3,"user_id":1,"article_name":"\u30c6\u30b9\u30c83","created_at":null,"updated_at":null}]

なんだこれは。文字化けか??
よくわかっていない私は、Webの検索と、意味のないトラブルシューティングを重ねた。

何時間かして、\u30c6\u30b9\u30c8テストのunicodeであることを理解した。

なぜ上記のようにすると日本語がUnicodeで表示されるのかはまだよくわかっていないが。。

意味のないトラブルシューティング例

Unicodeを理解していなかった私は、トラブルシューティングのなかで、下の意味のないものもやってしまった。

Eloquentで取得した型はコレクション。コレクションはforeachで回して値を取るというのでこれまでやってきたし、日本語もUnicodeで表示されずにきちんと表示されていた。文字化けが原因で、色々うまく行かないと思ったので、文字化けさせないことが大事と考えて、下のようにforeachを使って判定をやってみた。

index.blade.php
@foreach ($articles as $article)
<tr>
       <td>{{$article->name}}</td>
<td>        
@foreach ($likes as $like)
@if ($like->article_name == $article->name)
チェック済み
@else
<div class="form-check">
    <input class="form-check-input" type="checkbox" id="{{$articles->name}}" 
      name="article_names[]" value="{{$article>name}}">
</div>
@endforeach
</td>
</tr>
@endforeach

結果はそう、画面がチェックボックスだらけになるという。。。。
likesテーブルのarticle_nameの値を総当りして、ほとんどがelseの方でチェックボックスを描画するという、おバカなコード。

containsメソッドが使えない?!

コレクション中の語句が有り無しを調べればいいので、コレクションのメソッドであるcontains()を使えばいいとわかったのだが、下のようにしても、なぜか効かない。

ArticleController.php
$likes = Like::where('user_id', Auth::id())->get(); //likesには'テスト'というarticle_nameが含まれている
$bool = likes->contains('テスト'); //false

//下はテスト用のコレクションを作って、contains()メソッドを試した。
$collection = collect(['テスト記事です', 'テスト記事だよ', 'テスト記事なんだね' ]);
$bool = collection->contains('テスト記事です'); //true

上記の$likes$collectionをdd()してみると、下のようになった。

$likesのdd結果

$collectionのdd結果

なんだか、$likesのほうは、目的のarticle_nameまで行くには何階層もあるみたい。これが悪いのかなと思って、以下のようにしたところ、うまく行った。

pluck()でカラムを絞り込んでから、contains()してうまくいった

ArticleController.php
$like_article_names = Like::where('user_id', Auth::id())->pluck('article_name');
$bool = $like_article_names->contains('テスト');  //true

ようやく、'テスト'という語句がコレクション中にありますよ、と判定することができるようになった。

ということで、コレクションでcontains()を使うには、絞り込んで(?)から使う、というふうにすればいいのかなと理解した。