Laravelの基本はこちらから学べます

Laravel6×MAMPで掲示板を作ろう④




Postの詳細画面を作成する

一覧ページから投稿詳細画面に移動できるようにしましょう。

index.php

@foreach ($posts as $post)
  <div class="card-body">
      <h5 class="card-title">タイトル:{{ $post->title }}</h5>
      <p class="card-text">内容:{{ $post->body }}</p>
      <a href="{{ route('posts.show', $post->id) }}" class="btn btn-primary">詳細へ</a>
  </div>

aタグのhrefにshowメソッドのルートをセットしてあげましょう。

解説

{{ route('posts.show', $post->id) }}

posts.showに関しては、「どの」postの詳細にいくのかを指定して上げる必要がありますよね。

なので、「$post→id」を渡してあげています。

PostController.phpのshowメソッド

public function show($id)
    {
        $post = Post::find($id);

        return view('posts.show', compact('post'));
    }

show($id)で$idを受け取ります。この$idはユーザーが詳細ボタンをクリックし投稿のIDが入っています。

なので、Postモデルから$idを使って検索をしています。

検索した$postをviewにコンパクト関数で渡します。

次にshow.blade.phpの中身を作成していきましょう。

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card-header">
                <h5>タイトル:{{ $post->title }}</h5>
            </div>
            <div class="card-body">
                <p class="card-text">内容:{{ $post->body }}</p>
                <p>投稿日時:{{ $post->created_at }}</p>
            </div>
        </div>
    </div>
</div>
@endsection

コントローラーから渡された$postの情報を表示することで詳細ページを表示しています。

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/298b4e64-5f88-4239-abf4-b22eec243577/_2020-04-15_22.01.07.png

Postを編集できるようにする

まずは詳細画面から、編集画面に移動できるようにしましょう。

show.blade.php

<div class="card-body">
    <p class="card-text">内容:{{ $post->body }}</p>
    <p>投稿日時:{{ $post->created_at }}</p>
    <a href="{{ route('posts.edit', $post->id) }}" class="btn btn-primary">編集する</a>
</div>

aタグでPostControllerのeditメソッドに送っています。

こちらも詳細画面に送る時と同様に、「どの」記事を編集するのかを「$post→id」で渡します。

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7774898c-f8db-476d-ba0c-0435dc9d8bb9/_2020-04-15_21.59.37.png

PostController

public function edit($id)
    {
        $post = Post::find($id);

        return view('posts.edit', compact('post'));
    }

こちらはshowと同じです。

受け取った$idでPostモデルを通してpostsを検索。

検索したものを、$postとしてedit.bladeに渡しています。

edit.blade

@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            @if ($errors->any())
                <div class="alert alert-danger">
                    <ul>
                        @foreach ($errors->all() as $error)
                            <li>{{ $error }}</li>
                        @endforeach
                    </ul>
                </div>
            @endif
            <form action="{{ route('posts.update', $post->id) }}" method="POST">
            {{csrf_field()}}
            {{method_field('PATCH')}}
                <div class="form-group">
                    <label>タイトル</label>
                    <input type="text" class="form-control" value="{{ $post->title }}" name="title">
                </div>
                <div class="form-group">
                    <label>内容</label>
                    <textarea class="form-control" rows="5" name="body">{{ $post->body }}</textarea>
                </div>
                <button type="submit" class="btn btn-primary">更新する</button>
            </form>
        </div>
    </div>
</div>
@endsection

formがまた出てきました。次はPATCHで更新処理をします。

ここで注目するべきは{{ method_field(‘PATCH’) }}です。

formタグ内のmethod=”PATCH”と書きたくなるのですが、、

HTMLフォームはPUT、PATCH、DELETEアクションをサポートしていません。。要は上記のように記載してもエラーが出てしまいます。

よって、擬似フォームメソッドというのを使います。下のリファレンスの「擬似フォームメソッド」を流し見するといいかなと思います。

Laravel 6.x ルーティング

edit.bladeでは初期値を持たせていますので、下記のように初期値を持ちつつ編集できるようになっているかなと思います。

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/da15955f-0fff-429b-ba4a-8725aaadac64/_2020-04-15_22.32.20.png

次に編集内容をPostControllerのupdateメソッドを変更していきましょう。

public function update(PostRequest $request, $id)
    {
        $post = Post::find($id);

        $post -> title    = $request -> title;
        $post -> body     = $request -> body;

        $post -> save();

        return view('posts.show', compact('post'));
    }
  1. PostRequestを通す。 初期値ではRequestになっていますので、PostRequestとしてバリデーションを行いましょう。
  2. $idで更新したいPostを探す。 こちらは、showでも同様のことを行っていました。
  3. $requestで受け取ったユーザーの入力値を$postに加えていく。 最後にsaveを忘れずにしましょう。
  4. user_idは???と思った方。鋭いですね。 ただ、postに関しては投稿したユーザーは変更がないはずなので変更しません。

ここまでできると、投稿の更新ができるようになったはずです!!

Postを削除できるようにする

CRUD機能の最後になりますが、Deleteをできるようにしてみましょう。

まずは削除ボタンを詳細画面におきましょう。

show.blade.php

<div class="card-body">
    <p class="card-text">内容:{{ $post->body }}</p>
    <p>投稿日時:{{ $post->created_at }}</p>
    <a href="{{ route('posts.edit', $post->id) }}" class="btn btn-primary">編集する</a>
    <form action='{{ route('posts.destroy', $post->id) }}' method='post'>
        {{ csrf_field() }}
        {{ method_field('DELETE') }}
        <input type='submit' value='削除' class="btn btn-danger" onclick='return confirm("削除しますか??");'>
    </form>
</div>

削除ボタンはformで書きます。

注目する点は{{ method_field(‘DELETE’) }}です。

HTMLフォームはPUT、PATCH、DELETEアクションをサポートしていません。よって擬似フォームメソッドを用いて送ります。

PostControllerのdestroyメソッドを編集していきます。

public function destroy($id)
    {
        $post = Post::find($id);

        $post -> delete();

        return redirect()->route('posts.index');
    }

ここでもfindを用いて、該当のPostを探しています。

そして探したものを削除、一覧ページに戻るように設定しました。

リレーションを使って投稿したユーザー名を表示する。

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/6a0b3c23-b795-49af-ab32-dff8cdfae73e/_2020-04-15_23.13.57.png

現在の一覧ページ。。。。

誰の記事かわからん!!!

ということで、ユーザー名を表示します。

リレーションを結ぶ。

現在のデータベースは以下の図。

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/faa74cd0-787d-455e-9871-799943e2c3b3/_2020-04-15_10.18.46.png

Userは複数のPostを持ち、複数のCommentをもつ

Postは複数のCommentを持つ。

この関係性を各モデルに記載することで、データの取得が容易になります。

Userは複数のPostを持つ  → User hasMany Posts / Post belongsTo User

Userは複数のCommentを持つ → User hasMany Comments / Comment belongsTo User

Postは複数のCommentを持つ  → Post hasMany Comments / Comment belongsTo Post

例としては

特定の$postから投稿したユーザーの情報をリレーションを伝って取得できるようになります。

User.php に以下を追加しましょう。

class User extends Authenticatable
{

------省略-------

public function posts()
    {
        return $this->hasMany('App\Post');
    }

public function comments()
    {
        return $this->hasMany('App\Comment');
    }
}

次に

Post.php

class Post extends Model
{
    protected $fillable = ['title', 'body'];

    public function comments()
    {
        return $this->hasMany('App\Comment');
    }

    public function user()
    {
        return $this->belongsTo('App\User');
    }
    
}

最後にComment.php

class Comment extends Model
{
    protected $fillable = ['body'];

    public function post()
    {
        return $this->belongsTo('App\Post');
    }

    public function user()
    {
        return $this->belongsTo('App\User');
    }
}

以上でリレーションを結ぶことができました。

それでは実際にリレーションを使って一覧ページに投稿したユーザーの名前を表示してみましょう。

PostController

public function index()
    {
        $posts = Post::all();
        $posts->load('user'); →追加
        return view('posts.index', compact('posts'));
    }

今回「Eagerロード 」を追加しました。下記の「Eagerロード」を流し見しましょう。

Laravel 6.x Eloquent:リレーション

簡潔にいうと、load(‘user’)で$postsに紐ずくuserにアクセスできるようにしています。

次をみてみましょう。

index.blade

<div class="card-body">
    <h5 class="card-title">タイトル:{{ $post->title }}</h5>
    <p class="card-text">内容:{{ $post->body }}</p>
    <p class="card-text">投稿者:{{ $post->user->name }}</p> →追加
    <a href="{{ route('posts.show', $post->id) }}" class="btn btn-primary">詳細へ</a>
</div>

このように、$post→user→name

とすることで、投稿に紐ずくuserの名前を取得することができます。

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/683ca14d-ff7d-4cd8-8e3c-9a5a1f0127a3/_2020-04-15_23.31.54.png

そうしましたら、show.bladeにもuserの名前を表示できるようにしておきましょう。

Postの編集削除は本人でなければできないようにする。

CRUD機能をつけることができましたね。

しかし、1点問題があります。

それは、投稿したユーザー以外もPostを編集削除できてしまう点です。

投稿したユーザー以外が編集、削除をしようとした場合にはエラー画面に飛ばすように処理を書きましょう。

PostController

public function edit($id)
    {
        $post = Post::find($id);

        if(Auth::id() !== $post->user_id){
            return abort(404);
        }

        return view('posts.edit', compact('post'));
    }

----省略------

public function update(PostRequest $request, $id)
    {
        $post = Post::find($id);

        if(Auth::id() !== $post->user_id){
            return abort(404);
        }

        $post -> title    = $request -> title;
        $post -> body     = $request -> body;

        $post -> save();

        return view('posts.show', compact('post'));
    }

---省略-----

public function destroy($id)
    {
        $post = Post::find($id);

        if(Auth::id() !== $post->user_id){
            return abort(404);
        }

        $post -> delete();

        return redirect()->route('posts.index');
    }

ここでは、edit、update、destroyメソッドに

Auth::id() →ログイン中ユーザーのid

$post→user_id →該当の投稿のuser_id

を比較して、異なっていた場合は404エラーに飛ばす処理を書きました。

これでOKです!

ここで、webアプリの変な箇所を修正していきましょう。

ヘッダーのLaravelをクリックした時に、一覧ページに飛べるようにしましょう。

resources views layouts app.blade.php

<div id="app">
        <nav class="navbar navbar-expand-md navbar-light bg-white shadow-sm">
            <div class="container">
              //<a class="navbar-brand" href="{{ url('/') }}">
		<a class="navbar-brand" href="{{ route('posts.index') }}">
                    {{ config('app.name', 'Laravel') }}
                </a>
                <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}">
                    <span class="navbar-toggler-icon"></span>
                </button>

これでOKです!

実際にヘッダーの「Laravel」をクリックして一覧画面に戻れるか確認をしてみましょう。

今回はここまで。また次回あいましょう!




コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です