20201128のlaravelに関する記事は6件です。

ReactからLaravelのAPIサーバーを叩く + CORS概説

初めに

以前、LaravelをAPIサーバーとして利用する記事を書いたのですが、その続きとして、ReactとLaravelを使って、CORSとは何かを解説しながら、実際にLaravel側に設定を書くところまでをやってみたいと思います。

※ Laravelでは、CORSの設定は、fruitcake/laravel-corsを使うことが一般的かなと思いますが、本記事では、Middlewareの実装も行っています。

CORSって何?

Cross-Origin Resource Sharingの略で、コルスと呼びます。日本語訳するとオリジン間リソース共有と呼びます。この設定をしていないと、フロントからAPIを叩いて、APIサーバーの値を取得したり、保存したりということが出来ません。

Cross-Origin Resource Sharing (CORS) は、追加の HTTP ヘッダーを使用して、あるオリジンで動作しているウェブアプリケーションに、異なるオリジンにある選択されたリソースへのアクセス権を与えるようブラウザーに指示するための仕組みです。

オリジン間リソース共有 (CORS)

Cross-OriginのOriginってそもそも何?

Originとは、URL中の スキーム ホスト ポートの組み合わせのことです。

例:https://google.com:443

Origin(オリジン)

ブラウザはセキュリティ上の理由で、スクリプトによって開始されるHTTPリクエストを制限しています。これは「同一オリジンポリシー」に従う事とイコールで、APIを叩く際に、CORSの設定をしていない場合、同じオリジンに対してのみしかリクエストを行うことができません。これは以下のような脆弱性を防ぐことを目的としています。

CSRF(Cross-Site RequestForgeries)

直訳のまま、サイト横断的にリクエストを偽装する攻撃で、Web アプリケーションのユーザーが、意図しない処理をWebサーバー上で実行される脆弱性です。

本来はログインしたユーザーしか実行できない記事の書き込みを勝手にされたり、登録情報を強制的に変更されたりしてしまいます。

XSS(Cross Site Scripting)

ユーザーが Web サイトにアクセスすることで不正なスクリプトがWebブラウザ(Client)で実行されてしまう脆弱性です。

利用者のCookieが盗まれ、Cookie内にある利用者のセッション情報がそのまま使用されてしまう「なりすまし」の危険性があります。

繰り返しになりますが、上記の脆弱性を回避するために、APIを叩く際には、同一オリジンポリシーに従って、通常は異なるオリジンに対してリクエストを行うことができません。

CORSって何のためにあるの?

WebクライアントとAPIサーバーで分けて運用する場合など、異なるオリジンの間で通信を行う場合には、CORSの設定が必要です。

CORSは、ブラウザから情報を読み取ることを許可するオリジンを設定することを可能にします。

今回のケースでいうと、Reactアプリケーションから、Laravelで作ったAPIサーバーにリクエストを投げる際に、CORSの設定を行っていないと、エラーが返ってきてしまいます。

CORSの二つのリクエスト

CORSには、リクエストの種類として、単純リクエスト(Simple Request)プリフライトリクエスト(Preflight Request)に分類されています。

単純リクエスト(Simple Request)

以下のメソッドが単純リクエストで許可されています。

  • GET
  • POST
  • HEAD

詳細は以下を参照してみてください。
単純リクエスト

プリフライトリクエスト(Preflight Request)

HTTPメソッドには、サーバー情報に副作用を引き起こすメソッド(MIMEタイプを伴うPOSTやDELETE等)がありますよね。この場合、サーバーから対応するメソッドの一覧を取得し、サーバーの「認可」に基づいてリクエストを送信する必要があります。

方法としては、リクエストの始めにOPTIONSメソッドによるHTTPリクエストを他のドメインにあるリソースに向けて送り、実際のリクエストを送信しても安全かどうか確かめます。(=アクセスを許可するメソッドをレスポンスヘッダーに含める必要があります

以下のメソッドが、プリフライトリクエストでは許可されています。

  • PUT
  • PATCH
  • DELETE
  • CONNECT
  • OPTIONS
  • TRACE

詳細は以下を参照してみてください。
プリフライトリクエスト

CORSの設定がない状態でReactからAPIサーバーを叩いてみる

以下コンポーネントは、マウントされた時にAPIサーバーから値を取得する処理と、入力した値をPOSTして保存する処理が書かれたコンポーネントになります。

実際に手元で試したい場合は、以下記事を参考にAPIサーバーも立てて試してみてください。
LaravelでサクッとAPIサーバーを立てる

検証用コンポーネント
export default function Component(props) {

  const headers = {
    "Content-type": "application/json",
  }

  const [articles, setArticles] = useState([]);
  const [newArticle, setNewArticle] = useState({title: "", body: ""});

  useEffect(()=> {
    const get = async () => {
      const res = await axios("http://127.0.0.1:8000/api/articles");
      setArticles(res.data)
    }
    get();
  },[])

  const handleOnCreateNewArticle = async () => {
    const data = {
      title: newArticle.title,
      body: newArticle.body
    }

    await axios.post("http://127.0.0.1:8000/api/articles", data, {
      headers
    })

    const res = await axios("http://127.0.0.1:8000/api/articles");
    setArticles(res.data)
  }

  return (
    <>
      <h1>
        {props.title}
      </h1>
      <h2>ArticleList</h2>
      {articles.map((article) => 
        <div key={article.id}>
        <div>TITLE:{article.title}</div>
        <div>BODY:{article.body}</div>
        <hr />
        </div>
        )}

      <input type="text" name="title" onChange={(e)=>{setNewArticle({...newArticle, title: e.target.value})}} value={newArticle.title} placeholder="New Title?"/>
      <input type="text" name="body" onChange={(e)=>{setNewArticle({...newArticle, body: e.target.value})}} value={newArticle.body} placeholder="New Body?"/>
      <button onClick={ handleOnCreateNewArticle }>Create</button>

    </>
  )
}

実際にReactと、LaravelのAPIのローカルサーバーを起動して、検証してみます。

React:http://localhost:3000
↓リクエスト
Laravel(APIサーバー):http://127.0.0.1:8000/api/articles

すると、以下メッセージがDevToolで確認できるはずです。
「Access-Control-Allow-Originヘッダーが存在しないので、CORSポリシーによってブロックされていますよ」といったメッセージが表示されていますね。

Access to XMLHttpRequest at 'http://127.0.0.1:8000/api/articles' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

続いて、新規Articleを作成するためにPOSTメソッドでデータを送信してみます。すると、以下エラーが表示されます。プリフライトリクエストのチェックにパスしてませんと表示されています。

Access to XMLHttpRequest at 'http://127.0.0.1:8000/api/articles' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

以上で、CORSの設定をしていないとAPIを叩いた際にエラーとなることがわかりました。それでは、Access-Control-Allow-Originレスポンスヘッダーを追記して、CORSの設定をしていきます。

LaravelでCORS設定を行う

Middlewareを作成する

設定は簡単です。まずミドルウェアを作成しましょう。

php artisan make:middleware Cors
app/Http/Middleware/Cors.php
class Cors
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        return $next($request)
        ->header('Access-Control-Allow-Origin', 'http://localhost:3000')
        ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
        ->header('Access-Control-Allow-Headers', 'Content-Type');
    }
}

ここで、レスポンスのHTTPヘッダーに3つのフィールドを追加しています。簡単に各フィールドの役割について見ていきます。

Access-Control-Allow-Origin

MDN:Access-Control-Allow-Origin

Access-Control-Allow-Origin レスポンスヘッダーは、指定されたオリジンからのリクエストを行うコードでレスポンスが共有できるかどうかを示します。

MDNの記述の通りですが、APIサーバー(ここではLaravel)へのアクセスを許可するオリジン(ここではReact)を指定しています。
上記の例だと、http://localhost:3000は許可していますが、それ以外は、同一オリジンポリシーに従って、アクセス不可です、という設定になっています。

Access-Control-Allow-Methods

許可するHTTPメソッドを指定します。
Preflightリクエストを送信する場合には、ここに許可するメソッドを追記します。上記の例では、GET POST PUT DELETE OPTIONSを追記しています。

Access-Control-Allow-Headers

許可するHTTPヘッダーを指定します。
上記の例では、Content-Typeヘッダーが指定される値が設定されます。

作成したミドルウェアをカーネルに追加

続いて、作成したミドルウェアをカーネルに追加しましょう。

app/Http/Kernel.php
    protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
        'cors' => \App\Http\Middleware\Cors::class,
    ];

ルーティング

最後にルーティングを以下のように書き換えます。

routes/api.php
Route::group(['middleware' => ['api', 'cors']], function(){
    Route::options('articles', function() {
        return response()->json();
    });
    Route::resource('articles', 'Api\ArticlesController');
});

CORS設定後に実際にAPIを叩いてみる

以上で、CORSの設定は完了です。
先ほど、設定した3つのフィールドもヘッダーに含まれていることが確認できます。
スクリーンショット 2020-11-28 19.08.53.png

実際にArticlesのデータが返ってきていることも確認できました。
スクリーンショット 2020-11-28 19.08.01.png

以上で、CORSの設定からAPIを叩くところまで出来るようになると思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CORSを理解しながら、ReactからLaravelのAPIサーバーを叩く

初めに

以前、LaravelをAPIサーバーとして利用する記事を書いたのですが、その続きとして、ReactとLaravelを使って、CORSとは何かを解説しながら、実際にLaravel側に設定を書くところまでをやってみたいと思います。

※ Laravelでは、CORSの設定は、fruitcake/laravel-corsを使うことが一般的かなと思いますが、本記事では、Middlewareの実装も行っています。

CORSって何?

Cross-Origin Resource Sharingの略で、コルスと呼びます。日本語訳するとオリジン間リソース共有と呼びます。この設定をしていないと、フロントからAPIを叩いて、APIサーバーの値を取得したり、保存したりということが出来ません。

Cross-Origin Resource Sharing (CORS) は、追加の HTTP ヘッダーを使用して、あるオリジンで動作しているウェブアプリケーションに、異なるオリジンにある選択されたリソースへのアクセス権を与えるようブラウザーに指示するための仕組みです。

オリジン間リソース共有 (CORS)

Cross-OriginのOriginってそもそも何?

Originとは、URL中の スキーム ホスト ポートの組み合わせのことです。

例:https://google.com:443

Origin(オリジン)

ブラウザはセキュリティ上の理由で、スクリプトによって開始されるHTTPリクエストを制限しています。これは「同一オリジンポリシー」に従う事とイコールで、APIを叩く際に、CORSの設定をしていない場合、同じオリジンに対してのみしかリクエストを行うことができません。これは以下のような脆弱性を防ぐことを目的としています。

CSRF(Cross-Site RequestForgeries)

直訳のまま、サイト横断的にリクエストを偽装する攻撃で、Web アプリケーションのユーザーが、意図しない処理をWebサーバー上で実行される脆弱性です。

本来はログインしたユーザーしか実行できない記事の書き込みを勝手にされたり、登録情報を強制的に変更されたりしてしまいます。

XSS(Cross Site Scripting)

ユーザーが Web サイトにアクセスすることで不正なスクリプトがWebブラウザ(Client)で実行されてしまう脆弱性です。

利用者のCookieが盗まれ、Cookie内にある利用者のセッション情報がそのまま使用されてしまう「なりすまし」の危険性があります。

繰り返しになりますが、上記の脆弱性を回避するために、APIを叩く際には、同一オリジンポリシーに従って、通常は異なるオリジンに対してリクエストを行うことができません。

CORSって何のためにあるの?

WebクライアントとAPIサーバーで分けて運用する場合など、異なるオリジンの間で通信を行う場合には、CORSの設定が必要です。

CORSは、ブラウザから情報を読み取ることを許可するオリジンを設定することを可能にします。

今回のケースでいうと、Reactアプリケーションから、Laravelで作ったAPIサーバーにリクエストを投げる際に、CORSの設定を行っていないと、エラーが返ってきてしまいます。

CORSの二つのリクエスト

CORSには、リクエストの種類として、単純リクエスト(Simple Request)プリフライトリクエスト(Preflight Request)に分類されています。

単純リクエスト(Simple Request)

以下のメソッドが単純リクエストで許可されています。

  • GET
  • POST
  • HEAD

詳細は以下を参照してみてください。
単純リクエスト

プリフライトリクエスト(Preflight Request)

HTTPメソッドには、サーバー情報に副作用を引き起こすメソッド(MIMEタイプを伴うPOSTやDELETE等)がありますよね。この場合、サーバーから対応するメソッドの一覧を取得し、サーバーの「認可」に基づいてリクエストを送信する必要があります。

方法としては、リクエストの始めにOPTIONSメソッドによるHTTPリクエストを他のドメインにあるリソースに向けて送り、実際のリクエストを送信しても安全かどうか確かめます。(=アクセスを許可するメソッドをレスポンスヘッダーに含める必要があります

以下のメソッドが、プリフライトリクエストでは許可されています。

  • PUT
  • PATCH
  • DELETE
  • CONNECT
  • OPTIONS
  • TRACE

詳細は以下を参照してみてください。
プリフライトリクエスト

CORSの設定がない状態でReactからAPIサーバーを叩いてみる

以下コンポーネントは、マウントされた時に、APIサーバーから値を取得する処理と入力した値をPOSTして保存する処理が書かれたコンポーネントになります。

実際に手元で試したい場合は、以下記事を参考にAPIサーバーも立てて試してみてください。
LaravelでサクッとAPIサーバーを立てる

検証用コンポーネント
export default function Component(props) {

  const headers = {
    "Content-type": "application/json",
  }

  const [articles, setArticles] = useState([]);
  const [newArticle, setNewArticle] = useState({title: "", body: ""});

  useEffect(()=> {
    const get = async () => {
      const res = await axios("http://127.0.0.1:8000/api/articles");
      setArticles(res.data)
    }
    get();
  },[])

  const handleOnCreateNewArticle = async () => {
    const data = {
      title: newArticle.title,
      body: newArticle.body
    }

    await axios.post("http://127.0.0.1:8000/api/articles", data, {
      headers
    })

    const res = await axios("http://127.0.0.1:8000/api/articles");
    setArticles(res.data)
  }

  return (
    <>
      <h1>
        {props.title}
      </h1>
      <h2>ArticleList</h2>
      {articles.map((article) => 
        <div key={article.id}>
        <div>TITLE:{article.title}</div>
        <div>BODY:{article.body}</div>
        <hr />
        </div>
        )}

      <input type="text" name="title" onChange={(e)=>{setNewArticle({...newArticle, title: e.target.value})}} value={newArticle.title} placeholder="New Title?"/>
      <input type="text" name="body" onChange={(e)=>{setNewArticle({...newArticle, body: e.target.value})}} value={newArticle.body} placeholder="New Body?"/>
      <button onClick={ handleOnCreateNewArticle }>Create</button>

    </>
  )
}

実際にReactと、LaravelのAPIのローカルサーバーを起動して、検証してみます。

React:http://localhost:3000
↓リクエスト
Laravel(APIサーバー):http://127.0.0.1:8000/api/articles

すると、以下メッセージがDevToolで確認できるはずです。
「Access-Control-Allow-Originヘッダーが存在しないので、CORSポリシーによってブロックされていますよ」といったメッセージが表示されていますね。

Access to XMLHttpRequest at 'http://127.0.0.1:8000/api/articles' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

続いて、新規Articleを作成するためにPOSTメソッドでデータを送信してみます。すると、以下エラーが表示されます。プリフライトリクエストのチェックにパスしてませんと表示されています。

Access to XMLHttpRequest at 'http://127.0.0.1:8000/api/articles' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.

以上で、CORSの設定をしていないとAPIを叩いた際にエラーとなることがわかりました。それでは、Access-Control-Allow-Originレスポンスヘッダーを追記して、CORSの設定をしていきます。

LaravelでCORS設定を行う

Middlewareを作成する

設定は簡単です。まずミドルウェアを作成しましょう。

php artisan make:middleware Cors
app/Http/Middleware/Cors.php
class Cors
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        return $next($request)
        ->header('Access-Control-Allow-Origin', 'http://localhost:3000')
        ->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
        ->header('Access-Control-Allow-Headers', 'Content-Type');
    }
}

ここで、レスポンスのHTTPヘッダーに3つのフィールドを追加しています。簡単に各フィールドの役割について見ていきます。

Access-Control-Allow-Origin

MDN:Access-Control-Allow-Origin

Access-Control-Allow-Origin レスポンスヘッダーは、指定されたオリジンからのリクエストを行うコードでレスポンスが共有できるかどうかを示します。

MDNの記述の通りですが、APIサーバー(ここではLaravel)へのアクセスを許可するオリジン(ここではReact)を指定しています。
上記の例だと、http://localhost:3000は許可していますが、それ以外は、同一オリジンポリシーに従って、アクセス不可です、という設定になっています。

Access-Control-Allow-Methods

許可するHTTPメソッドを指定します。
Preflightリクエストを送信する場合には、ここに許可するメソッドを追記します。上記の例では、GET POST PUT DELETE OPTIONSを追記しています。

Access-Control-Allow-Headers

許可するHTTPヘッダーを指定します。
上記の例では、Content-Typeヘッダーが指定される値が設定されます。

作成したミドルウェアをカーネルに追加

続いて、作成したミドルウェアをカーネルに追加しましょう。

app/Http/Kernel.php
    protected $routeMiddleware = [
        'auth' => \App\Http\Middleware\Authenticate::class,
        'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
        'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
        'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
        'can' => \Illuminate\Auth\Middleware\Authorize::class,
        'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
        'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
        'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
        'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
        'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
        'cors' => \App\Http\Middleware\Cors::class,
    ];

ルーティング

最後にルーティングを以下のように書き換えます。

routes/api.php
Route::group(['middleware' => ['api', 'cors']], function(){
    Route::options('articles', function() {
        return response()->json();
    });
    Route::resource('articles', 'Api\ArticlesController');
});

CORS設定後に実際にAPIを叩いてみる

以上で、CORSの設定は完了です。
先ほど、設定した3つのフィールドもヘッダーに含まれていることが確認できます。
スクリーンショット 2020-11-28 19.08.53.png

実際にArticlesのデータが返ってきていることも確認できました。
スクリーンショット 2020-11-28 19.08.01.png

以上で、CORSの設定からAPIを叩くところまで出来るようになると思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

SIerエンジニアからWeb系フロントエンドエンジニアに転身するために今やっていること

こんにちは!SIerでJavaプログラマをしているゆうきデザイン(@yuki_design_gr)と言います。

Qiita初投稿として、自己紹介も兼ねて
"SIerエンジニアからWeb系フロントエンドエンジニアに転身するために今やっていること"
というテーマで書いてみようと思います。

同じような境遇にいる人の道しるべの1つになりますように!

目次

1. 自己紹介
2. なぜWeb系を目指すのか
3. SIerエンジニアからWeb系フロントエンドエンジニアに転身するために今やっていること

1. 自己紹介

東京在住の20代半ば。

学歴

東京外大韓国語専攻卒業

職歴

新卒で大手SIerに入社し、アカウント営業を担当(10ヶ月)
→SE(現職。Java・.NET・Oracleのコーディング実務1年半)
→Web系企業への転職準備中

モットー

技術とデザインのことをポジティブに共有すること

目標

・世の中をポジティブにするWebサービスを作ること
・ビジネスを始めたい・サービスを作りたい友だちをIT・デザイン面でサポートすること

趣味など

韓国語と英語は日常会話レベル
持久系のスポーツが好き(陸上・水泳。社会人になってからもたまにやってる)
実写の動画編集
映画・音楽・コーヒー
ミスチルが生まれた時から好きで、人生ピンチの時に助けてくれる存在(←いま)

さあ、本題に入ります!

2. なぜWeb系を目指すのか

①新しいものを追いかけるのが好きだから

音楽や映画などのエンタメや好きで、
SI業界よりもトレンドの移り変わりが激しいWeb業界が楽しそうに見えるため。

②Webサービスを作りたいという目標があるから

自己紹介でも書いたように
世の中をポジティブにするWebサービスを作る
という目標があり、
SI業界に身を置くよりも目標実現への近道だと思っています。

③システムの裏側の処理よりも見た目に魅かれるから

Javaエンジニアをしていてプログラミングは基本的に全般楽しいですが、
JSPやCSS等のシステムの見た目の開発が楽しく、
また他のメンバーが気にかけないレイアウトのズレなどに何度も気づくことができました。

そのため、まずはWebデザイナーやUI・UXデザイナーに興味を持ち、
デザイナーのための勉強会などに参加してきました。

しかし、自分のプログラマとしての経験を活かす×システムの見た目に寄与できる
というフロントエンドの技術が一番しっくりくるなあと今は思っています。

3. SIerエンジニアからWeb系フロントエンドエンジニアに転身するために今やっていること

①フロントエンド技術に触れること

結局はどの言語がベスト!とかはなさそうなので
今は色々触ってみてます。

フロント: react, vue, rails
バックエンドやインフラ等: node, ruby, docker, laravel
その他: TypeScript, Sass, Bootstrap

色々触れる今の時期が一番楽しいですね。
何かを極めた方が勉強になる気もしますが。

個人的にはnode + react(またはvue・angular) + TypeScriptがアツい気がします!
全部jsで書けるなんて!

②フロントエンド技術を用いたWebアプリを作ってみること

上記のそれぞれの言語を使って
簡単なSNSやTodoリストをチュートリアルを見ながら作成中です。
ネット上に公開するところまでやりたいです。

③SNSやGitHub、Qiita等でのアウトプット

個人的に苦手であまりできていないアウトプット。
でも見てる人との交流が生まれたり、自分の特性や技術力を客観視できる機会と思って、
定期的に取り組んでいきたいです。


この投稿の内容は以上です。

ここまで読んでいただきありがとうございます。
これからも有益な情報をポジティブに発信していきたいです。

ぜひ、Twitter(@yuki_design_gr)のフォローもよろしくお願いします。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel6 検索フォーム (GET) パラメータを取得できない時の対処法

この記事は、Laravel6において検索時にGETメソッドのパラメータを取得できない時の対処法です。

環境

macOS
Laravel 6.20.3
Doeker 19.03.13

Eventテーブルにあるtitleカラム(string型)を検索します。一致する場合それを返し、一致しない場合はEventの全てのカラムを返します。

解決する前

検索機能に関係あるところだけ書いています。

event/index.blade.php
<div class="container">
  <div class="row">
  <form action="{{url('/event')}}" method="GET">
    <input type="text" name="search" value="{{ $search }}" placeholder="イベント名を入力してください">
    <input type="submit" value="検索">
  </form>
  </div>
</div>
EventController.php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Event;

public function index(Request $request)
    {   

        $search = $request->input('search');
        if (!empty($search)){
            $events = Event::where('title',$search)->orderBy('created_at','desc')->get();
        }else{
            $events = Event::orderBy('created_at','desc')->get();
        }
        return view('event.index',compact('events'));
}
wep.php
Route::resource('event', 'EventController');

formのGETメソッドを使いパラメータをcontrollerに渡しています。
しかしパラメータは空になりelse文ですべてのEventが返ってしまい検索ができませんでした。
(ちなみにcontrollerの一行目にdd($request)を追記することでパラメータは確認できます。)

解決方法

index.blade.phpを以下のように修正します。

index.blade.php
 <form action="{{ route('event.index') }}" method="post">  
      {{ csrf_field()}}  
      {{method_field('get')}}
      <input type="search" name="search" placeholder="イベント名を入力してください">
      <input type="submit" value="検索">
 </form>

formの方をpostにして、{{method_field('get')}}でメソッドをgetにしています。
この方法で無事にパラメータが渡り、検索できるようになりました。

Laravelのバージョンによっては解決前の書き方でできるみたいなので、環境に合わせて色々試してみてください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[PHP]フォームのデータを受け取る

PHPでフォームのデータを受け取る

PHP
 <?php echo $_POST['name属性に指定した値']; ?>
 <div class="form-item"> 内容</div>

$_POST['name']

フォームで送信した値を受け取るには「$_POST」を使用する。
「$_POST」は連想配列になっていて[ ]の中に、とのname属性に指定した値を入れることで、それぞれの送信した値を受け取ることが出来る

以上!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Laravel] ユーザー登録

Laravelの認証関連の方法についてまとめます。今回はLaravel/uiを使った場合と使わない場合とで整理しました。

Laravel/uiを使わない場合

ルーティングを修正する

laravel/routes/web.php
//ルーティングに以下を追加。
Auth::routes();

記述することで以下のコマンドでルーティングに自動的に認証関連のパスが取得できる。

php artisan route:list

ユーザー登録に関するルーティングは以下になります。

+--------+----------+------------------------+------------------+------------------------------------------------------------------------+------------+
| Domain | Method   | URI                    | Name             | Action                                                                 | Middleware |
+--------+----------+------------------------+------------------+------------------------------------------------------------------------+------------+
// 略
|        | GET|HEAD | register               | register         | App\Http\Controllers\Auth\RegisterController@showRegistrationForm      | web,guest  |
|        | POST     | register               |                  | App\Http\Controllers\Auth\RegisterController@register                  | web,guest  |
+--------+----------+------------------------+------------------+------------------------------------------------------------------------+------------+

RegisterControllerは、Laravelがインストールされた時点から存在するコントローラーになります。

RegisterControllerクラスの確認

<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use App\User;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;


class RegisterController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Register Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles the registration of new users as well as their
    | validation and creation. By default this controller uses a trait to
    | provide this functionality without requiring any additional code.
    |
    */

    use RegistersUsers;
    //-- 略
}

トレイトの確認。トレイトを定義することでトレイト内で定義している機能が使えるようになります。
他の複数のクラスで共通して使う、といった使い方をします。

vendor/laravel/framework/src/Illuminate/Foundation/Auth/RegistersUsers.php
<?php

namespace Illuminate\Foundation\Auth;

use Illuminate\Auth\Events\Registered;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

trait RegistersUsers
{
    use RedirectsUsers;

    /**
     * Show the application registration form.
     *
     * @return \Illuminate\Http\Response
     */
    public function showRegistrationForm()
    {
        return view('auth.register');
    }

    /**
     * Handle a registration request for the application.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function register(Request $request)
    {
        $this->validator($request->all())->validate();

        event(new Registered($user = $this->create($request->all())));

        $this->guard()->login($user);

        return $this->registered($request, $user)
                        ?: redirect($this->redirectPath());
    }
    //-- 略
}

showRegistrationFormアクションメソッドの確認

以下の記述よりresources/views/authディレクトリにある、registerという名前のビューを表示する処理となっています。

vendor/laravel/framework/src/Illuminate/Foundation/Auth/RegistersUsers.php
<?php
// 略
    public function showRegistrationForm()
    {
        return view('auth.register');
    }
// 略

authディレクトリにregisterファイルを作って新規登録画面を作成します。

registerアクションメソッドの確認とredirectToプロパティの変更

vendor/laravel/framework/src/Illuminate/Foundation/Auth/RegistersUsers.php
<?php
// 略
    public function register(Request $request)
    {
//ここで登場する$thisは、トレイトをuseしているクラス、つまりRegisterControllerクラス(のインスタンス)のことを指します。以下のコードでバリデーションを行う。
        $this->validator($request->all())->validate();

//ユーザー情報をデータベースのusersテーブルに登録し、それをイベントとして発生させている。
        event(new Registered($user = $this->create($request->all())));

//先ほど登録したユーザー情報で、ログイン済みの状態にしている。
        $this->guard()->login($user);

//registeredメソッド内で何か処理が定義してあれば、その結果を返す。registeredメソッド内で何も処理が定義していなければ、redirectPathメソッドで定義されたパス(URL)へリダイレクトするといったことを行なっています。 
        return $this->registered($request, $user)
                        ?: redirect($this->redirectPath());
    }

registeredメソッドの確認

vendor/laravel/framework/src/Illuminate/Foundation/Auth/RegistersUsers.php
<?php

namespace Illuminate\Foundation\Auth;

use Illuminate\Auth\Events\Registered;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

trait RegistersUsers
{
    use RedirectsUsers;
    // 略
}
vendor/laravel/framework/src/Illuminate/Foundation/Auth/RedirectsUsers.php
<?php

namespace Illuminate\Foundation\Auth;

trait RedirectsUsers
{
    /**
     * Get the post register / login redirect path.
     *
     * @return string
     */
    public function redirectPath()
    {
//method_exists関数は、第一引数にクラス、第二引数にメソッド名を受け取り、第一引数のクラスに第二引数のメソッドが存在するかどうかをtrueかfalseで返します。


        if (method_exists($this, 'redirectTo')) {
            return $this->redirectTo();
        }
//もし、条件に合致しなければ/homeに移動する。
        return property_exists($this, 'redirectTo') ? $this->redirectTo : '/home';
    }
}

RegisterControllerクラスにredirectToというプロパティがあるか確認

app/Http/Controllers/Auth/RegisterController.php
<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use App\User;
use Illuminate\Foundation\Auth\RegistersUsers;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Validator;

class RegisterController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Register Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles the registration of new users as well as their
    | validation and creation. By default this controller uses a trait to
    | provide this functionality without requiring any additional code.
    |
    */

    use RegistersUsers;

    /**
     * Where to redirect users after registration.
     *
     * @var string
     */

//以下に記述あり
    protected $redirectTo = RouteServiceProvider::HOME;
    // 略
}

RouteServiceProviderの確認

<?php

namespace App\Providers;

use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Support\Facades\Route;

class RouteServiceProvider extends ServiceProvider
{
    // 略
    /**
     * The path to the "home" route for your application.
     *
     * @var string
     */

//以下を'/home'から'/'に変更
    public const HOME = '/';
    // 略
}

バリデーションのチェック

app/Http/Controllers/Auth/RegisterController.php
<?php
// 略

class RegisterController extends Controller
{
    // 略
    protected function validator(array $data)
    {
//以下の記述でname,email,passwordに制限をかけている。変更したい場合は、以下を修正。
        return Validator::make($data, [
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'string', 'email', 'max:255', 'unique:users'],
            'password' => ['required', 'string', 'min:8', 'confirmed'],
        ]);
    }
    // 略
}

新規登録用のビュー作成

resources/views/auth/register.blade.php
@extends('app')

@section('title', 'ユーザー登録')

@section('content')
  <div class="container">
    <div class="row">
      <div class="mx-auto col col-12 col-sm-11 col-md-9 col-lg-7 col-xl-6">
        <h1 class="text-center"><a class="text-dark" href="/">memo</a></h1>
        <div class="card mt-3">
          <div class="card-body text-center">
            <h2 class="h3 card-title text-center mt-2">ユーザー登録</h2>

            <div class="card-text">
              {{--ここから--}}
              <form method="POST" action="{{ route('register') }}">

//@csrfは脆弱性を守るため
                @csrf
                <div class="md-form">
                  <label for="name">ユーザー名</label>
//old関数は入力した情報にエラーがあれば消えてしまうのを防ぎ、情報を保持したままエラーを返してくれる。
                  <input class="form-control" type="text" id="name" name="name" required value="{{ old('name') }}">
                  <small>英数字3〜16文字(登録後の変更はできません)</small>
                </div>
                <div class="md-form">
                  <label for="email">メールアドレス</label>
                  <input class="form-control" type="text" id="email" name="email" required value="{{ old('email') }}" >
                </div>
                <div class="md-form">
                  <label for="password">パスワード</label>
                  <input class="form-control" type="password" id="password" name="password" required>
                </div>
                <div class="md-form">
                  <label for="password_confirmation">パスワード(確認)</label>
                  <input class="form-control" type="password" id="password_confirmation" name="password_confirmation" required>
                </div>
                <button class="btn btn-block blue-gradient mt-2 mb-2" type="submit">ユーザー登録</button>
              </form>
              {{--ここまで--}}

              <div class="mt-0">
                <a href="{{ route('login') }}" class="card-text">ログインはこちら</a>
              </div>

            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
@endsection

ユーザー登録画面「表示」のルートなので、リクエストする先のルートとしては不適切なのではないか、と思いがちだが、HTTPのメソッドがGETとPOSTのどちらであるかで決まります。formタグのmethod属性ではPOSTと定義してあるので、リクエストはPOSTメソッドとなり、Laravelではユーザー登録処理を行ってくれます。

ログアウトを可能にする

app/Http/Controllers/Auth/LoginController.php
<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use App\Providers\RouteServiceProvider;
use Illuminate\Foundation\Auth\AuthenticatesUsers;

class LoginController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Login Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles authenticating users for the application and
    | redirecting them to your home screen. The controller uses a trait
    | to conveniently provide its functionality to your applications.
    |
    */

    use AuthenticatesUsers;
    // 略
}

AuthenticatesUsersトレイトの確認

vendor/laravel/framework/src/Illuminate/Foundation/Auth/AuthenticatesUsers.php
<?php

namespace Illuminate\Foundation\Auth;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\ValidationException;

trait AuthenticatesUsers
{
    // 略
    /**
     * Log the user out of the application.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function logout(Request $request)
    {
        //ユーザーをログアウトさせる処理
        $this->guard()->logout();
        //セッションを再作成しています
        $request->session()->invalidate();
        //CSRFトークンを再作成しています。
        $request->session()->regenerateToken();
        //loggedoutメソッドから何か戻り値があれば、それをlogoutアクションメソッドの呼び出し元に返しています。

loggedoutアクションメソッドから何も戻り値が無ければredirect関数により'/'へリダイレクトされます
        return $this->loggedOut($request) ?: redirect('/');
    }

    /**
     * The user has logged out of the application.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return mixed
     */

//もしリダイレクト先を/でなく別のところにしたい場合は以下に処理を記述する。
    protected function loggedOut(Request $request)
    {
        //
    }
// 略
}

@guest, @auth

@guestから@endguestに囲まれた部分は、ユーザーがまだログインしていない状態の時のみ処理されます。

逆に@authから@endauthに囲まれた部分は、ユーザーがログイン済みの状態の時のみ処理されます。

エラーメッセージの表示

resources/views/auth/register.blade.php
<h2 class="h3 card-title text-center mt-2">ユーザー登録</h2>
//以下のコードではエラーメッセージが1件以上ある場合の記述を追加
@foreach($errors->all() as $error)
  <li>{{ $error }}</li>
@endforeach

$errors変数は、Illuminate\Support\MessageBagクラスのインスタンスであり、バリデーションエラーメッセージを配列で持っている。

//以下の記述でエラーメッセージの有無を返す。
@if ($errors->any())

エラーメッセージの日本語化

config/app.php
//以下を'en'から'ja'に変更
'locale' => 'ja', 

//fallback_localeは、localeで指定した言語のファイルが見つからなかった場合にどの言語ファイルを使用するかを定義
'fallback_locale' => 'en',

項目名の日本語化

resources/lang/ja/validation.php
<?php

return [

    // 略

    /*
    |--------------------------------------------------------------------------
    | カスタムバリデーション属性名
    |--------------------------------------------------------------------------
    |
    | 以下の言語行は、例えば"email"の代わりに「メールアドレス」のように、
    | 読み手にフレンドリーな表現でプレースホルダーを置き換えるために指定する
    | 言語行です。これはメッセージをよりきれいに表示するために役に立ちます。
    |
    */

    //==========ここから削除==========
    'attributes' => [],
    //==========ここまで削除==========

    //==========ここから追加==========
    'attributes' => [
        'name' => 'ユーザー名',
        'email' => 'メールアドレス',
        'password' => 'パスワード',
    ],
    //==========ここまで追加==========

];

上記のようにattributesの値に、連想配列で項目名とその別名を定義することができます。

この別名を日本語にすることで、バリデーションエラーメッセージでの項目名を日本語化できます。

なお、attributesでの英語の項目名と日本語の項目名の対応は、ユーザー登録画面に限らず全画面のバリデーションで適用されることに注意してください。

例えば、ユーザー登録画面以外に、チーム登録画面があったとします。

そのチーム登録画面でチーム名をnameと取り扱っていた場合に、チーム名に関してバリデーションエラーがあると、エラーメッセージはユーザー名は・・・と表示されます。

SQLやTinkerを使ったデータの操作

psql -d データベース名 -U ユーザ名 -h ホスト名

-Uオプションで、ユーザー名を指定します。

-hオプションで、接続先のホスト名を指定します。

-dオプションで、接続先のデータベース名を指定します。

パスワードを聞かれたら入力します。パスワードはenvファイルに設定してあります。

SELECT文やUPDATE文などでデータを操作します。

tinkerを使用する場合

//以下のコマンドで起動できます。
$ php artisan tinker

ログイン登録について

//以下のコマンドでログインのルートを調べます。
$ php artisan route:list
+--------+----------+------------------------+------------------+------------------------------------------------------------------------+------------+
| Domain | Method   | URI                    | Name             | Action                                                                 | Middleware |
+--------+----------+------------------------+------------------+------------------------------------------------------------------------+------------+
// 略
|        | 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        |
// 略
+--------+----------+------------------------+------------------+------------------------------------------------------------------------+------------+

・showLoginFormアクションメソッドでログイン画面表示
・loginアクションメソッドでログイン処理

LoginControllerの確認

app/Http/Controllers/Auth/LoginController.php
<?php

namespace App\Http\Controllers\Auth;

use App\Http\Controllers\Controller;
use Illuminate\Foundation\Auth\AuthenticatesUsers;
use Illuminate\Http\Request;

class LoginController extends Controller
{
    /*
    |--------------------------------------------------------------------------
    | Login Controller
    |--------------------------------------------------------------------------
    |
    | This controller handles authenticating users for the application and
    | redirecting them to your home screen. The controller uses a trait
    | to conveniently provide its functionality to your applications.
    |
    */

    use AuthenticatesUsers;
    // 略
}

AuthenticatesUsersトレイトの確認

vendor/laravel/framework/src/Illuminate/Foundation/Auth/AuthenticatesUsers.php
<?php

namespace Illuminate\Foundation\Auth;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\ValidationException;

trait AuthenticatesUsers
{
    use RedirectsUsers, ThrottlesLogins;

    /**
     * Show the application's login form.
     *
     * @return \Illuminate\Http\Response
     */
    public function showLoginForm()
    {
        return view('auth.login');
    }

    /**
     * Handle a login request to the application.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|\Illuminate\Http\JsonResponse
     *
     * @throws \Illuminate\Validation\ValidationException
     */
    public function login(Request $request)
    {
        $this->validateLogin($request);

        // If the class is using the ThrottlesLogins trait, we can automatically throttle
        // the login attempts for this application. We'll key this by the username and
        // the IP address of the client making these requests into this application.
        if (method_exists($this, 'hasTooManyLoginAttempts') &&
            $this->hasTooManyLoginAttempts($request)) {
            $this->fireLockoutEvent($request);

            return $this->sendLockoutResponse($request);
        }

        if ($this->attemptLogin($request)) {
            return $this->sendLoginResponse($request);
        }

        // If the login attempt was unsuccessful we will increment the number of attempts
        // to login and redirect the user back to the login form. Of course, when this
        // user surpasses their maximum number of attempts they will get locked out.
        $this->incrementLoginAttempts($request);

        return $this->sendFailedLoginResponse($request);
    }
    //-- 略
}

showLoginFormアクションメソッドの確認

vendor/laravel/framework/src/Illuminate/Foundation/Auth/AuthenticatesUsers.php
<?php
// 略
trait AuthenticatesUsers
{
    // 略
    public function showLoginForm()
    {
        return view('auth.login');
    }
    // 略
}

上記よりauthディレクトリにloginファイルを用意する必要があります。

loginアクションメソッドの確認

vendor/laravel/framework/src/Illuminate/Foundation/Auth/AuthenticatesUsers.php
<?php
// 略
trait AuthenticatesUsers
{
    // 略
    public function login(Request $request)
    {
        /**
         * 1. バリデーション(形式のチェック)
        **/
        $this->validateLogin($request);

        /**
         * 2. ログイン試行回数を超過していればロックアウトをレスポンス
        **/
        // If the class is using the ThrottlesLogins trait, we can automatically throttle
        // the login attempts for this application. We'll key this by the username and
        // the IP address of the client making these requests into this application.
        if (method_exists($this, 'hasTooManyLoginAttempts') &&
            $this->hasTooManyLoginAttempts($request)) {
            $this->fireLockoutEvent($request);

            return $this->sendLockoutResponse($request);
        }

        /**
         * 3. ログイン試行して認証OKであればログイン成功をレスポンス(トップページにリダイレクト)
        **/
        if ($this->attemptLogin($request)) {
            return $this->sendLoginResponse($request);
        }

        /**
         * 4. 認証NGであればログイン試行回数を1増やしてログイン画面をレスポンス
        **/
        // If the login attempt was unsuccessful we will increment the number of attempts
        // to login and redirect the user back to the login form. Of course, when this
        // user surpasses their maximum number of attempts they will get locked out.
        $this->incrementLoginAttempts($request);

        return $this->sendFailedLoginResponse($request);
    }
    // 略
}

・validateLoginメソッドは、同じAuthenticatesUsersトレイトの中に定義されています。
・usernameメソッドも、同じAuthenticatesUsersトレイトの中に定義されています。

vendor/laravel/framework/src/Illuminate/Foundation/Auth/AuthenticatesUsers.php
<?php
    // 略
    protected function validateLogin(Request $request)
    {
        $request->validate([
            $this->username() => 'required|string',
            'password' => 'required|string',
        ]);
    }
    // 略


    // 略
    public function username()
    {
        return 'email';
    }
    // 略

method_exists関数でhasTooManyLoginAttemptsメソッドの有無を調べ、存在しない場合はif文を抜けます。
存在する場合はhasTooManyLoginAttemptsメソッドが実行され、その戻り値がfalseやnullでなければ、if文の中の処理が実行されます。

hasTooManyLoginAttemptsメソッドは、AuthenticatesUsersトレイトがさらにuseしているThrottlesLoginsトレイトに定義されています。

trait AuthenticatesUsers
{
    use RedirectsUsers, ThrottlesLogins;
    //略
}
vendor/laravel/framework/src/Illuminate/Foundation/Auth/ThrottlesLogins.php
<?php
// 略
trait ThrottlesLogins
{
    // 略
    /**
     * Get the maximum number of attempts to allow.
     *
     * @return int
     */
    public function maxAttempts()
    {
        return property_exists($this, 'maxAttempts') ? $this->maxAttempts : 5;
    }

    /**
     * Get the number of minutes to throttle for.
     *
     * @return int
     */
    public function decayMinutes()
    {
        return property_exists($this, 'decayMinutes') ? $this->decayMinutes : 1;
    }
}

maxAttemptsメソッドがログインを試せる回数の上限、decayMinutesメソッドが時間間隔(単位は分)を返しています。

それぞれメソッドでは、メソッドと同じ名前のプロパティが存在するかを調べ、存在すればそのプロパティの値を返します。

存在しない場合はデフォルト値(5回および1分)を返します。

もし、このデフォルト値をカスタマイズしたい場合は、LoginControllerに以下のようにmaxAttemptsプロパティ、decayMinutesプロパティを追加すれば良いことになります。

app/Http/Controllers/Auth/LoginController.php
<?php
// 略
class LoginController extends Controller
{
    // 略
    use AuthenticatesUsers;

    protected $maxAttempts = 5;
    protected $decayMinutes = 1;
    // 略
}

ログイン試行して認証OKであればログイン成功をレスポンス(トップページにリダイレクト)

vendor/laravel/framework/src/Illuminate/Foundation/Auth/AuthenticatesUsers.php
if ($this->attemptLogin($request)) {
    return $this->sendLoginResponse($request);
}

上記のコードでは、attemptLoginメソッドでログイン処理を行い、成功すればsendLoginResponseメソッドの結果を返し、早期returnとなるので、loginアクションメソッドの処理はここで終了します。

これら2つのメソッドのコードの解説は割愛しますが、

attemptLoginメソッドでは、パスワードが一致するかのチェック
sendLoginResponseメソッドでは、
セッションの再作成
ログイン試行回数のクリア(ログインに成功したので)
redirectToプロパティで指定したURLヘリダイレクト
を行なっています。

redirectToプロパティは、LoginControllerに存在します。

app/Http/Controllers/Auth/LoginController.php
<?php
// 略
class LoginController extends Controller
{
    // 略
    /**
     * Where to redirect users after login.
     *
     * @var string
     */
    protected $redirectTo = RouteServiceProvider::HOME;
    // 略
}

RouteServiceProviderクラスの定数HOMEは、新規登録の際に'/'に変更したのでログイン後のリダイレクト先はトップページになります。

認証NGであればログイン試行回数を1増やしてログイン画面をレスポンス

vendor/laravel/framework/src/Illuminate/Foundation/Auth/AuthenticatesUsers.php

先ほどのattemptLoginメソッドでパスワードが不一致の場合は、if文を抜けてこの処理が行われます。

incrementLoginAttemptsメソッドでは、ログイン試行回数を1つ増やします(ログインが失敗しているので)。

sendFailedLoginResponseメソッドでは、ログイン失敗のメッセージとともにログイン画面をレスポンスします。

キャッシュについて

ログイン試行回数はLaravelのキャッシュで管理されています。

Laravelのキャッシュの保存先は、デフォルトの設定ではファイルとなっており、storage/framework/cache/dataディレクトリに保存されます。

キャッシュの保存先の設定はconfigディレクトリのcache.phpにあります。

config/cache.php
<?php

use Illuminate\Support\Str;

return [

    /*
    |--------------------------------------------------------------------------
    | Default Cache Store
    |--------------------------------------------------------------------------
    |
    | This option controls the default cache connection that gets used while
    | using this caching library. This connection is used when another is
    | not explicitly specified when executing a given caching function.
    |
    | Supported: "apc", "array", "database", "file",
    |            "memcached", "redis", "dynamodb"
    |
    */

    'default' => env('CACHE_DRIVER', 'file'),
    //略
];

env関数は、第一引数に指定した環境変数の値を返し、もしその環境変数が無ければ第二引数の値をデフォルト値として返します。

laravelディレクトリの.envファイルでは環境変数CACHE_DRIVERの値はfileとなっているため、キャッシュの保存先はファイルとなります。

env.
CACHE_DRIVER=file

(もし、.env内にCACHE_DRIVERが無かったり、.envファイル自体が無かったりした場合は、cache.phpのenv関数の第二引数が'file'となっているので結局ファイルとなります)

なお、本格的なWebサービスであればキャッシュの保存先はファイルではなく、より高速に読み書きできるメモリとすることが多いです

ログイン画面の作成

resources/views/authディレクトリにlogin.blade.phpを作成します。

resources/views/auth/login.blade.php
@extends('app')

@section('title', 'ログイン')

@section('content')
  <div class="container">
    <div class="row">
      <div class="mx-auto col col-12 col-sm-11 col-md-9 col-lg-7 col-xl-6">
        <h1 class="text-center"><a class="text-dark" href="/">memo</a></h1>
        <div class="card mt-3">
          <div class="card-body text-center">
            <h2 class="h3 card-title text-center mt-2">ログイン</h2>

            @include('error_card_list')

            <div class="card-text">
              <form method="POST" action="{{ route('login') }}">
                @csrf

                <div class="md-form">
                  <label for="email">メールアドレス</label>
                  <input class="form-control" type="text" id="email" name="email" required value="{{ old('email') }}">
                </div>

                <div class="md-form">
                  <label for="password">パスワード</label>
                  <input class="form-control" type="password" id="password" name="password" required>
                </div>

                {{--ここから--}}
                <input type="hidden" name="remember" id="remember" value="on">
                {{--ここまで--}}

                <button class="btn btn-block blue-gradient mt-2 mb-2" type="submit">ログイン</button>

              </form>

              <div class="mt-0">
                <a href="{{ route('register') }}" class="card-text">ユーザー登録はこちら</a>
              </div>

            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
@endsection

<input type="hidden" name="remember" id="remember" value="on">

上記のinputタグは、世の中のWebサービスのログイン画面によく登場する、

次回から自動でログインする
という説明がされたチェックボックスに相当するもの。

type属性をcheckboxではなくhiddenとすることでユーザーが直接操作できない隠し項目とし、value属性をonとすることで常にチェックが入ったのと同じ状態にしています。

上記のinputタグがあることで、ユーザーがログインボタンを押した際にメールアドレスとパスワード以外にもrememberという名前のパラメータがonの状態でPOST送信されます。

この結果どうなるかというと、ユーザーがログインした後はログアウト操作を行わない限り、そのブラウザではログイン状態が維持されます。

ログイン持続時間について

Cookieがない場合は、ログイン後に120分間ユーザーの操作が無いとログイン状態が解除されます。

config/session.php
<?php
// 略
return [
    // 略
    /*
    |--------------------------------------------------------------------------
    | Session Lifetime
    |--------------------------------------------------------------------------
    |
    | Here you may specify the number of minutes that you wish the session
    | to be allowed to remain idle before it expires. If you want them
    | to immediately expire on the browser closing, set that option.
    |
    */

    'lifetime' => env('SESSION_LIFETIME', 120),
    // 略
];

env.
SESSION_LIFETIME=120

(もし、.env内にSESSION_LIFETIMEが無かったり、.envファイル自体が無かったりした場合は、session.phpのenv関数の第二引数が120となっているので結局120分間となります)

認証エラーメッセージの日本語化

resources/lang/jaディレクトリにauth.phpを作成します。

以下のサイトからコピペします。
https://readouble.com/laravel/6.x/ja/auth-php.html

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む