LaravelでTwitterクローン開発 | ユーザー一覧ページの表示とユーザープロフィールページへのリンク生成

解決済

回答 2

投稿 編集

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

KIYZ

score 15

 前提・実現したいこと

Cloud9でLaravel 5.1 LTSを用いてTwitterクローンを開発しています。
現在、以下の2点を同時に実現しようとしているところですが、どちらかを実現させるともう片方が実現できないという状態で、対処法が分からない状況です。

  1. DBのUsersテーブル上に存在する全ユーザー一覧のページを表示
  2. ログインしたユーザーが自分のプロフィールページにアクセスするためのリンクをナビバーに設置

 発生している問題・エラーメッセージ

1.を実現した場合のエラーメッセージ(ナビバーの"My profile"をクリックした時):

1/1
FatalErrorException in UsersController.php line 26:
Call to a member function microposts() on a non-object

※ナビバーに設置したログインユーザーのプロフィールページへのリンクは ワークスペース名.c9users.io:8080/users/userのid となるべきですが、 ワークスペース名.c9users.io:8080/users/%7Busers%7D となってしまいます。
アドレスバーに直接 ワークスペース名.c9users.io:8080/users/1 等と入力してアクセスすると正常に目当てのページが表示されます。

2.を実現した場合:

/microposts/resources/views/commons/navbar.blade.php の20行目の link_to_route() に渡している第3引数を削除すると、ナビバーに設置したログインユーザーのプロフィールページへのリンクが正常に ワークスペース名.c9users.io:8080/users/1 等になり、目当てのページにアクセスできるようになりますが、この場合、1.が実現できなくなります。

ワークスペース名.c9users.io:8080/usersにアクセスした時のエラーメッセージ:

4/4
ErrorException in 2a5576d3a3140862ca55ac1bbbf9bb65 line 20:
Undefined variable: user (View: /home/ubuntu/workspace/microposts/resources/views/commons/navbar.blade.php) (View: /home/ubuntu/workspace/microposts/resources/views/commons/navbar.blade.php) (View: /home/ubuntu/workspace/microposts/resources/views/commons/navbar.blade.php)

3/4
ErrorException in 2a5576d3a3140862ca55ac1bbbf9bb65 line 20:
Undefined variable: user (View: /home/ubuntu/workspace/microposts/resources/views/commons/navbar.blade.php) (View: /home/ubuntu/workspace/microposts/resources/views/commons/navbar.blade.php)

2/4
ErrorException in 2a5576d3a3140862ca55ac1bbbf9bb65 line 20:
Undefined variable: user (View: /home/ubuntu/workspace/microposts/resources/views/commons/navbar.blade.php)

1/4
ErrorException in 2a5576d3a3140862ca55ac1bbbf9bb65 line 20:
Undefined variable: user

 ヒントになりそうなこと

UsersController.php の index() に以下を追記すると1.と2.を両方とも実現することができます。

public function index()
    {
        $users = User::paginate(10);
        $user = User::find(2); // 追記。 動作確認としてidを直接指定。$userにログインユーザーのidを代入することができれば実現可能?

        return view('users.index', [
           'users' => $users,
           'user' => $user, // 追記。ユーザー一覧ページのviewに渡す
        ]);
    }

 試したこと

UsersController.php の show(){} の様に、

public function index($id)
    {
        $users = User::paginate(10);
        $user = User::find($id);

        return view('users.index', [
           'users' => $users,
           'user' => $user,
        ]);
    }


とすれば index.blade.php にログインユーザーの情報を渡すことができるのではないかと思ったのですが、それをすると Missing Argument エラーが出てしまいます。

1/1
ErrorException in UsersController.php line 14:
Missing argument 1 for App\Http\Controllers\UsersController::index()

 該当のソースコード

※GitHubで下記以外のファイルのコードもご確認頂けます。
https://github.com/KIYZ/sns

/microposts/resources/views/commons/navbar.blade.php

<header>
    <nav class="navbar navbar-inverse navbar-static-top">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
                <a class="navbar-brand" href="/">Microposts</a>
            </div>
            <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
                <ul class="nav navbar-nav navbar-right">
                    @if (Auth::check())
                        <li>{!! link_to_route('users.index', 'Users') !!}</li>
                        <li class="dropdown">
                            <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">{{ Auth::user()->name }} <span class="caret"></span></a>
                            <ul class="dropdown-menu">
                                <li>{!! link_to_route('users.show', 'My profile', ['id' => $user->id]) !!}</li>
                                <li>{!! link_to_route('micropost.favorites', 'Favorites') !!}</li> <!--TODO-->
                                <li role="separator" class="divider"></li>
                                <li>{!! link_to_route('logout.get', 'Logout') !!}</li>
                            </ul>
                        </li>
                    @else
                        <li>{!! link_to_route('signup.get', 'Signup') !!}</li>
                        <li>{!! link_to_route('login.get', 'Login') !!}</li>
                    @endif
                </ul>
            </div>
        </div>
    </nav>
</header>

/microposts/app/Http/Controllers/UsersController.php

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Http\Requests;
use App\Http\Controllers\Controller;

use App\User;

class UsersController extends Controller
{
    public function index()
    {
        $users = User::paginate(10);

        return view('users.index', [
           'users' => $users,
        ]);
    }

    public function show($id)
    {
        $user = User::find($id);
        $microposts = $user->microposts()->orderBy('created_at', 'DESC')->paginate(10);

        $data = [
            'user' => $user,
            'microposts' => $microposts,
        ];

        $data += $this->counts($user);

        return view('users.show', $data);
    }

    public function followings($id)
    {
        $user = User::find($id);
        $followings = $user->followings()->paginate(10);

        $data = [
            'user' => $user,
            'users' => $followings,
        ];

        $data += $this->counts($user);

        return view('users.followings', $data);
    }

    public function followers($id)
    {
        $user = User::find($id);
        $followers = $user->followers()->paginate(10);

        $data = [
            'user' => $user,
            'users' => $followers,
        ];

        $data += $this->counts($user);

        return view('users.followers', $data);
    }

    public function favorites($id)
    {
        $user = User::find($id);
        $microposts = $user->microposts()->orderBy('created_at', 'desc')->paginate(10);
        $favorites = $user->favorites()->orderBy('created_at', 'desc')->paginate(10);
        foreach ($microposts as $micropost) {
            if ($micropost->id === $favorites->user_id) {
                $favorite_microposts = $micropost;
            }
        }

        $data = [
            'user' => $user,
            'microposts' => $microposts,
            'favorites' => $favorites,
            'favorite_microposts' => $favorite_microposts,
        ];

        $data += $this->counts($user);

        return view('users.favorites', $data);
    }
}

/microposts/app/Http/routes.php

<?php

Route::get('/', 'WelcomeController@index');

// ユーザー登録
Route::get('signup', 'Auth\AuthController@getRegister')->name('signup.get');
Route::post('signup', 'Auth\AuthController@postRegister')->name('signup.post');

// ログイン
Route::get('login', 'Auth\AuthController@getLogin')->name('login.get');
Route::post('login', 'Auth\AuthController@postLogin')->name('login.post');
Route::get('logout', 'Auth\AuthController@getLogout')->name('logout.get');

// ログイン認証を必要とするルーティンググループ
Route::group(['middleware' => 'auth'], function () {
    Route::resource('users', 'UsersController', ['only' => ['index', 'show']]); // ユーザ一覧, ユーザ詳細
    Route::group(['prefix' => 'users/{id}'], function () {
        Route::post('follow', 'UserFollowController@store')->name('user.follow');
        Route::delete('unfollow', 'UserFollowController@destroy')->name('user.unfollow');
        Route::get('followings', 'UsersController@followings')->name('users.followings');
        Route::get('followers', 'UsersController@followers')->name('users.followers');
    });
    Route::resource('microposts', 'MicropostsController', ['only' => ['store', 'destroy']]); // 投稿の保存, 投稿の削除

    // お気に入り
    Route::post('favorite/{id}', 'FavoritesController@store')->name('micropost.favorite');
    Route::delete('unfavorite/{id}', 'FavoritesController@destroy')->name('micropost.unfavorite');
    Route::get('favorites/{id}', 'UsersController@favorites')->name('micropost.favorites');
});

よろしくお願いします。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

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

  • mix-peach

    2018/04/12 17:53

    おもしろそうなので、gitまでソースを見に行ってみました。ちょっと不思議なのですが、$user には今ログインしている人の情報とかが入っているはずなんです、よね? でも、一覧では foreach($users as $user) で代入していて、詳細ではviewに渡すデータで、 ’user’ に「id指定したuserの情報」を入れていたように見えたんですが、これらは問題なく動くのでしょうか??

    キャンセル

  • KIYZ

    2018/04/12 18:12

    コメント誠にありがとうございます。UsersControllerのindex(){}内で定義した$userにログインしたユーザーの情報を代入し、それをindex.bladeに渡すことができれば実装できるのではないかと思っています。同ファイルのshow(){}の様にindex($id) {$user = User::find($id);}とすればそれが実現できるのではないかとも思ったのですが、それをするとMissing Argumentエラーが出てしまいます。詳細ページは現状のコードで正常に表示できています。(ページ内の一部の機能は実装途中です。)

    キャンセル

回答 2

+1

ログイン情報の部分、解決されたようなのと、
質問の回答とは若干異なる内容なので、回答欄に書き込むのはどうかな・・とは思ったんですが。

根本的にバグになり得る部分なので、一応突っ込んでおこうかと思いまして、
コメントだと、うまいこと改行ができないので、回答に書かせてもらいます・・!


先にコメントで書いたように、ユーザー一覧のviewのお話です。

@foreach ($users as $user)
@endforeach

もし、このままにしているのであれば、

今は出力される順序のおかげで、
commons.navbarで利用しているのはコントローラーから渡した $user(ログインユーザーの情報)
になっているようですが、

実際には、

<!-- ここまでは、$userは、コントローラで渡した「ログインユーザーの情報」が入ってる -->

@foreach ($users as $user)
@endforeach

<!-- ここからは、$userには、$usersから最後に取り出した情報が入っている

となり、一覧を出した後は、
最後に取り出したレコードが「たまたまログインユーザーと同じユーザー」でない限り、
ログインユーザー情報は、もう参照できなくなります。

・ログインユーザーの情報を入れている変数名は $auth_user など、明確に見分けられるようにする。
・ログインユーザーの情報の取得を1回する以外は参照のみにとどめ、同じ変数は使わない。

というのがおススメ。。というか一般的?かなぁと。

※更新された?gitは見に行ってないので、もしすでに対応済みだった場合は、とんだ余計なお世話を失礼しました・・

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/04/13 21:33 編集

    自分はプログラミング初心者ですのでこのようなご指摘は大変有り難いです。本当にありがとうございます。
    そちらの問題については気づいておりませんでした。

    一先ず @foreach ($users as $user) で取り出した $user とログインユーザーを格納するための変数を別にするために UsersController のコードを下記のようにして、

    class UsersController extends Controller
    {
    public function index()
    {
    $users = User::paginate(10);
    $auth_user = \Auth::user();

    return view('users.index', [
    'users' => $users,
    'auth_user' => $auth_user,
    ]);
    }
    }

    navbar.blade.php のプロフィールページへのリンク生成の部分のコードを下記のようにしたのですが、

    <li>{!! link_to_route('users.show', 'My profile', ['id' => $auth_user->id]) !!}</li>

    / にアクセスすると、

    Undefined variable: auth_user (View: /home/ubuntu/workspace/microposts/resources/views/commons/navbar.blade.php)

    となってしまいました。

    Laravelの公式ドキュメントのブレードテンプレートのページを読み返して @include('view.name', ['some' => 'data']) のようにして $auth_user を navbar.blade.php に渡すように試みたのですが、色々と検証してみた結果、 index.blade.php までしか渡っていないようでした。

    コントローラーで取得したデータを目的のビューに渡す流れは作れていたと思っていたので、 UsersController で $auth_user とする前の状態で期待通りの動作をしていた根拠も分からなくなってしまいました…。
    UsersController で取得したログインユーザーの情報は、
    index.blade.php => app.blade.php => navbar.blade.php
    という順番で渡せていたと思っていたのですが、この辺のコーディングにも問題がありそうでしょうか?

    >ログインユーザーの情報の取得を1回する以外は参照のみにとどめ、同じ変数は使わない。
    @foreach ($users as $user) で取り出した $user と UsersController で取得したログインユーザーの情報は別名の変数に格納するようにして、一度 UsersController でログインユーザー情報を取得した後は、そのデータが必要となった時は取得時に格納した変数から取り出すようにする。
    という認識で合っていますか?

    キャンセル

  • 2018/04/14 18:33 編集

    新規の質問として投稿させて頂きました。
    https://teratail.com/questions/121848

    キャンセル

check解決した方法

0

 UsersController の index(){} 内で \Auth::User() を用いてログイン中のユーザーを取得して view に渡す

ナビバーにユーザープロフィールページへのリンクを実装してから /users (ユーザー一覧ページ) にアクセスすると

Undefined variable: user (View: /home/ubuntu/workspace/microposts/resources/views/commons/navbar.blade.php)

となるのは、viewの継承 (user.index => layouts.app => commons.navbar) を経て最終的に navbar.blade.php にログイン中のユーザーの情報が渡っていない状態でユーザープロフィールページへのリンクを生成するための link_to_route() メソッドの第3引数に ['id' => $user->id] を渡していたことが原因でした。

故に UsersController.php の index() {} 内を下記のようにコーディングすると1.と2.を両方とも実現することができました。

public function index()
    {
        $users = User::paginate(10);
        $user = \Auth::User();

        return view('users.index', [
           'users' => $users,
           'user' => $user,
        ]);
    }

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/04/12 19:44 編集

    問題が解決しましたのでGitHub上のコードは今後変更していきます。

    キャンセル

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

  • ただいまの回答率 90.22%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる
  • トップ
  • PHPに関する質問
  • LaravelでTwitterクローン開発 | ユーザー一覧ページの表示とユーザープロフィールページへのリンク生成