39
33

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【全6回】Laravel5.8でTwitterっぽいSNSツールを作る(第3回ユーザ関連とフォロー機能)

Last updated at Posted at 2019-08-24

Laravelで始めるTwitter風(Twitterクローン)のSNSツール開発チュートリアル

概要

スクールとかの課題だったりLaravelを初めてみたいけど何を作ろうって迷ってる人向けによくあるTwitter風のWEBサイトを作ってみます。

前回

第3回はユーザ関連とフォロー関連の機能を追加していきます。

前提

  • PHPをある程度理解している
  • Laravelが使える環境がある
  • MVC構造をある程度理解している

環境

  • Mac
  • Homestead
  • Laravel 5.8

Controllerを作成

MVCで中枢の役割を担うControllerを作成していきます。

ResourceController

ResourceとはCRUD操作(作成、一覧、編集、削除など)を予め決められた処理を簡潔にできる機能のことです。
LaravelではartisanコマンドでControllerファイルを生成するとき後ろに--resourceと付けることでResourceに対応したControllerを自動的に生成してくれます。

言葉で言っても難しいと思うのでtweetsで表してみたいと思います。
※{tweet}の部分はIDが入ります。

HTTP動詞 URL アクション 役割
GET /tweets index 一覧表示
GET /tweets/create create 新規ツイート入力画面
POST /tweets store 新規ツイート投稿処理
GET /tweets/{tweet}/show show ツイート詳細画面
GET /tweets/{tweet}/edit edit ツイート編集画面
PUT/PATCH /tweets/{tweet} update ツイート編集処理
DELETE /tweets/{tweet} destory ツイート削除処理

Conrollerで書くとこうなります。

TweetsConroller.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class TweetsConroller extends Controller
{
    // 一覧表示
    public function index()
    {
        //
    }

    // 新規ツイート入力画面
    public function create()
    {
        //
    }

    // 新規ツイート投稿処理
    public function store(Request $request)
    {
        //
    }

    // ツイート詳細画面
    public function show($id)
    {
        //
    }

    // ツイート編集画面
    public function edit($id)
    {
        //
    }

    // ツイート編集処理
    public function update(Request $request, $id)
    {
        //
    }

    // ツイート削除処理
    public function destroy($id)
    {
        //
    }
}

どこでどの処理を行なっているのか分かりやすくなりましたね( ◠‿◠ )

参考にしました🙇‍♂️
Laravelのリソースコントローラを理解して使ってみよう!

では早速Resourceを利用してUsersControllerを作成していきます。

UsersController

php artisan make:controller UsersController --resource

これでapp/Http/Controllers/UsersController.phpが生成されたと思います。
作成したControllerのアクションとルートを紐づけていきましょう!

Routing

LaravelではControllerのアクション(メソッド)に紐づける際はroutesweb.phpに記述します。

routes/web.php
<?php

/*
|--------------------------------------------------------------------------
| 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::get('/home', 'HomeController@index')->name('home');

// ログイン状態
Route::group(['middleware' => 'auth'], function() {

    // ユーザ関連
    Route::resource('users', 'UsersController');

});

このクロージャーの中にルートを設定することでログインした時にしかアクセス出来ないようにします。

Route::group(['middleware' => 'auth'], function() {
    //
});

Resourceを使ったことない人なら記述これだけで済むの??って思うかもしれませんが、ResourceControllerにすることでシステムが自動的にそれぞれのアクションに紐づけてくれます。

Route::resource('users', 'UsersController');

今回のユーザ機能では一覧/詳細/編集/更新のみを使用するので第3引数にonlyと記述して使うアクションのみを設定しましょう。

Route::resource('users', 'UsersController', ['only' => ['index', 'show', 'edit', 'update']]);

これでRoutingの設定は終わりです。

ユーザを取得

ユーザ一覧表示画面

一覧表示なのでUsersControllerindexに書いていきます。

Controller

ユーザを取得するgetAllUsers()というメソッドにログインしているユーザIDを引数で渡しています。
Modelから返ってきた結果をViewに返します。

後で使うので色々useしておきます。

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

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use App\Models\User;
use App\Models\Tweet;
use App\Models\Follower;

class UsersController extends Controller
{
    public function index(User $user)
    {
        $all_users = $user->getAllUsers(auth()->user()->id);
        
        return view('users.index', [
            'all_users'  => $all_users
        ]);
    }
}
メソッドインジェクション

index()の引数はどこから来たの??って思うかもしれません。
LaravelにはDI(依存性の注入)というのが内蔵されており、メソッドの引数にインジェクトしたいオブジェクトを書くだけで、そのインスタンスが使用できます。

これをメソッドインジェクションと呼びます( ◠‿◠ )

public function index(User $user)
{
    $all_users = $user->getAllUsers(auth()->user()->id);
}

つまり下記の様にわざわざインスタンスを発行する必要がなく、Userクラスに依存されないということです。

public function index()
{
    $user = new User;
    $all_users = $user->getAllUsers(auth()->user()->id);
}

もちろんメソッド単位ではなく__constructに書くコンストラクタインジェクションというのもあります。

話が逸れたのでModelにメソッドを書いていきます。。

Model

引数で受け取ったログインしているユーザを除くユーザを1ページにつき5名取得しています。

app/Models/User.php
    public function getAllUsers(Int $user_id)
    {
        return $this->Where('id', '<>', $user_id)->paginate(5);
    }

View

resources/views/usersというディレクトリを作成し、そのディレクトリ内に
index.blade.phpファイルを作成してください。

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

@section('content')
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-8">
                @foreach ($all_users as $user)
                    <div class="card">
                        <div class="card-haeder p-3 w-100 d-flex">
                            <img src="{{ $user->profile_image }}" class="rounded-circle" width="50" height="50">
                            <div class="ml-2 d-flex flex-column">
                                <p class="mb-0">{{ $user->name }}</p>
                                <a href="{{ url('users/' .$user->id) }}" class="text-secondary">{{ $user->screen_name }}</a>
                            </div>
                        </div>
                    </div>
                @endforeach
            </div>
        </div>
        <div class="my-4 d-flex justify-content-center">
            {{ $all_users->links() }}
        </div>
    </div>
@endsection

一覧表示

自身の環境で/usersを叩けばこの様に表示できているはずです

スクリーンショット 2019-08-20 22.41.02.png

フォロー関連の処理

では作成したユーザ一覧画面にフォローする機能とフォローを解除する機能ついでにフォローされているかの処理を書いていきます。

フォロー/フォロー解除/フォローされているか

Controller

フォローとフォロー解除はresourceのどのアクションにも該当しないので、独自に追加します。

app/Http/Controllers/UsersController.php
    // フォロー
    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();
        }
    }

Model

app/Models/User.php
    // フォローする
    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 (boolean) $this->follows()->where('followed_id', $user_id)->first(['id']);
    }

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

View

先ほど記述したindex.blade.phpにコードを追加しただけなので、そのままコピペでもいけます。

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

@section('content')
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-8">
                @foreach ($all_users as $user)
                    <div class="card">
                        <div class="card-haeder p-3 w-100 d-flex">
                            <img src="{{ $user->profile_image }}" class="rounded-circle" width="50" height="50">
                            <div class="ml-2 d-flex flex-column">
                                <p class="mb-0">{{ $user->name }}</p>
                                <a href="{{ url('users/' .$user->id) }}" class="text-secondary">{{ $user->screen_name }}</a>
                            </div>
                            @if (auth()->user()->isFollowed($user->id))
                                <div class="px-2">
                                    <span class="px-1 bg-secondary text-light">フォローされています</span>
                                </div>
                            @endif
                            <div class="d-flex justify-content-end flex-grow-1">
                                @if (auth()->user()->isFollowing($user->id))
                                    <form action="{{ route('unfollow', ['id' => $user->id]) }}" method="POST">
                                        {{ csrf_field() }}
                                        {{ method_field('DELETE') }}

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

                                        <button type="submit" class="btn btn-primary">フォローする</button>
                                    </form>
                                @endif
                            </div>
                        </div>
                    </div>
                @endforeach
            </div>
        </div>
        <div class="my-4 d-flex justify-content-center">
            {{ $all_users->links() }}
        </div>
    </div>
@endsection

フォローされているかの判定

resources/views/users/index.blade.php
@if (auth()->user()->isFollowed($user->id))

フォローしているかの判定

resources/views/users/index.blade.php
@if (auth()->user()->isFollowing($user->id))

Routing

先ほど設定したresourceのルーティングの下にフォロー/フォロー解除のアクションも紐づけておきます。

routes/web.php
// ログイン状態
Route::group(['middleware' => 'auth'], function() {

    // ユーザ関連
    Route::resource('users', 'UsersController', ['only' => ['index', 'show', 'edit', 'update']]);

    // フォロー/フォロー解除を追加
    Route::post('users/{user}/follow', 'UsersController@follow')->name('follow');
    Route::delete('users/{user}/unfollow', 'UsersController@unfollow')->name('unfollow');

});

一覧表示(フォロー/フォロー解除/フォロー関係)

これでフォロー関連は終わりです。フォロー/フォロー解除したりして試してみてください。
またログアウトして別のユーザに切り替えてフォロー関係も確かめてみてください。

スクリーンショット 2019-08-20 23.08.18.png

とりあえずここまでと言いたいがこのままだと後3回で終われる気がしないので、もう少し頑張ります。。😨😨

ユーザ詳細画面

この画面では以下の内容を実装していきます。

  • プロフィール
  • プロフィールが自身だった時に編集ボタンを追加
  • プロフィールが自身以外のユーザだった時にフォロー/フォロー解除/フォローされているかの判定を追加
  • 総ツイート数/フォロー数/フォロワー数の表示
  • ユーザがツイートしたタイムラインの表示

。。。大変そう😱😱
しかし先ほど作った一覧画面で使った処理を使いまわしていくのでそこまで大変ではないと思います!

Controller

$login_userは自身の情報です。これを元にプロフィールが自身か別のユーザかを判定します。
$userと被るのであえて$login_userにしました(ネーミングセンス皆無)
その$login_userを元にフォロバ関連の判定をしています。

$timelinesはユーザのツイート情報、$~~countってついてるのがカウント関連です。

app/Http/Controllers/UsersController.php
    public function show(User $user, Tweet $tweet, Follower $follower)
    {
        $login_user = auth()->user();
        $is_following = $login_user->isFollowing($user->id);
        $is_followed = $login_user->isFollowed($user->id);
        $timelines = $tweet->getUserTimeLine($user->id);
        $tweet_count = $tweet->getTweetCount($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,
            'timelines'      => $timelines,
            'tweet_count'    => $tweet_count,
            'follow_count'   => $follow_count,
            'follower_count' => $follower_count
        ]);
    }

Model

User

User.phpの処理は先ほどのメソッドを使うので省略します。

Tweet
app/Models/Tweet.php
    public function getUserTimeLine(Int $user_id)
    {
        return $this->where('user_id', $user_id)->orderBy('created_at', 'DESC')->paginate(50);
    }

    public function getTweetCount(Int $user_id)
    {
        return $this->where('user_id', $user_id)->count();
    }
Follower
app/Models/Follower.php
    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();
    }

View

show

resources/views/users/の中にshow.blade.phpファイルを作ってください。

resources/views/users/show.blade.php
@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8 mb-3">
            <div class="card">
                <div class="d-inline-flex">
                    <div class="p-3 d-flex flex-column">
                        <img src="{{ $user->profile_image }}" class="rounded-circle" width="100" height="100">
                        <div class="mt-3 d-flex flex-column">
                            <h4 class="mb-0 font-weight-bold">{{ $user->name }}</h4>
                            <span class="text-secondary">{{ $user->screen_name }}</span>
                        </div>
                    </div>
                    <div class="p-3 d-flex flex-column justify-content-between">
                        <div class="d-flex">
                            <div>
                                @if ($user->id === Auth::user()->id)
                                    <a href="{{ url('users/' .$user->id .'/edit') }}" class="btn btn-primary">プロフィールを編集する</a>
                                @else
                                    @if ($is_following)
                                        <form action="{{ route('unfollow', ['id' => $user->id]) }}" method="POST">
                                            {{ csrf_field() }}
                                            {{ method_field('DELETE') }}

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

                                            <button type="submit" class="btn btn-primary">フォローする</button>
                                        </form>
                                    @endif

                                    @if ($is_followed)
                                        <span class="mt-2 px-1 bg-secondary text-light">フォローされています</span>
                                    @endif
                                @endif
                            </div>
                        </div>
                        <div class="d-flex justify-content-end">
                            <div class="p-2 d-flex flex-column align-items-center">
                                <p class="font-weight-bold">ツイート数</p>
                                <span>{{ $tweet_count }}</span>
                            </div>
                            <div class="p-2 d-flex flex-column align-items-center">
                                <p class="font-weight-bold">フォロー数</p>
                                <span>{{ $follow_count }}</span>
                            </div>
                            <div class="p-2 d-flex flex-column align-items-center">
                                <p class="font-weight-bold">フォロワー数</p>
                                <span>{{ $follower_count }}</span>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
        @if (isset($timelines))
            @foreach ($timelines as $timeline)
                <div class="col-md-8 mb-3">
                    <div class="card">
                        <div class="card-haeder p-3 w-100 d-flex">
                            <img src="{{ $user->profile_image }}" class="rounded-circle" width="50" height="50">
                            <div class="ml-2 d-flex flex-column flex-grow-1">
                                <p class="mb-0">{{ $timeline->user->name }}</p>
                                <a href="{{ url('users/' .$timeline->user->id) }}" class="text-secondary">{{ $timeline->user->screen_name }}</a>
                            </div>
                            <div class="d-flex justify-content-end flex-grow-1">
                                <p class="mb-0 text-secondary">{{ $timeline->created_at->format('Y-m-d H:i') }}</p>
                            </div>
                        </div>
                        <div class="card-body">
                            {{ $timeline->text }}
                        </div>
                        <div class="card-footer py-1 d-flex justify-content-end bg-white">
                            @if ($timeline->user->id === Auth::user()->id)
                                <div class="dropdown mr-3 d-flex align-items-center">
                                    <a href="#" role="button" id="dropdownMenuLink" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                                        <i class="fas fa-ellipsis-v fa-fw"></i>
                                    </a>
                                    <div class="dropdown-menu" aria-labelledby="dropdownMenuLink">
                                        <form method="POST" action="{{ url('tweets/' .$timeline->id) }}" class="mb-0">
                                            @csrf
                                            @method('DELETE')

                                            <a href="{{ url('tweets/' .$timeline->id .'/edit') }}" class="dropdown-item">編集</a>
                                            <button type="submit" class="dropdown-item del-btn">削除</button>
                                        </form>
                                    </div>
                                </div>
                            @endif
                            <div class="mr-3 d-flex align-items-center">
                                <a href="#"><i class="far fa-comment fa-fw"></i></a>
                                <p class="mb-0 text-secondary">{{ count($timeline->comments) }}</p>
                            </div>
                            <div class="d-flex align-items-center">
                                <a href="#"><i class="far fa-comment fa-fw"></i></a>
                                <p class="mb-0 text-secondary">{{ count($timeline->favorites) }}</p>
                            </div>
                        </div>
                    </div>
                </div>
            @endforeach
        @endif
    </div>
    <div class="my-4 d-flex justify-content-center">
        {{ $timelines->links() }}
    </div>
</div>
@endsection

ついでに外枠のレイアウトのlayouts/app.blade.phpも修正していきます。

アイコンを使用したいのでFontAwesomeのSDNを読み込むのとツイッターっぽくメニューバーに画像を追加しました。

layouts/app.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <!-- CSRF Token -->
        <meta name="csrf-token" content="{{ csrf_token() }}">

        <title>{{ config('app.name', 'Laravel') }}</title>

        <!-- Scripts -->
        <script src="{{ asset('js/app.js') }}"></script>

        <!-- Fonts -->
        <link rel="dns-prefetch" href="//fonts.gstatic.com">
        <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet">

        <!-- Styles -->
        <link href="{{ asset('css/app.css') }}" rel="stylesheet">
        <!-- Font Awesome -->
        <link href="https://use.fontawesome.com/releases/v5.6.1/css/all.css" rel="stylesheet">
    </head>
    <body>
        <div id="app">
            <nav class="navbar navbar-expand-md navbar-light bg-white shadow-sm">
                <div class="container">
                    <a class="navbar-brand" href="{{ url('/') }}">
                        {{ 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>

                    <div class="collapse navbar-collapse" id="navbarSupportedContent">
                        <!-- Left Side Of Navbar -->
                        <ul class="navbar-nav mr-auto">

                        </ul>

                        <!-- Right Side Of Navbar -->
                        <ul class="navbar-nav ml-auto align-items-center">
                            <!-- Authentication Links -->
                            @guest
                                <li class="nav-item">
                                    <a class="nav-link" href="{{ route('login') }}">{{ __('Login') }}</a>
                                </li>
                                @if (Route::has('register'))
                                    <li class="nav-item">
                                        <a class="nav-link" href="{{ route('register') }}">{{ __('Register') }}</a>
                                    </li>
                                @endif
                            @else
                                <li class="nav-item">
                                    <img src="{{ auth()->user()->profile_image }}" class="rounded-circle" width="50" height="50">
                                </li>
                                <li class="nav-item dropdown">
                                    <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
                                        {{ auth()->user()->name }} <span class="caret"></span>
                                    </a>

                                    <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
                                        <a href="{{ url('users/' .auth()->user()->id) }}" class="dropdown-item">プロフィール</a>
                                        <a href="{{ route('logout') }}" class="dropdown-item"
                                        onclick="event.preventDefault();
                                                        document.getElementById('logout-form').submit();">
                                            {{ __('Logout') }}
                                        </a>

                                        <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
                                            @csrf
                                        </form>
                                    </div>
                                </li>
                            @endguest
                        </ul>
                    </div>
                </div>
            </nav>

            <main class="py-4">
                @yield('content')
            </main>
        </div>
    </body>
</html>

これで一通り詳細画面は出来上がったので画面を開いてみます🤳

ユーザ詳細画面

ログインしているユーザのIDが1の場合/users/1を開くとこの様になっていると思います!

スクリーンショット 2019-08-24 11.40.16.png

別のユーザの詳細画面を叩くとこうなっているはずです!

users/2

スクリーンショット 2019-08-24 11.50.21.png

ユーザ編集

もうひと頑張り😨😨

Controller

$requestで取得したデータをValidator::makeを使ってバリデーションをかけていきます( ◠‿◠ )

Rule::unique('users')->ignore($user->id)の部分はユニークに設定しているscreen_nameemailを自身のIDの時だけ無効にするという設定です。
これを設定しておかないとバリデーションで弾かれてしまいます。

app/Http/Controllers/UsersController.php
    public function edit(User $user)
    {
        return view('users.edit', ['user' => $user]);
    }

    public function update(Request $request, User $user)
    {
        $data = $request->all();
        $validator = Validator::make($data, [
            'screen_name'   => ['required', 'string', 'max:50', Rule::unique('users')->ignore($user->id)],
            'name'          => ['required', 'string', 'max:255'],
            'profile_image' => ['file', 'image', 'mimes:jpeg,png,jpg', 'max:2048'],
            'email'         => ['required', 'string', 'email', 'max:255', Rule::unique('users')->ignore($user->id)]
        ]);
        $validator->validate();
        $user->updateProfile($data);

        return redirect('users/'.$user->id);
    }
Model

$paramsの中に画像があれば処理を分けています。

$file_name = $params['profile_image']->store('public/profile_image/');
こうすることで画像ファイルが/storage/app/public/profile_image/に保存されます。

app/Models/User.php
    public function updateProfile(Array $params)
    {
        if (isset($params['profile_image'])) {
            $file_name = $params['profile_image']->store('public/profile_image/');

            $this::where('id', $this->id)
                ->update([
                    'screen_name'   => $params['screen_name'],
                    'name'          => $params['name'],
                    'profile_image' => basename($file_name),
                    'email'         => $params['email'],
                ]);
        } else {
            $this::where('id', $this->id)
                ->update([
                    'screen_name'   => $params['screen_name'],
                    'name'          => $params['name'],
                    'email'         => $params['email'],
                ]); 
        }
        
        return;
    }
View

resources/views/users/の中にedit.blade.phpファイルを作ってください。

resources/views/users/edit.blade.php
@extends('layouts.app')

@section('content')
<div class="container">
    <div class="row justify-content-center">
        <div class="col-md-8">
            <div class="card">
                <div class="card-header">Update</div>

                <div class="card-body">
                    <form method="POST" action="{{ url('users/' .$user->id) }}" enctype="multipart/form-data">
                        @csrf
                        @method('PUT')

                        <div class="form-group row align-items-center">
                            <label for="profile_image" class="col-md-4 col-form-label text-md-right">{{ __('Profile Image') }}</label>

                            <div class="col-md-6 d-flex align-items-center">
                                <img src="{{ $user->profile_image }}" class="mr-2 rounded-circle" width="80" height="80" alt="profile_image">
                                <input type="file" name="profile_image" class="@error('profile_image') is-invalid @enderror" autocomplete="profile_image">

                                @error('profile_image')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row">
                            <label for="screen_name" class="col-md-4 col-form-label text-md-right">{{ __('Account Name') }}</label>

                            <div class="col-md-6">
                                <input id="screen_name" type="text" class="form-control @error('screen_name') is-invalid @enderror" name="screen_name" value="{{ $user->screen_name }}" required autocomplete="screen_name" autofocus>

                                @error('screen_name')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row">
                            <label for="name" class="col-md-4 col-form-label text-md-right">{{ __('Name') }}</label>

                            <div class="col-md-6">
                                <input id="name" type="text" class="form-control @error('name') is-invalid @enderror" name="name" value="{{ $user->name }}" required autocomplete="name" autofocus>

                                @error('name')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row">
                            <label for="email" class="col-md-4 col-form-label text-md-right">{{ __('E-Mail Address') }}</label>

                            <div class="col-md-6">
                                <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ $user->email }}" required autocomplete="email">

                                @error('email')
                                    <span class="invalid-feedback" role="alert">
                                        <strong>{{ $message }}</strong>
                                    </span>
                                @enderror
                            </div>
                        </div>

                        <div class="form-group row mb-0">
                            <div class="col-md-6 offset-md-4">
                                <button type="submit" class="btn btn-primary">更新する</button>
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>
</div>
@endsection

一旦これで更新は完了です!
アカウント名や画像を更新して確認してみて下さい!

しかし保存した画像を読み込むために/storage/app/public/profile_image/にシンボリックリンクを設定しないといけないのでシンボリックリンクを設定します。

シンボリックリンクとは簡単に言うと、ショートカットのようなものです。
あるファイルやフォルダにシンボリックリンクを貼ることで、別のパス(別の場所)にあるファイルやフォルダを参照する事が出来ます。

シンボリックリンクを設定する必要は??

Laravelのドキュメントルート(アプリケーションを公開するときに参照されるフォルダ)がpublicになっています。
そのpublicフォルダにファイルや処理の全てが集約されています。
そのため保存した画像もpublicディレクトリ内に存在しないとアクセスする事ができません。
そこでシンボリックリンク を利用してstorageディレクトリにアクセスできるようにします。

Laravelのシンボリックリンク

Laravelではstorageのシンボリックリンクをartisanコマンドで設定できます( ˘ω˘ )

vagrant sshでプロジェクトのフォルダに移動して以下のコマンドを叩いてください。
それだけで勝手にpublicフォルダにstorageのシンボリックリンクが作成されます。

php artisan storage:link

あとは画像を読み込んでいる(<img src="">)以下のコード部分を修正してください!!

  • users/index.blade.php
  • users/show.blade.php
  • users/edit.blade.php
src="{{ $user->profile_image }}"

src="{{ asset('storage/profile_image/' .$user->profile_image) }}"
  • layouts/app.blade.php
src="{{ auth()->user()->profile_image }}"

src="{{ asset('storage/profile_image/' .auth()->user()->profile_image) }}"

結果!!

スクリーンショット 2019-08-24 16.59.31.png

ちなみに画像は好きなバスケ選のラッセル・ウェストブルック(聞いてない)

これにて第3回は終了です( ˘ω˘ )

次回 -> 【全6回】Laravel5.8でTwitterっぽいSNSツールを作る(第4回ツイートのCRUD機能)

39
33
9

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
39
33

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?