LoginSignup

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

Undefined variable $follower_count エラー表記について

解決したいこと

今課題でインスタグラムの簡易アプリを作っているのですが、フォロー数とフォロワー数を表示させようとしたところエラーが出てしまいますので解決方法を教えてください。

発生している問題・エラー

Undefined variable $follower_count

Views

resources/views/profiles/index.blade.php.php
@extends('layouts.app')

@section('content')
<div class="container">
  <div class="row" style="margin: auto;">
    <div class="col-3 p-5">
      <img src="{{ $user->profile->profileImage() }}" class="rounded-circle w-100">
    </div>
    <div class="col-9 pt-5">
      <div class="d-flex justify-content-between align-items-baseline">
        <div class="d-flex align-items-center pb-4">
          <div class="h4">{{ $user->username }}</div>


          <div class="d-flex justify-content-end flex-grow-1 ps-3">
            @if (auth()->user()->isFollowing($user->id))
            <form action="{{ route('unfollow', ['user' => $user->id]) }}" method="POST">
              {{ csrf_field() }}
              {{ method_field('DELETE') }}


              <button type="submit" class="btn btn-danger">フォロー解除</button>
            </form>
            @else
            <form action="{{ route('follow', ['user' => $user->id]) }}" method="POST">
              {{ csrf_field() }}

              @cannot('update', $user->profile)
              <button type="submit" class="btn btn-primary">フォローする</button>
            </form>
            @endif
            @endcan

            @if (auth()->user()->isFollowed($user->id))
            <div class="px-2">
              <span class="px-1 bg-secondary text-light">フォローされています</span>
            </div>
            @endif

          </div>
        </div>
      </div>

         @can('update', $user->profile)
          <a href="/p/create">投稿</a>
         @endcan



         @can('update', $user->profile)
          <a href="/profile/{{ $user->id}}/edit">プロフィールを編集</a>
         @endcan

      <div class="d-flex">
        <div class="pe-5">投稿<strong>{{ $user->posts->count() }}</strong> </div>
        <div class="pe-5">フォロワー<strong>{{ $follower_count }}</strong></div>
        <div class="pe-5">フォロー中<strong>{{ $follow_count }}</strong></div>
      </div>
      <div class="pt-4 font-weight-bold">{{ $user->profile->title }}</div>
      <div>{{ $user->profile->description }}</div>
      <div><a href="#">{{ $user->profile->url }}</a></div>
    </div>
  </div>

  <div class="row pt-5">
    @foreach($user->posts as $post)
     <div class="col-4 pb-4">
       <a href="/p/{{ $post->id }}">
         <img src="/storage/{{ $post->image }}" class="w-100">
       </a>
     </div>
    @endforeach

  </div>
</div>
@endsection

Models

app/Models/Follower.php.php
<?php

namespace App\Models\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;


class Follower extends Model
{
  protected $primaryKey = [
      'following_id',
      'followed_id'
  ];
  protected $fillable = [
      'following_id',
      'followed_id'
  ];
  public $timestamps = false;
  public $incrementing = false;

  public function getFollowCount($user_id)
    {
        return $this->where('following_id', $user_id)->count();
    }

    public function getFollowerCount($user_id)
    {
        return $this->where('followed_id', $user_id)->count();
    }

    // フォローしているユーザのIDを取得
    public function followingIds(Int $user_id)
    {
        return $this->where('following_id', $user_id)->get('followed_id');
    }

}

Models

app/Models/User.php.php
<?php

namespace App\Models;

// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'name',
        'email',
        'username',
        'password',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    protected static function boot()
      {
        parent::boot();

        static::created(function ($user){
          $user->profile()->create([
            'title' => $user->username,
          ]);
        });
      }


    public function posts()
    {
      return $this->hasMany(Post::class)->orderBy('created_at', 'DESC');
    }

    public function profile()
    {
      return $this->hasOne(\App\Models\Profile::class);
    }

    public function followers()
   {
       return $this->belongsToMany(self::class, 'followers', 'followed_id', 'following_id');
   }

   public function follows()
   {
       return $this->belongsToMany(self::class, 'followers', 'following_id', 'followed_id');
   }

   // フォローする
    public function follow(Int $user_id)
    {
        return $this->follows()->attach($user_id);
    }

    // フォロー解除する
    public function unfollow(Int $user_id)
    {
        return $this->follows()->detach($user_id);
    }

    // フォローしているか
    public function isFollowing(Int $user_id)
    {
        return $this->follows()->where('followed_id', $user_id)->exists();
    }

    // フォローされているか
   public function isFollowed(Int $user_id)
    {
        return (boolean) $this->followers()->where('following_id', $user_id)->first(['id']);
    }




}

Controller

app/Http/Controllers/ProfilesController.php.php
<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;
use Intervention\Image\Facades\Image;
use App\Models\Follower;

class ProfilesController extends Controller
{
  public function index(User $user)
  {
      return view('profiles.index', compact('user'));
  }
  public function edit(User $user)
  {
    $this->authorize('update', $user->profile);

    return view('profiles.edit', compact('user'));
  }
  public function update(User $user)
  {
    $data = request()->validate([
      'title' => 'required',
      'description' => 'required',
      'url' => 'url',
      'image' => '',
    ]);
    if (request('image')) {
      $imagePath = request('image')->store('profile', 'public');

      $image = Image::make(public_path("storage/{$imagePath}"))->fit(1000,1000);
      $image->save();

      $imageArray = ['image' => $imagePath];
    }
    auth()->user()->profile->update(array_merge(
      $data,
      $imageArray ?? []
    ));
    return redirect("/profile/{$user->id}");
  }


  // フォロー
      public function follow(User $user)
      {
          $follower = auth()->user();
          // フォローしているか
          $is_following = $follower->isFollowing($user->id);
          if(!$is_following) {
              // フォローしていなければフォローする
              $follower->follow($user->id);
              return back();
          }
      }

      // フォロー解除
      public function unfollow(User $user)
      {
          $follower = auth()->user();
          // フォローしているか
          $is_following = $follower->isFollowing($user->id);
          if($is_following) {
              // フォローしていればフォローを解除する
              $follower->unfollow($user->id);
              return back();
          }
      }

     public function show(User $user, Follower $follower)
    {
        $login_user = auth()->user();
        $is_following = $login_user->isFollowing($user->id);
        $is_followed = $login_user->isFollowed($user->id);
        $follow_count = $follower->getFollowCount($user->id);
        $follower_count = $follower->getFollowerCount($user->id);

        return view('users.show', [
            'user'           => $user,
            'is_following'   => $is_following,
            'is_followed'    => $is_followed,
            'follow_count'   => $follow_count,
            'follower_count' => $follower_count
        ]);
    }
  }

自分で試したこと

Viewのフォロー数を{{ Auth::user()->follows()->count() }}とフォロワー数を{{ Auth::user()->followers()->count() }}
に変更したところエラーは改善しましたが、ログインしているユーザーのフォロー数とフォロワー数がすべて他のユーザーのページで反映してしまうため、他のユーザーのページでもきちんと反映できるように変更したいです。
初心者でわからないことだらけなので教えていただきたいです。

※追記 Routes

routes/web.php.php
<?php

use Illuminate\Support\Facades\Route;

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', function () {
    return view('welcome');
});

Auth::routes();


  // フォロー/フォロー解除を追加
  Route::post('/profile/{user}/follow', 'App\Http\Controllers\ProfilesController@follow')->name('follow');
  Route::delete('/profile/{user}/unfollow', 'App\Http\Controllers\ProfilesController@unfollow')->name('unfollow');

Route::get('/p/create','App\Http\Controllers\PostsController@create');
Route::get('/p/{post}','App\Http\Controllers\PostsController@show');
Route::post('/p','App\Http\Controllers\PostsController@store');

Route::get('/profile/{user}', 'App\Http\Controllers\ProfilesController@index')->name('profile.show');
Route::get('/profile/{user}/edit', 'App\Http\Controllers\ProfilesController@edit')->name('profile.edit');
Route::patch('/profile/{user}', 'App\Http\Controllers\ProfilesController@update')->name('profile.update');

Auth::routes();

Route::get('/home', [App\Http\Controllers\HomeController::class, 'index'])->name('home');



Route::get('/', 'App\Http\Controllers\PostsController@index')->name('posts.index');

Route::post('/like/{postId}',[App\Http\Controllers\LikeController::class,'store']);
Route::post('/unlike/{postId}',[App\Http\Controllers\LikeController::class,'destroy']);
0

6Answer

※コード整形をしたいので新しい回答から失礼します。
ありがとうございます。
そうですね。見ているのはprofile.indexなので、
app/Http/Controllers/ProfilesController.php

  public function index(User $user)
  {
      return view('profiles.index', compact('user'));←ここ
  }

showメソッドのように
$follower_count$follow_countを渡していないので、「変数が定義されてなくて値が見れないよ」と言われてますね。

 compact('user', 'follow_count', 'follower_count''))

にしたいので、
その前に、public function indexの中で$follow_count$follower_countを取ってきて変数として定義する必要があります。
https://qiita.com/ryo2132/items/63ced19601b3fa30e6de#%E8%A4%87%E6%95%B0%E3%81%AE%E5%A4%89%E6%95%B0%E3%81%AE%E9%80%81%E4%BF%A1

Follower $followerをurlのパスから取りたいですが、一番最初のコメントに書いたように、複数の主キーを持つテーブルはモデル結合(route model Binding)で取れないです。

Follower はUserから取ってやるといいと思います。
ただここで、
UserとFollowerはbelongsToManyapp/Models/User.phpでリレーションを記述してますので、それぞれのbelongsToManyを定義しているメソッド名

Follower $follower = $user->followers->first();
Follower $follow = $user->follows->first();

などでFollowerモデルは取れそうですが、
個人的にはapp/Models/Follower.phpgetFollowCountgetFollowerCountメソッドはUserモデルに書いてあげたほうがいいと思います。

1つに定まるもののモデルからメソッドで呼んであげたほうがいいと思います。
Follower $follower = $user->followers->first();
「ユーザのフォロワーの配列を取って、どれか一つから、カウントして」では動きも正直うまくできるのか机上で見てて自信はありません。
「一人の特定したユーザーからフォロワーの件数を取ってくる」とし、UserモデルにgetFollowCountgetFollowerCountを書いてやるほうがいいと思います。
長く書いてしまいましたが、詰まってしまったらまたコメントなどでお願いします。微力ながらお手伝いさせて頂きます。

1Like
This answer has been deleted for violation of our Terms of Service.

そうですね。PHPは動的型付けなので、型書きませんね。失礼しました。

user


   public function getFollowCount()
    {
        return $this->follows()->count();
    }

    public function getFollowerCount()
    {
        return $this->followers()->count();
    }

になって
ProfilesController.php


public function index(User $user)
  {
    $follower_count = $user->getFollowerCount();
    $follow_count = $user->getFollowCount();

      return view('profiles.index', compact('user', 'follow_count', 'follower_count'));
  }

になるイメージです。
明日だと私も机上でなく試せる時間が取れるかもしれませんので、申し訳ありませんが、一旦これでやって頂き、ダメだったら明日の夜、私も試してみます。
よろしくお願いします。

1Like

Comments

  1. @mechatuyoi

    Questioner
    お忙しい中コメントありがとうございます。
    @mae616さんの仰る通りUserとコントローラーを書き換えたところ改善されました!
    やっと改善されてとても嬉しいです。
    ありがとうございました!!

こんにちは。
app/routes/web.phpのファイル内容はどうなっていますでしょうか?

Viewのフォロー数を{{ Auth::user()->follows()->count() }}とフォロワー数を{{ Auth::user()->followers()->count() }}
に変更したところエラーは改善しました

とのお話なので、
https://readouble.com/laravel/9.x/ja/routing.html
「ルートモデル結合(route model Binding)」というもので、IDをurlに指定すると、指定されたIDに一致するUserモデル等を取得する機能があるのですが、ProfilesControllerクラスのshowメソッドの引数、User $user, Follower $followerがうまく取得できてない可能性を知りたい為の質問です。

追記:Followerモデルは複合主キー(主キーが複数あるもの)なので、Followerモデルのルートモデル結合(route model Binding)はできないとは思ってます

0Like

Comments

  1. @mechatuyoi

    Questioner
    こんにちは。
    追記にweb.phpのコードを記載いたしましたので確認をお願い致します。
    モデル結合をしてしまっているためうまく取得できていない可能性があるということでしょうか?
  2. ありがとうございます。

    モデル結合のid指定の記載方法が違うのかなと網を張っていたのですが、それは大丈夫のようです。
    ただ、そもそも唯一、$follower_countをviewに受け渡している
    `ProfilesController@show`の記載がweb.phpにありませんね。

    質問ばかりで申し訳ありませんが、今何のページを見ているでしょうか?
    「発生している問題・エラー」の時、アクセスしているurlと、
    「Views」に書いてあるファイル内容のファイル名と一つ上のディレクトリ名を教えて頂けないでしょうか?
    よろしくお願いします。
This answer has been deleted for violation of our Terms of Service.

ご丁寧に回答ありがとうございます。
@mae616さんの仰る通りにコードを書いてみたのですが、
syntax error, unexpected variable "$follower"
というエラーが出てしまいます。

.php
Follower $follower = $user->followers->first();
Follower $follow = $user->follows->first();

こちらのコードから

.php
$follower = $user->followers->first();
$follow = $user->follows->first();

こちらに変更をすると
Cannot redeclare App\Models\User::getFollowCount()
というエラーが出てしまいます。
UserモデルにgetFollowCount、getFollowerCountを書いてもエラーが出てしまいます。
原因がわからないので教えて頂けたらと存じます。

現在のコードを記載します。(変更箇所のみ)

App\Models\User.php.php
<?php

namespace App\Models;

// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;

class User extends Authenticatable
{
    use HasApiTokens, HasFactory, Notifiable;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<int, string>
     */
    protected $fillable = [
        'name',
        'email',
        'username',
        'password',
    ];

    /**
     * The attributes that should be hidden for serialization.
     *
     * @var array<int, string>
     */
    protected $hidden = [
        'password',
        'remember_token',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'email_verified_at' => 'datetime',
    ];

    protected static function boot()
      {
        parent::boot();

        static::created(function ($user){
          $user->profile()->create([
            'title' => $user->username,
          ]);
        });
      }


    public function getAllUsers(Int $user_id)
    {
    return $this->Where('id', '<>', $user_id)->paginate(5);
  }


    public function posts()
    {
      return $this->hasMany(Post::class)->orderBy('created_at', 'DESC');
    }

    public function profile()
    {
      return $this->hasOne(\App\Models\Profile::class);
    }

    public function getFollowCount($user_id)
    {
        return $this->where('following_id', $user_id)->count();
    }

    public function getFollowerCount($user_id)
    {
        return $this->where('followed_id', $user_id)->count();
    }
    public function followingIds(Int $user_id)
    {
        return $this->where('following_id', $user_id)->get('followed_id');
    }

    public function followers()
   {
       return $this->belongsToMany(self::class, 'followers', 'followed_id', 'following_id');
   }

   public function follows()
   {
       return $this->belongsToMany(self::class, 'followers', 'following_id', 'followed_id');
   }

   // フォローする
    public function follow(Int $user_id)
    {
        return $this->follows()->attach($user_id);
    }

    // フォロー解除する
    public function unfollow(Int $user_id)
    {
        return $this->follows()->detach($user_id);
    }

    // フォローしているか
    public function isFollowing(Int $user_id)
    {
      return $this->follows()->where('followed_id', $user_id)->exists();
    }

    // フォローされているか
    public function isFollowed(Int $user_id)
    {
        return (boolean) $this->followers()->where('following_id', $user_id)->first(['id']);
    }

    public function getFollowCount($user_id)
    {
        return $this->where('following_id', $user_id)->count();
    }

    public function getFollowerCount($user_id)
    {
        return $this->where('followed_id', $user_id)->count();
    }
    public function followingIds(Int $user_id)
    {
        return $this->where('following_id', $user_id)->get('followed_id');
    }


    //多対多のリレーションを書く
    public function likes()
    {
        return $this->belongsToMany('App\Models\Post','likes','user_id','post_id')->withTimestamps();
    }

    //この投稿に対して既にlikeしたかどうかを判別する
    public function isLike($postId)
    {
      return $this->likes()->where('post_id',$postId)->exists();
    }

    //isLikeを使って、既にlikeしたか確認したあと、いいねする(重複させない)
    public function like($postId)
    {
      if($this->isLike($postId)){
        //もし既に「いいね」していたら何もしない
      } else {
        $this->likes()->attach($postId);
      }
    }

    //isLikeを使って、既にlikeしたか確認して、もししていたら解除する
    public function unlike($postId)
    {
      if($this->isLike($postId)){
        //もし既に「いいね」していたら消す
        $this->likes()->detach($postId);
      } else {
      }
    }

}

app/Http/Controllers/ProfilesController.php.php
<?php

namespace App\Http\Controllers;

use App\Models\User;
use Illuminate\Http\Request;
use Intervention\Image\Facades\Image;
use App\Models\Follower;

class ProfilesController extends Controller
{
  public function index(User $user)
  {
    $follower = $user->followers->first();
    $follow = $user->follows->first();

      return view('profiles.index', compact('user', 'follow_count', 'follower_count'));
  }
  public function edit(User $user)
  {
    $this->authorize('update', $user->profile);

    return view('profiles.edit', compact('user'));
  }
  public function update(User $user)
  {
    $data = request()->validate([
      'title' => 'required',
      'description' => 'required',
      'url' => 'url',
      'image' => '',
    ]);
    if (request('image')) {
      $imagePath = request('image')->store('profile', 'public');

      $image = Image::make(public_path("storage/{$imagePath}"))->fit(1000,1000);
      $image->save();

      $imageArray = ['image' => $imagePath];
    }
    auth()->user()->profile->update(array_merge(
      $data,
      $imageArray ?? []
    ));
    return redirect("/profile/{$user->id}");
  }


  // フォロー
     public function follow(User $user)
     {
         $follower = auth()->user();
         // フォローしているか
         $is_following = $follower->isFollowing($user->id);
         if(!$is_following) {
             // フォローしていなければフォローする
             $follower->follow($user->id);
             return back();
         }
     }

     // フォロー解除
     public function unfollow(User $user)
     {
         $follower = auth()->user();
         // フォローしているか
         $is_following = $follower->isFollowing($user->id);
         if($is_following) {
             // フォローしていればフォローを解除する
             $follower->unfollow($user->id);
             return back();
         }
     }


}
0Like

Your answer might help someone💌