ErrorException count(): Parameter must be an array or an object that implements Countable
某アカデミーでTwitterクローンを作っている際に発生しました。
結論:count()の引数にカウントできないものを入れている。
結論から言うと、私の場合はcount()の引数にBelongsToMany, HasMany等のクラスのオブジェクトを入れていたことが原因でした。
それらをget(),first(),pagenate()などでCollectionクラスにしてやることでcount()できるようになりました。
Controller
class UsersController extends Controller
{
/**
* ユーザのお気に入り一覧ページを表示するアクション。
*
* @param $id ユーザのid
* @return \Illuminate\Http\Response
*/
public function favorites($id)
{
$user = \Auth::user();
- $microposts = $user->favorites();
+ $microposts = $user->favorites()->get();
$data = ['user' => $user,'microposts' => $microposts,];
// favoritesビューでそれらを表示
return view('users.favorites', $data);
}
}
再現と解決までの過程
私はControllerからViewに渡す際にうまくいっていませんでした。
Model
class User extends Authenticatable
{
/**
* このユーザのお気に入り
*/
public function favorites()
{
return $this->belongsToMany(Micropost::class, 'user_favorite', 'user_id', 'favorite_id')->withTimestamps();
}
}
Controller
// 略
class UsersController extends Controller
{
public function favorites($id)
{
$user = \Auth::user();
$microposts = $user->favorites();
$data = ['user' => $user, 'microposts' => $microposts,];
// favoritesビューでそれらを表示
return view('users.favorites', $data);
}
}
Router
Route::group(['middleware' => ['auth']], function () {
Route::group(['prefix' => 'users/{id}'], function () {
Route::get('favorites', 'UsersController@favorites')->name('users.favorites');
});
});
View
@if (count($microposts) > 0)
@foreach ($microposts as $micropost)
@endforeach
@endif
さて、/users/{id}/favosで動作確認してみると…
ErrorException
count(): Parameter must be an array or an object that implements Countable
(View: /home/ubuntu/environment/microposts/resources/views/users/favorites.blade.php)
Viewの1行目のcountでエラーです。
「パラメーターは配列かカウントできるオブジェクトじゃないとアカンで!」と怒られてるようです。
Controllerで\$microposts = $user->favorites();で配列やCollectionが取れていると思い込んでいました。
tinkerで確認してみます。
$ php artisan tinker
>>> $user = User::find(1)
>>> get_class($user->favorites());
=> "Illuminate\Database\Eloquent\Relations\BelongsToMany"
>>> count($user->favorites());
PHP Warning: count(): Parameter must be an array or an
object that implements Countable in Psy Shell code on line 1
=> 1
なるほど、BelongsToManyクラスはカウントできるけど警告が出ますね。
1時間近くあれこれやってみたのですがカウンタブルなクラスに変えられず、メンターに質問しました。
「paginate() や get() や first() などを実行することでデータの取得が行われ、countなどを実行できる形式のデータを取得できます」とのこと。
さっそくget()で試してみる。
>>> get_class($user->favorites()->get());
=> "Illuminate\Database\Eloquent\Collection"
>>> count($user->favorites()->get());
=> 4
Collectionクラスになりました!カウントもできます。
その結果、Controllerを書き換えて出来上がったのが冒頭と同様の↓
class UsersController extends Controller
{
/**
* ユーザのお気に入り一覧ページを表示するアクション。
*
* @param $id ユーザのid
* @return \Illuminate\Http\Response
*/
public function favorites($id)
{
$user = \Auth::user();
- $microposts = $user->favorites();
+ $microposts = $user->favorites()->get();
$data = ['user' => $user,'microposts' => $microposts,];
// favoritesビューでそれらを表示
return view('users.favorites', $data);
}
}
Viewを少し書き換えて
@if (count($microposts) > 0)
@foreach ($microposts as $micropost)
{{-- 投稿内容 --}}
<p class="mb-0">{!! nl2br(e($micropost->content)) !!}</p>
@endforeach
@endif
無事に内容が取得できました!