takanori-matsushita / laravel-practice

http://laraveltutorial.herokuapp.com/
0 stars 0 forks source link

rails tutorial 7章をlaravelで実装 #5

Open takanori-matsushita opened 4 years ago

takanori-matsushita commented 4 years ago

branch: sign-up

takanori-matsushita commented 4 years ago

7.1 ユーザーを表示する

7.1.1 デバッグとRails環境

Laravelのデバッグはデバッグバーを入れると便利らしい。

cd [Laravei-project]
composer require barryvdh/laravel-debugbar

config/app.php

'providers' => [
        :
  Barryvdh\Debugbar\ServiceProvider::class,
  ],

'aliases' => [
        :
  'Debugbar' => Barryvdh\Debugbar\Facade::class,
  ],

.env

APP_DEBUG=true

.envファイルを編集後はphp artisan config:clearで再読み込み

スクリーンショット 2020-04-17 3 20 45

こんな感じ感じのバーが下部に出てきたら完了。

[参考]デバッグバー導入 [参考]デバッグバーの使い方 [gitリポジトリ]debugbar

takanori-matsushita commented 4 years ago

リスト 7.1:, 7.2 省略

takanori-matsushita commented 4 years ago

演習

  1. スクリーンショット 2020-04-17 14 07 25
  2. 省略

takanori-matsushita commented 4 years ago

7.1.2 Usersリソース

$ php artisan tinker
>>> User::count()
=> 1
>>> User::first()
=> App\User {#3118
     id: 1,
     name: "Michael Hartl",
     email: "mhartl@example.com",
     email_verified_at: null,
     created_at: "2020-04-16 17:46:38",
     updated_at: "2020-04-16 17:46:38",
   }
takanori-matsushita commented 4 years ago

リスト 7.3: Usersリソースをroutesファイルに追加する

Route::get('/', 'StaticPagesController@home')->name('root');
Route::get('/help', 'StaticPagesController@help')->name('help');
Route::get('/about', 'StaticPagesController@about')->name('about');
Route::get('/contact', 'StaticPagesController@contact')->name('contact');

Route::get('users/signup', 'UsersController@new')->name('users.signup');
Route::resource('users', 'UsersController');  //追加

resourceを使った場合のルート一覧

|        | GET|HEAD  | users                         | users.index           | App\Http\Controllers\UsersController@index                    | web                                                  |
|        | POST      | users                         | users.store           | App\Http\Controllers\UsersController@store                    | web                                                  |
|        | GET|HEAD  | users/create                  | users.create          | App\Http\Controllers\UsersController@create                   | web                                                  |
|        | GET|HEAD  | users/{user}                  | users.show            | App\Http\Controllers\UsersController@show                     | web                                                  |
|        | PUT|PATCH | users/{user}                  | users.update          | App\Http\Controllers\UsersController@update                   | web                                                  |
|        | DELETE    | users/{user}                  | users.destroy         | App\Http\Controllers\UsersController@destroy                  | web                                                  |
|        | GET|HEAD  | users/{user}/edit             | users.edit            | App\Http\Controllers\UsersController@edit                     | web                                                  |
takanori-matsushita commented 4 years ago

リスト 7.4: ユーザー情報を表示するための仮のビュー resources/views/users/show.blade.php を作成

@php
$title = ''
@endphp
@extends('layouts.layout')
@section('content')
{{$user->name}},{{$user->email}}
@endsection
takanori-matsushita commented 4 years ago

リスト 7.5: Usersコントローラのshowアクション app/Http/Controllers/UsersController.php

use App\User;
  :
  public function show($id)
  {
    $user = User::find($id);
    return view('users.show', ['user' => $user]);
  }
スクリーンショット 2020-04-17 15 41 42
takanori-matsushita commented 4 years ago

演習

  1. @php
    $title = ''
    @endphp
    @extends('layouts.layout')
    @section('content')
    {{$user->name}},{{$user->email}},{{$user->created_at}},{{$user->updated_at}}
    @endsection
  2. 現在時刻:{{date("Y-m-d H:i:s")}}
takanori-matsushita commented 4 years ago

7.1.3 debuggerメソッド

リスト 7.6: eval(\Psy\sh())をUsersコントローラに差し込む

  public function show($id)
  {
    $user = User::find($id);
    eval(\Psy\sh());  //追加
    return view('users.show', ['user' => $user]);
  }

/users/1 をリロードしたら以下のようになる。

スクリーンショット 2020-04-17 16 03 54

$user->name
=> "Michael Hartl"
$user->email
=> "mhartl@example.com"
$id
=> "1"
takanori-matsushita commented 4 years ago

リスト 7.7: eval(\Psy\sh());をUsersコントローラーから取り外す

  public function show($id)
  {
    $user = User::find($id);
    return view('users.show', ['user' => $user]);
  }
takanori-matsushita commented 4 years ago

演習

  1. 省略

  2. $user
    PHP Notice:  Undefined variable: user in /Users/matsushitatakanori/Desktop/codebase/tech-traning/laravel-practiceeval()'d code on line 3
    => null
takanori-matsushita commented 4 years ago

リスト 7.8: ユーザー表示ビューに名前とGravatarを表示する resources/views/users/show.blade.php

@php
$title = $user->name
@endphp
@extends('layouts.layout')
@section('content')
<img src={{gravator_for($user)}}>
{{$user->name}}
@endsection
takanori-matsushita commented 4 years ago

リスト 7.9: gravatar_forヘルパーメソッドを定義する app/Helpers/UsersHelper.php を作成

<?php
function gravator_for($user)
{
  $gravator_id = md5(strtolower(trim($user->email)));
  return "http://www.gravatar.com/avatar/" . $gravator_id;
}

[公式]Gravator API

Helperを作成したら、composer.jsonに以下を追加

"autoload": {
        :
        "files": [
            :
            "app/helpers/UsersHelper.php"
        ]
}

ターミナルで以下を実行。 composer dump-autoload

takanori-matsushita commented 4 years ago

データベース情報の更新

$ php artisan tinker
=> "Example User"
>>> $user->email = "example@railstutorial.org"
=> "example@railstutorial.org"
>>> $user->password = "foobar"
=> "foobar"
>>> $user->save()
=> true
takanori-matsushita commented 4 years ago

リスト 7.10: ユーザーのshowビューにサイドバーを追加する resources/views/users/show.blade.php

@php
$title = $user->name
@endphp
@extends('layouts.layout')
@section('content')
<div class="row">
  <aside class="col-md-4">
    <section class="user_info">
      <h1>
        <img src={{gravator_for($user)}}>
        {{$user->name}}
      </h1>
    </section>
  </aside>
</div>
@endsection
takanori-matsushita commented 4 years ago

**リスト 7.11: SCSSを使ってサイドバーなどのユーザー表示ページにスタイルを与える resources/sass/_custom.scss そのままコピペした後に以下のコマンドを実行 yarn dev

takanori-matsushita commented 4 years ago

演習

  1. 省略

  2. リスト 7.12: gravatar_forヘルパーにオプション引数を追加する app/Helpers/UsersHelper.php

    <?php
    function gravator_for($user, $options = ["size" => 80])
    {
    $gravator_id = md5(strtolower(trim($user->email)));
    $size = $options["size"];
    return "http://www.gravatar.com/avatar/" . $gravator_id . "?s=" . $size;
    }

    resources/views/users/show.blade.php <img src={{gravator_for($user, $options = ["size"=>50])}}>

  3. 省略

takanori-matsushita commented 4 years ago

7.2 ユーザー登録フォーム

7.2.1 form_forを使用する

リスト 7.14: newアクションに@user変数を追加する app/Http/Controllers/UsersController.php

  public function new()
  {
    $user = new User();
    return view('users.new', ["user"=>$user]);
  }
takanori-matsushita commented 4 years ago

Laravelでは、すでに用意されているUserモデルに対応する登録フォーム等があるので、それを導入する。

まずは、larabel/ui をインストールする。 composer require laravel/ui --dev

インストールが完了したら、以下のコマンドを実行。 php artisan ui bootstrap --auth

以下のファイルが生成される。

app/Http/Controllers/Auth/
app/Http/Controllers/HomeController.php
database/migrations/2014_10_12_100000_create_password_resets_table.php
resources/sass/_variables.scss
resources/views/auth/
resources/views/home.blade.php
resources/views/layouts/app.blade.php

※『Please run "npm install && npm run dev" to compile your fresh scaffolding.』と言われた場合は以下のコマンド実行 npm install or yarn install

resources/sass/app.scss ファイルが変更されているので、一番下の行に以下を追加。 @import "custom";

以下のコマンドを実行し、scssをビルドする。 npm run dev or yarn dev

usersのルート一覧 php artisan route:list

|        | GET|HEAD | api/user                      |                       | Closure                                                                | api,auth:api                                         |
|        | GET|HEAD | home                          | home                  | App\Http\Controllers\HomeController@index                              | web,auth                                             |
|        | GET|HEAD | login                         | login                 | App\Http\Controllers\Auth\LoginController@showLoginForm                | web,guest                                            |
|        | POST     | login                         |                       | App\Http\Controllers\Auth\LoginController@login                        | web,guest                                            |
|        | POST     | logout                        | logout                | App\Http\Controllers\Auth\LoginController@logout                       | web                                                  |
|        | GET|HEAD | password/confirm              | password.confirm      | App\Http\Controllers\Auth\ConfirmPasswordController@showConfirmForm    | web,auth                                             |
|        | POST     | password/confirm              |                       | App\Http\Controllers\Auth\ConfirmPasswordController@confirm            | web,auth                                             |
|        | POST     | password/email                | password.email        | App\Http\Controllers\Auth\ForgotPasswordController@sendResetLinkEmail  | web                                                  |
|        | GET|HEAD | password/reset                | password.request      | App\Http\Controllers\Auth\ForgotPasswordController@showLinkRequestForm | web                                                  |
|        | POST     | password/reset                | password.update       | App\Http\Controllers\Auth\ResetPasswordController@reset                | web                                                  |
|        | GET|HEAD | password/reset/{token}        | password.reset        | App\Http\Controllers\Auth\ResetPasswordController@showResetForm        | web                                                  |
|        | GET|HEAD | register                      | register              | App\Http\Controllers\Auth\RegisterController@showRegistrationForm      | web,guest                                            |
|        | POST     | register                      |                       | App\Http\Controllers\Auth\RegisterController@register                  | web,guest                                            |
takanori-matsushita commented 4 years ago

リスト 7.15: 新規ユーザーのためのユーザー登録フォーム php artisan ui bootstrap --authで生成された登録フォームを以下のように変更する。 resources/views/auth/register.blade.php

@php
$title = 'Sign up';
@endphp
@extends('layouts.layout')

@section('content')
<h1>Sign up</h1>
<div class="row">
  <div class="col-md-6 offset-md-3">
    <form method="POST" action="{{ route('users.create') }}">
      @csrf
      @include('shared.error_messages')
      <label for="name">Name</label>
      <input type="text" id="user_name" name="name" class="form-control @error('name') is-invalid @enderror" value="{{old('name')}}">
      <label for="email">Email</label>
      <input type="email" id="user_email" name="email" class="form-control @error('email') is-invalid @enderror" value="{{old('email')}}">
      <label for="password">Password</label>
      <input type="password" id="user_password" name="password" class="form-control @error('password') is-invalid @enderror">
      <label for="password_confirmation">Confirmation</label>
      <input type="password" id="user_password_confirmation" name="password_confirmation" class="form-control">
      <input type="submit" class="btn btn-primary" value="Create my account">
    </form>
  </div>
</div>
@endsection

form actionのルートが存在しないため、エラーで怒られる。そのため、ルートに以下を追加。

Route::post('/users', 'Auth\RegisterController@register')->name('users.create');
takanori-matsushita commented 4 years ago

リスト 7.16: ユーザー登録フォームのCSS resources/sass/_custom.scss そのままコピペし、以下のコマンドを実行。 yarn dev

takanori-matsushita commented 4 years ago

7.2.2 フォームHTML

リスト 7.17: 図 7.12のフォームのHTMLソース 省略

takanori-matsushita commented 4 years ago

演習

  1. CSRF対策のトークンを発行できないため。
takanori-matsushita commented 4 years ago

7.3 ユーザー登録失敗

7.3.1 正しいフォーム

リスト 7.18: ユーザー登録の失敗に対応できるcreateアクション app/Http/Controllers/Auth/RegisterController.php use RegistersUsers;をたどると、 vendor/laravel/ui/auth-backend/RegistersUsers.php がtraitとしてファイル分割されており、その中にrgisterアクションが用意されているため、そちらを利用する。

takanori-matsushita commented 4 years ago

7.3.2 Strong Parameters

省略

takanori-matsushita commented 4 years ago

7.3.3 エラーメッセージ

リスト 7.20: ユーザー登録失敗時にエラーメッセージが表示されるようにする resources/views/auth/register.blade.php

@php
$title = 'Sign up';
@endphp
@extends('layouts.layout')

@section('content')
<h1>Sign up</h1>
<div class="row">
  <div class="col-md-6 offset-md-3">
    <form method="POST" action="{{ route('users.create') }}">
      @csrf
      @include('shared.error_messages')  //追加・以下それぞれクラス追加
      <label for="name">Name</label>
      <input type="text" id="user_name" name="name" class="form-control">
      <label for="email">Email</label>
      <input type="email" id="user_email" name="email" class="form-control">
      <label for="password">Password</label>
      <input type="password" id="user_password" name="password" class="form-control">
      <label for="password_confirmation">Confirmation</label>
      <input type="password" id="user_password_confirmation" name="password_confirmation" class="form-control">
      <input type="submit" class="btn btn-primary" value="Create my account">
    </form>
  </div>
</div>
@endsection
takanori-matsushita commented 4 years ago

リスト 7.21: フォーム送信時にエラーメッセージを表示するためのパーシャル resources/views/shared/error_messages.blade.php

@if ($errors->any())
<div id="error_explanation">
  <div class="alert alert-danger">
    @if (count($errors) == 1)
    The form contains 1 error
    @elseif (count($errors) > 1)
    The form contains {{count($errors)}} errors
    @endif
  </div>
  <ul>
    @foreach ($errors->all() as $error)
    <li>{{$error}}</li>
    @endforeach
  </ul>
</div>
@endif
takanori-matsushita commented 4 years ago

リスト 7.22: エラーメッセージにスタイルを与えるためのCSS resources/sass/_custom.scss

#error_explanation {
    color: red;
    ul {
        color: red;
        margin: 0 0 30px 0;
    }
}

.field_with_errors {
    @extend .invalid-feedback;
    .form-control {
        color: $red;
    }
}

以下のコマンドを実行 yarn dev

takanori-matsushita commented 4 years ago

演習

  1. app/Http/Controllers/Auth/RegisterController.php のvalidatorメソッドを以下のように変更する。
    :
    'password' => ['required', 'string', 'min:5', 'confirmed'], //min:5に変更
  2. 省略
takanori-matsushita commented 4 years ago

7.3.4 失敗時のテスト

php artisan dusk:make UsersSignupTest

リスト 7.23: 無効なユーザー登録に対するテストgreen tests/Browser/UsersSignupTest.php

  public function testInvalidSignupInformation()
  {
    $this->browse(function (Browser $browser) {
      $browser->visit(route('users.signup'))
        ->type('name', '')
        ->type('email', 'user@invalid')
        ->type('password', 'foo')
        ->type('password_confirmation', 'bar')
        ->press('Create my account')
        ->assertPathIs('/signup')
        ->assertSee('The form contains 3 errors');
    });
  }
takanori-matsushita commented 4 years ago

リスト 7.24: green php artisan dusk

takanori-matsushita commented 4 years ago

演習

  1. 先程のテストファイルに以下を追加
    public function testInvalidSignupInformation()
    {
    $this->browse(function (Browser $browser) {
          :
        ->assertSee('The name field is required')
        ->assertSee('The password must be at least 8 characters.')
        ->assertSee('The password confirmation does not match.');
    });
    }
  2. Laravelでは、signupのままなので3,4も合わせて省略
takanori-matsushita commented 4 years ago

7.4 ユーザー登録成功

7.4.1 登録フォームの完成

リスト 7.28: 保存とリダイレクトを行う、userのcreateアクション 保存のコードはLaravel Authで実装されているので、リダイレクト先の設定を行う。 [参考]リダイレクト先変更 app/Http/Controllers/Auth/RegisterController.php

  protected $redirectTo = RouteServiceProvider::HOME; //この行を削除

  //redirectTOメソッドを実装
  protected function redirectTo()
  {
    return route('users.show', ['user' => \Auth::id()]);
  }
takanori-matsushita commented 4 years ago

リスト 7.29: ユーザー登録ページにフラッシュメッセージを追加する app/Http/Controllers/Auth/RegisterController.php

  protected function redirectTo()
  {
    session()->flash('success', 'Welcome to the Sample App!');
    return route('users.show', ['user' => \Auth::id()]);
  }
takanori-matsushita commented 4 years ago

リスト 7.30: コンソールでflashハッシュをイテレート (each do ... end) する

$ php artisan tinker
>>> $flash = ["success"=>"It worked!", "danger"=>"It failed."]
=> [
     "success" => "It worked!",
     "danger" => "It failed.",
   ]
>>> foreach($flash as $key => $value) {
           var_dump($key);
           var_dump($value);
        }
string(7) "success"
string(10) "It worked!"
string(6) "danger"
string(10) "It failed."
takanori-matsushita commented 4 years ago

リスト 7.31: flash変数の内容をWebサイトのレイアウトに追加する resources/views/layouts/flash.blade.php を作成

@if (Session::has('success'))
<div class="alert alert-success">{{session('success')}}</div>
@endif

resources/views/layouts/layout.blade.php

<!DOCTYPE html>
<html>

<head>
  <title>{{fullTitle($title)}}</title>
  @include('layouts.laravel_default')
  @include('layouts.shim')
</head>

<body>
  @include('layouts.header')
  <div class="container">
    @include('layouts.flash')
    @yield('content')
    @include('layouts.footer')
  </div>
</body>

</html>
takanori-matsushita commented 4 years ago

7.4.3 実際のユーザー登録

データベースのリセット php artisan migrate:refresh

takanori-matsushita commented 4 years ago

演習

  1. $ php artisan tinker
    >>> User::where("email", "example@railstutorial.org")->first()
    => App\User {#3136
     id: 1,
     name: "Rails Tutorial",
     email: "example@railstutorial.org",
     email_verified_at: null,
     created_at: "2020-04-18 23:03:03",
     updated_at: "2020-04-18 23:03:03",
    }
  2. 省略
takanori-matsushita commented 4 years ago

7.4.4 成功時のテスト

リスト 7.33: 有効なユーザー登録に対するテストgreen tests/Browser/UsersSignupTest.php

  public function testValidSignupInformation()
  {
    $this->browse(function (Browser $browser) {
      $browser->visit(route('users.signup'))
        ->type('name', 'Example User')
        ->type('email', 'user@example.com')
        ->type('password', 'password')
        ->type('password_confirmation', 'password')
        ->press('Create my account')
        ->assertPathIs('/users/1')
        ->assertSee('Welcome to the Sample App!')
        ->assertSee('Example User')
        ->visit('/users/1')
        ->assertDontSee('Welcome to the Sample App!');
    });
  }

テスト後にテストデータを削除するため、クラスの最初の行に以下を追加。 use DatabaseMigrations;

ここでDuskテストを実行してしまうと、データベースがリセットされるため、以下の設定を行ってから実行する。

takanori-matsushita commented 4 years ago

今後データベースを使ったテストは、本番用とテスト用で使用するデータベースを分けたほうが、利便性が高いため、以下のように設定する。

  1. laravel_practice_test データベースの作成

    $ psql -d laravel_practice
    laravel_practice=# CREATE DATABASE laravel_practice_test;
  2. Laravelプロジェクトルートで以下を実行。 cp .env .env.dusk.local

  3. .env.dusk.localを編集 DB_DATABASE=laravel_practice_test

  4. php artisan config:clear

takanori-matsushita commented 4 years ago

phpunitテストの設定も変更しておく。

  1. Laravelプロジェクトルートで以下を実行。 cp .env .env.testing

  2. .env.testingを編集

    APP_ENV=testing
    DB_DATABASE=laravel_practice_test
  3. phpunit.xmlの編集 以下の2行を削除

    <server name="DB_CONNECTION" value="sqlite"/>
    <server name="DB_DATABASE" value=":memory:"/>
takanori-matsushita commented 4 years ago

これに伴い、UserTestを一部編集する。 tests/Feature/UserTest.php

: 
use Illuminate\Foundation\Testing\DatabaseMigrations;

class UserTest extends TestCase
{
  use DatabaseMigrations;

  private $attributes;

  public function setUp(): void  //各テストで初めに実行する処理を記述する。
  {
    parent::setUp();

    $this->attributes = [
      'name'     => 'Laravel User',
      'email'     => 'laravel@example.com',
      'password' => bcrypt('password'),
    ];
  }
  : 
 }

use DatabaseMigrations; は各テストごとにデータベースのマイグレーションを実行する。

takanori-matsushita commented 4 years ago

そして、それぞれのテスト・データベースプロバイダをを修正する。

  public function testShouldBeValid($name, $email, $expect)
  {
    User::create($this->attributes); //追加
    : 
  }

  private function stringUpper($emailUpper)
  {
    User::create($this->attributes); //追加
    : 
  }

  private function stringLower($emailUpper)
  {
    User::create($this->attributes); //追加
    : 
  }

  public function userDataProvider()
  {
      :
      'email unique error' => ["Laravel User", "laravel@example.com", false],  //変更
  }

  public function emailDataProvider()
  {
      : 
      'email upper lower error' => ["laravel@example.com", true],  //変更
  }
takanori-matsushita commented 4 years ago

vendor/bin/phpunit これでテストがgreenになる。

takanori-matsushita commented 4 years ago

演習 上記で実装したため省略