前回のTurbolinks、再びにおいて掲載した記事に、修正が必要なことに気づきました。登録フォームのPOSTでバリデーションエラーが出て同画面に戻るときは問題ないのですが、エラーがなく登録完了してホーム画面へリダイレクトされたときが問題です。

以下のように、画面はホーム画面の中身となっていますが、ブラウザのURLがhomeであるべきところ、registerのままでした。

これを修正するには、まずブレードのインラインのjsから、

...
@section('script')

    $(document).on('turbolinks:load', function() {
      $("form").on("submit", function(event) {
        event.preventDefault();

        $.ajax({
          url: '/register',
          type: 'POST',
          dataType: 'html',
          processData: false,
          contentType: false,
          cache: false,
          data: new FormData($("form")[0])
        }).done(function(response, status, $xhr) {

          var redirect = $xhr.getResponseHeader('Turbolinks-Location');

          if (redirect) { // 投稿成功でリダイレクトしたとき
            Turbolinks.visit(redirect);
          } else { // バリデーションエラーのとき
            var referrer = window.location.href;
            Turbolinks.controller.cache.put(referrer, Turbolinks.Snapshot.wrap(response));
            Turbolinks.visit(referrer, { action: 'restore' });
          }
        });
      });
    });

@endsection

変更は、投稿で成功したときを認識して、Turbolinks.visit()をリダイレクト先でコールするところです。

さて、問題は投稿が成功したか否かをどう判定するか、です。

それには、サーバーサイドでPOSTが生成するレスポンスのヘッダーにリダイレクト先を含む項目を追加してクライアント(ブラウザ)にコミュニケートする必要があります。上の例では、ヘッダーにTurbolinks-Locationの項目に値があれば、リダイレクトが必要という条件文になりました。その情報がなければ、バリデーションエラーが発生したので同じ画面を表示します。

さて、POSTのレスポンスにこの特別なヘッダーの情報を追加するには、以下のようにコントローラを編集します。


namespace App\Http\Controllers\Auth;

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

// register()で使用される以下の2行の宣言が必要!
use Illuminate\Http\Request;
use Illuminate\Auth\Events\Registered;

class RegisterController extends Controller
{
    use RegistersUsers;
...
   /**
     * 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());

        return response('success', 200)
            ->header('Turbolinks-Location', $this->redirectPath());

    }
}

RegistersUsersのトレイトで定義されている、register()メソッドをコピーペーストして上書きします。
ヘッダーのTurbolinks-Locationには、リダイレクト先のURLが含まれることになります。
ちなみに、response()で指定している、’success’の文字列は使用しないのでなんでもよいです。空でも。

これで登録を実行すると、POSTのヘッダーには以下のように, Turbolinks-Location/homeの値となります。そして、その値が先のインラインのjsで抽出されて、Turbolinks.visit()に渡されてbodyタグの中身を取り換えてブラウザのURLを変更します。

HTTP/1.1 200 OK
Date: Fri, 13 Dec 2019 17:31:21 GMT
Server: Apache/2.4.38 (Fedora) OpenSSL/1.1.1b
X-Powered-By: PHP/7.2.16
Cache-Control: no-cache, private
Turbolinks-Location: /home
...

リダイレクト先には、Turbolinks-Locationというヘッダーの変数名を使用しましたが、他と重複がなければどんな名前でもよいです。サーバーとクラインとで一致している限り。

コントローラを編集しなければならない部分が残念ですが、たいした編集ではないので、私の中ではやはりサイトのSPA化ではまだまだコストパフォーマンスが大きい技術です。

By khino