You are here
Home > 開発ツール

phanは楽しい!(2) laravel編

前回紹介した楽しい開発ツールphanを、今回はLaravelのプロジェクトに適用してみます。さて、どれだけ楽しくなるか!

まず、Laravel 5.4の私の日本語開発用のレポジトリをインストールします。

$ git clone git@github.com:lotsofbytes/larajapan.gitcat

次に、phanのパッケージをインストールします(注1)

$ cd larajapan
$ composer require --dev phan/phan

インストールできたところで、phanの設定をします。

.phanのディレクトリを作成して、config.phpのファイルを作成します。


return [
    // 解析のためにクラスやメソッドの情報を取得するディレクトリ。
    // これらと、以下の exclude_analysis_directory_list で指定した
    // ディレクトリとの差分が解析チェックの対象となる
    'directory_list' => [
        'app',
        'routes',
        'vendor'
    ],

    // 解析チェックの対象から除外するディレクトリ。
    'exclude_analysis_directory_list' => [
       'vendor'
    ],
];

設定ファイルが用意できたところで、最初のphanの実行をしてみましょう。

$ vendor/bin/phan -p 
app/Auth/Notifications/ResetPassword.php:51 PhanTypeMismatchArgument Argument 2 (parameters) is string but \route() takes array defined at vendor/laravel/framework/src/Illuminate/Foundation/helpers.php:707
app/Auth/Passwords/CanResetPassword.php:16 PhanUndeclaredProperty Reference to undeclared property \App\Auth\Passwords\CanResetPassword->email
app/Auth/Passwords/CanResetPassword.php:27 PhanUndeclaredMethod Call to undeclared method \App\Auth\Passwords\CanResetPassword::notify
app/Providers/RouteServiceProvider.php:54 PhanUndeclaredMethod Call to undeclared method \Illuminate\Routing\Route::namespace
app/Providers/RouteServiceProvider.php:68 PhanUndeclaredMethod Call to undeclared method \Illuminate\Routing\Route::namespace
routes/api.php:16 PhanUndeclaredClassMethod Call to method middleware from undeclared class \Route
routes/channels.php:14 PhanUndeclaredClassMethod Call to method channel from undeclared class \Broadcast
routes/console.php:16 PhanUndeclaredClassMethod Call to method command from undeclared class \Artisan
routes/console.php:17 PhanUndeclaredVariable Variable $this is undeclared
routes/web.php:14 PhanUndeclaredClassMethod Call to method get from undeclared class \Route
routes/web.php:22 PhanUndeclaredClassMethod Call to method get from undeclared class \Route
routes/web.php:23 PhanUndeclaredClassMethod Call to method post from undeclared class \Route
routes/web.php:24 PhanUndeclaredClassMethod Call to method post from undeclared class \Route
routes/web.php:27 PhanUndeclaredClassMethod Call to method get from undeclared class \Route
routes/web.php:28 PhanUndeclaredClassMethod Call to method post from undeclared class \Route
routes/web.php:31 PhanUndeclaredClassMethod Call to method get from undeclared class \Route
routes/web.php:32 PhanUndeclaredClassMethod Call to method post from undeclared class \Route
routes/web.php:33 PhanUndeclaredClassMethod Call to method get from undeclared class \Route
routes/web.php:34 PhanUndeclaredClassMethod Call to method post from undeclared class \Route
routes/web.php:36 PhanUndeclaredClassMethod Call to method get from undeclared class \Route

たくさんエラー出てきました。

その中でもPhanUndeclaredClassMethodが多い。これは宣言されていないクラスのメソッドをコールしているエラーです。

例えば、一番多いエラーとなっているRouteのクラスは、Laravel特有のFacade(ファサード)です。ネームスペースnamespaceがあるクラスと違い、phanにとっては解析が難しいようです。

調査したところ、IDEヘルパーの開発ツールを使用して、stabのファイルを作成することで回避できそうです。Laravelのstabのファイルの作成には、まず以下を実行してide-helperのパッケージをインストールします。

$ composer require --dev barryvdh/laravel-ide-helper

次に、config/app.phpを編集して以下を追加、

Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class

このパッケージは、debugbarの開発者が作成したものです。本来は、phpStormなどのIDEのエディターでのヘルパーが目的(それゆえにlaravel-ide-helper)ですが、ファサードのクラスのメソッドを宣言してくれるために、phanでも役に立つようです。

..
    'providers' => [  
..
        /*                                                                                                                                                                                                         
        * Package Service Providers...                                                                                                                                                                            
        */                                                                                                                                                                                                        
        Laravel\Tinker\TinkerServiceProvider::class,                                                                                                                                                               
        Barryvdh\Debugbar\ServiceProvider::class,
        Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider::class,

..

そして、以下を実行。

$ php artisan ide-helper:generate

_ide_helper.phpのファイルが実行したディレクトリに作成されます。これを、.phan/stabsのディレクトリを作成して移します。

$ mkdir .phan/stabs
$ mv _ide_helper.php .phan/stabs

さらに、このファイルを利用するために、.phan/config.phpも編集が必要です。


return [
    // 解析のためにクラスやメソッドの情報を取得するディレクトリ。
    // これらと、以下の exclude_analysis_directory_list で指定した
    // ディレクトリとの差分が解析チェックの対象となる
    'directory_list' => [
     '.phan/stabs',
        'app',
        'routes',
        'vendor'
    ],

    // 解析チェックの対象から除外するディレクトリ。
    'exclude_analysis_directory_list' => [
       '.phan',
       'vendor'
    ],
];

これで実行準備完了です。再度実行してみましょう。

$ vendor/bin/phan -p
app/Auth/Notifications/ResetPassword.php:51 PhanTypeMismatchArgument Argument 2 (parameters) is string but \route() takes array defined at vendor/laravel/framework/src/Illuminate/Foundation/helpers.php:707
app/Auth/Passwords/CanResetPassword.php:16 PhanUndeclaredProperty Reference to undeclared property \App\Auth\Passwords\CanResetPassword->email
app/Auth/Passwords/CanResetPassword.php:27 PhanUndeclaredMethod Call to undeclared method \App\Auth\Passwords\CanResetPassword::notify
app/Providers/RouteServiceProvider.php:54 PhanUndeclaredStaticMethod Static call to undeclared method \Illuminate\Support\Facades\Route::middleware
app/Providers/RouteServiceProvider.php:68 PhanUndeclaredStaticMethod Static call to undeclared method \Illuminate\Support\Facades\Route::prefix
routes/api.php:16 PhanUndeclaredStaticMethod Static call to undeclared method \Route::middleware
routes/channels.php:14 PhanUndeclaredStaticMethod Static call to undeclared method \Broadcast::channel
routes/console.php:17 PhanUndeclaredVariable Variable $this is undeclared

PhanUndeclaredClassMethodのエラーが少なくなりました。しかし、残り未宣言のエラーの解決は現段階では無理そうです。実際宣言されていにも関わらず、その情報をphanに伝えることが自動のツールでは不可能だからです。将来に期待しましょう。

とりあえず、ここでは-iオプションで、この未宣言のエラーを無視することにします。

$ vendor/bin/phan -p -i
app/Auth/Notifications/ResetPassword.php:51 PhanTypeMismatchArgument Argument 2 (parameters) is string but \route() takes array defined at vendor/laravel

-iで、エラーは1つだけとなりました。

このエラーは、以下のroute()の2番目の引数が配列である必要があるということで、

..
   public function toMail($notifiable)                                                                                                                                                                            
    {                                                                                                                                                                                                              
        return (new MailMessage)                                                                                                                                                                                   
            ->subject('パスワードリセット')                                                                                                                                                                        
            ->greeting('パスワードリセット')                                                                                                                                                                       
            ->line('パスワードリセットリンクの送信のリクエストがありました。')                                                                                                                                     
            ->action('リセットパスワード', url(config('app.url').route('password.reset', [$this->token], false)))                                                                                                    
            ->line('リクエストされていなかったら、無視してください。');                                                                                                                                            
    }     
..

と編集して解決できました。

ということで、インストールは”そう楽しくなさそうな”ツールですが、もし大きなプログラムがあるなら一度でも試してください。私のプロジェクトではいくつか重要なバグをみつけてくれてそれだけでも十分な貢献です。これからも、検証のツールとしてunit testとともに重要なものになりそうです。

ひとつ注意することは、この実行には空きメモリが2GB以上必要です。残念ながら私のテスト環境のawsのsmall(メモリ2GB)のインスタンスでは実行不可能でした。仮想の開発環境(メモリ4GB)で実行可能でした。

注1

composerで、phpdocumentor/reflection-docblockのバージョンのコンフリクトが出ました。すでにインストールされているそのパッケージのバージョンは4.1.1ですが、phan/phanが必要とするものはそれよりも昔のバージョン。ということで、

..
   "require": {
        "php": ">=5.6.4",
        "barryvdh/laravel-debugbar": "~2.4",
        "laravel/framework": "5.4.*",
        "laravel/tinker": "~1.0",
        "phpdocumentor/reflection-docblock": "4.1.1 as 3.3.0"
    },
..

4.1.1 as 3.3.0として4.1.1のバージョンを3.3.0のバージョンとして扱ってもらうことで解決できました。

phanは楽しい!(1)

php7に更新したら使ってみたいと思っていたツールがありました。

このphpの静的解析ツールツールの名前は、Phan。ファンと呼びます。多分、楽しいという英語のfunにひっかけて。Githubでは、以下で公開されています。

https://github.com/phan/phan

さて、これがどうして重要なツールかというと、

phpはもともと開発の敷居を落とすためにCやJavaなどと違ってデータタイプを宣言しなくてよいプログラム言語、しかしプログラムが大きくなり複雑になってくると逆にその緩さが多くの間違いの原因となります。

例えば、

function double($i)
{
  return $i*2;
}

$x = '文字列';

echo double($x);

これを実行するとphpではエラーなしに、0を返します。関数doubleは数字を引数に期待しているはずなのに、phpは数字でなく文字列を渡しても問題なく処理してしまいます。もちろん、ここでの実行は意図的ですが、もし間違いで文字列を渡してしまったら、そのコードがもし他のコードの奥に隠れていたら、原因がわからないバグになりかねません。

phanは、ここの間違いをphpのプログラムを実行することなし(それゆえに静的解析)に見つけてくれる重要な開発検証ツールなのです。

まずは、phanのインストールからなのですが、今回はphanの紹介ということで、Laravelなしの環境でのインストールです。次回には、より複雑なLaravelプログラムの環境でのphanの実行を説明します。

さて、簡単にインストールできるよ、と行きたいのですが、実際は少々ややこしく、astのモジュールとphanのパッケージの2ステップのインストールとなります。

1.astのモジュールのインストール

ast(abstract syntax tree、抽象シンタックスツリー)は、phanに不可欠なphpプログラムの解析モジュールで、特定のプログラムでなくマシン全体でのグローバルのインストールとなります。そして、多分たいていの環境ではデフォルトでインストールされていないと思います。

まずは、そのモジュールが存在するかどうかのチェックとして、

$ php -m

の実行で、astがでて来なかったら、以下を実行してください。

$ pecl install ast

その後、/etc/php.iniを編集して、

extension=ast.so

を追加します。

残念ながらここでくじけたら、もうphanは使えません。くじけたくないなら、以下の開発者のgithubを見てください。

https://github.com/nikic/php-ast

話それますが、このastの開発者、いろいろ面白いツール作成しています。プログラムのコンパイルなどの理論に興味のある方は是非以下も。

https://github.com/nikic

2.phanのインストール

次は以下を実行して、パッケージをインストールします。

$ composer require --dev phan/phan

--devは、開発だけに使用という意味です。

これで、現在のディレクトリは、

.
├── composer.json
├── composer.lock
├── test.php
└── vendor
    ├── autoload.php
    ├── bin
    ├── composer
    ├── felixfbecker
    ├── netresearch
    ├── nikic
    ├── phan
    ├── phpdocumentor
    ├── psr
    ├── sabre
    ├── symfony
    └── webmozart

となります。test.phpは先ほどの例のプログラム。

3.phanの実行

実行は簡単です。ターゲットのファイルを指定するのみです。

$ ./vendor/bin/phan test.php

これを実行すると、何も出力ありません!

何がおかしいのでしょう?

phanは、エラーを出すには関数の引数のデータタイプが必要なのです。phpではそのためにDocblock(ドクブロック)をコメントの中に入れる必要あります。test.phpを編集して、@param float $iを関数定義の直上のコメントに入れます。

/**                                                                                                                                                                                                                
 * @param float $i                                                                                                                                                                                                   
 * @return float                                                                                                                                                                                                    
 */                                                                                                                                                                                                                
function double($i)                                                                                                                                                                                                
{                                                                                                                                                                                                                  
  return $i*2;                                                                                                                                                                                                     
}                                                                                                                                                                                                                  
                                                                                                                                                                                                                   
$x = '文字列';                                                                                                                                                                                                     
                                                                                                                                                                                                                   
echo double($x); 

再度、phanを実行すると、

test.php:14 PhanTypeMismatchArgument Argument 1 (i) is string but \double() takes float defined at test.php:7

とエラーを出してくれます。もちろん、$x = 1.5と数字ならエラーは出ません。

簡単なphanの実演でしたが、このツールを活用するにはDocblockの作成がまず必要ということです。Docblockの作成はちょっと面倒な作業かもしれません。しかし、phpはバージョン5から、関数で型宣言ができ、sublimeなどの最近のエディターはそれを探知して、自動的にエディタでドクブロックも作成してくれます。また、それをもとにAPIドキュメントも作成してくれるツールもあります。ドクブロックは必須となっている昨今です。しかし、最終的には、1つの間違いでも減らしたいかどうかなのです。
 
 
最後に、このツールは、つい最近までは、phan/phanではなくetsy/phanという名前でした。
 

 
このetsy(エッチーあるいはイッチーと発音)というのはetsy.comハンドクラフトの人が自分の作品を販売できるサイトです。基本的にはハンドメードのものしか販売ダメという方針で人気があり、2014年末で登録会員(売る方でなく買う方)がなんと5400万人という。

そして凄いことに、この大きなスケールのサイトは、なんとphpで動いています。さらにでかいFacebookもPHPという話だからそう驚く必要はないですけれど。そして、それゆえに、今回のようなphpの開発のためのプログラムツールを公開してくれています。

Laravelのアップグレードとgitでのバージョン管理

Gitは、開発になくてはならないバージョン管理のツール。Laravelのバージョンをアップグレードがあるときは、フォルダーやファイルが入れ替わるためにGitで新ブランチを作成してから、更新作業を行います。しかし、問題はこの新ブランチは現在のマスターの複製であるため、以前のバージョンのファイルがすでに存在することです。

新しいバージョンと入れ替える1つの方法は、.git以外のフォルダーやファイルをすべて削除して、そこに新バージョンのLaravelを入れ直す。しかし、これを行うとGitで保存されている以前の変更やログぼ履歴が残ってしまい、新しくゼロからスタートができません。新しいバージョンでの開発は、あたかもまっさらからのように始めたい。

前回のLaravel 5.4のレポジトリ作成時にこの問題とぶつかり、どうしたら良いものやらと調査した結果、以下のような手順になりました。

masterを5.3ブランチと改名

現在は、masterにいます。

$ git branch -a
* master
  remotes/origin/HEAD -> origin/master
  remotes/origin/master

 
masterを5.3のブランチに改名します。

$ git branch -m master 5.3

 
リモートレポジトリ(github)にプッシュします。

$ git push origin 5.3

 

5.4ブランチを作成してmasterに改名

現在は、5.3のブランチにいます。

$ git branch -a
* 5.3
  remotes/origin/5.3
  remotes/origin/HEAD -> origin/master
  remotes/origin/master

 
まずは、親なしの5.4ブランチを作成します。親なし--orphanとすることによりブランチの変更やログの履歴が削除されます。この時点ではすべてのフォルダーやファイルはチェックアウトされていません。そして、それらをresetで全部削除します。残っているのは、.gitのディレクトリのみです。

$ git checkout --orphan 5.4
$ git reset --hard

 
次に、ここにLaravel 5.4の初期ファイルを入れますが、.gitのディレクトリがあるためにエラーがでて入れることができません。それゆえに、一旦tmpのディレクトリに入れてから中身を移します。.env.exampleなどのドットファイルも移していることに注意してください。.gitを違うディレクトリに移してから、同ディレクトリに作成してもいいですね。そして、gitのディレクトリを戻す。

$ composer create-project --prefer-dist laravel/laravel tmp 5.4.*
$ mv tmp/* tmp/.[^.]* .
$ rmdir tmp

 
追加されたフォルダーやファイルをコミットします。そして、5.4ブランチをmasterに改名します。

$ git add .
$ git commit -m 'Laravel 5.4 init'
$ git branch -m master

 
そして最後にレポジトリ(github)にプッシュします。全部入れ替えるので --forceオプションが必要です。

$ git push -u origin master --force
$ git branch -a
* master
  remotes/origin/5.3
  remotes/origin/HEAD -> origin/master
  remotes/origin/master

ということで、githubでは以下のようにドロップダウンに2つのブランチが見えます。

Laravelの日本語レポジトリの作成(Laravel 5.4)

前回に作成したLaravelの日本語のリポジトリ(Laravel 5.4)。今回はその作成の仕方を説明します。ほとんどは、Laravel 5.3のときと同じですが、いくつか違いがあります。

コマンドの実行

まずは、以下のcomposerのコマンドを実行します。

composer create-project --prefer-dist laravel/laravel larajapan 5.4.*	

上で使用されているコマンドの引数は、

--prefer-dist laravel/laravel

https://packagist.org/packages/laravel/laravelからパッケージをダウンロードすることを指示します。

larajapan

パッケージのダウンロード先。その名前でディレクトリを作成します。このディレクトリ名は、先のコマンドラインで違う名前を指定可能であるし、実行完了してから改名も可能です。

5.4.*

パッケージのバージョンを指定。ここでは、laravelの5.4を使用します。マイナーバージョンを指定したいなら、5.4.36のように指定します。

実行すると、パッケージに含まれるファイル、さらにパッケージが依存するパッケージのファイルが多数ダウンロードされ少々時間がかかります。

最終的には、実行したディレクトリのもとにlarajapanのディレクトリが作成され、ダウンロードされたファイルが収納されます。

larajapan
├── app/
├── bootstrap/
├── config/
├── database/
├── public/
├── resources/
├── routes/
├── storage/
├── tests/
├── vendor/
├── artisan
├── composer.json
├── composer.lock
├── package.json
├── phpunit.xml
├── readme.md
├── server.php
└── webpack.mix.js

*5.3と違いgulpfile.jsの代わりにwebpack.mix.jsがあります。asset(sassやjs)のコンパイルは、gulpでなくwebpackを使用したlaravel-mixに変わったからです。

次に、ユーザー認証のためのファイル作成を以下の実行で行います。

php artisan make:auth	

この実行により、resources/viewsのディレクトリにおいて、すでにインストールされている以下のコントローラで使用されるbladeファイルが作成されます。

app/Http/Controllers
├── Auth/
│   ├── ForgotPasswordController.php(パスワードのリセットのリンク送信画面)
│   ├── LoginController.php(ログイン画面)
│   ├── RegisterController.php(会員登録画面)
│   └── ResetPasswordController.php(パスワードリセット画面)
├── Controller.php
└── HomeController.php(ログイン後のホーム画面)

最後に以下のコマンドを実行して、先のパスワードのリセットのリンク送信画面から発行されるEメールので使用されるHTMLのテンプレートを作成作成します。

php artisan vendor:publish

日本語化

さて、ここからが日本語化の作業です。

まず、config/app.phpの編集から。

                                                                                                                                                                                                                                                                                                                                                   
...
    'timezone' => 'UTC',                                                                                                                                                                                                                                                                                                               
                                                                                                                                                                                              
    'locale' => 'en',
...

                                                                                                                                                                                                                                                                                                                                                   
...
    'timezone' => 'Asia/Tokyo',                                                                                                                                                                                                                                                                                                               
                                                                                                                                                                                              
    'locale' => 'ja',
...

と変えて保存します。

timezone

これは、通常、プログラム内の日時設定のタイムゾーンとして使用されるもので、PHPの以下の関数で使用されます。

date_default_timezone_set()

ここで設定すれば、後はLaravelが面倒みてくれます。

日本時間の場合は、Aisa/Tokyoの設定だけで十分。

locale

resources/langで言語のファイルが以下のように存在します。これらは、Laravelのプロジェクトでバリデーションのエラーメッセージなどを定義しています。

resources/lang
└── en/
    ├── auth.php
    ├── pagination.php
    ├── passwords.php
    └── validation.php

デフォルトの設定では、英語のenのディレクトリしかありません。日本語の翻訳を作成するには、上で設定したjaと同じ名前のディレクトリをそこに作成します。以下の実行でディレクトリごとコピーしてください。

$ cp -pr en ja

バリデーションに関しては、見米氏のバリデーション(1)Validatorファサードのextend を参照してください。

今回は、新しく追加されたバリデーションの以下のエントリーもあります。

after_or_equal
before_or_equal

次は、ユーザー認証画面などで使用されるブレードファイルの翻訳です。

私のLaravelの日本語レポジトリでは、以下は、すべて翻訳してあります。

resources/views
├── auth
│   ├── login.blade.php
│   ├── passwords
│   │   ├── email.blade.php
│   │   └── reset.blade.php
│   └── register.blade.php
├── errors
│   └── 404.blade.php
├── home.blade.php
├── layouts
│   └── app.blade.php
├── vendor
│   ├── mail
│   │   ├── html
│   │   │   ├── button.blade.php
│   │   │   ├── footer.blade.php
│   │   │   ├── header.blade.php
│   │   │   ├── layout.blade.php
│   │   │   ├── message.blade.php
│   │   │   ├── panel.blade.php
│   │   │   ├── promotion
│   │   │   │   └── button.blade.php
│   │   │   ├── promotion.blade.php
│   │   │   ├── subcopy.blade.php
│   │   │   ├── table.blade.php
│   │   │   └── themes
│   │   │       └── default.css
│   │   └── markdown
│   │       ├── button.blade.php
│   │       ├── footer.blade.php
│   │       ├── header.blade.php
│   │       ├── layout.blade.php
│   │       ├── message.blade.php
│   │       ├── panel.blade.php
│   │       ├── promotion
│   │       │   └── button.blade.php
│   │       ├── promotion.blade.php
│   │       ├── subcopy.blade.php
│   │       └── table.blade.php
│   ├── notifications
│   │   └── email.blade.php
│   └── pagination
│       ├── bootstrap-4.blade.php
│       ├── default.blade.php
│       ├── simple-bootstrap-4.blade.php
│       └── simple-default.blade.php
└── welcome.blade.php

5.3と違い、errorsのフォルダーは作成されていませんでしたので追加しました。また、vendor/mailのフォルダーは新規のもので、HTMLだけでなくマークダウンも対応しています。

パスワードリセットで送信されるEメールの翻訳

ここまで来ても、残念ながら、パスワードを忘れたときに送信される、パスワードリセットを含むEメールの内容がまだ翻訳されていません。なぜなら、本文がハードコードされているからです。

これはちょっと頭をひねりましたが、多分以下が最小の変更で対応できると思います。

まず、

vendor/laravel/framework/src/Illuminate/AuthのディレクトリからResetPassword.phpCanResetPassword.phpのファイルを以下の場所にコピーします。

app/Auth
├── Notifications/
│   └── ResetPassword.php
└── Passwords/
    └── CanResetPassword.php

次に、以下のようにファイルを編集します。app/User.phpのファイルも変更必要です。

namespace App\Auth\Notifications;                                                                                                                                                             
                                                                                                                                                                                              
use Illuminate\Notifications\Notification;                                                                                                                                                    
use Illuminate\Notifications\Messages\MailMessage;                                                                                                                                            
                                                                                                                                                                                              
class ResetPassword extends Notification                                                                                                                                                      
{                  
...
    /**                                                                                                                                                                                       
     * Build the mail representation of the notification.                                                                                                                                     
     *                                                                                                                                                                                        
     * @param  mixed  $notifiable                                                                                                                                                             
     * @return \Illuminate\Notifications\Messages\MailMessage                                                                                                                                 
     */                                                                                                                                                                                       
    public function toMail($notifiable)                                                                                                                                                       
    {    
        return (new MailMessage)
            ->subject('パスワードリセット')
          ->greeting('パスワードリセット')
            ->line('パスワードリセットリンクの送信のリクエストがありました。')
            ->action('リセットパスワード', url(config('app.url').route('password.reset', $this->token, false)))
            ->line('リクエストされていなかったら、無視してください。');                                                                                                          
    }     
}
namespace App\Auth\Passwords;                                                                                                                                                                 
                                                                                                                                                                                              
use App\Auth\Notifications\ResetPassword as ResetPasswordNotification;                                                                                                                        
                                                                                                                                                                                              
trait CanResetPassword                                                                                                                                                                        
{                                                                                                                                                                                             
    /**                                                                                                                                                                                       
     * Get the e-mail address where password reset links are sent.                                                                                                                            
     *                                                                                                                                                                                        
     * @return string                                                                                                                                                                         
     */                                                                                                                                                                                       
    public function getEmailForPasswordReset()                                                                                                                                                
    {                                                                                                                                                                                         
        return $this->email;                                                                                                                                                                  
    }                                                                                                                                                                                         
                                                                                                                                                                                              
    /**                                                                                                                                                                                       
     * Send the password reset notification.                                                                                                                                                  
     *                                                                                                                                                                                        
     * @param  string  $token                                                                                                                                                                 
     * @return void                                                                                                                                                                           
     */                                                                                                                                                                                       
    public function sendPasswordResetNotification($token)                                                                                                                                     
    {                                                                                                                                                                                         
        $this->notify(new ResetPasswordNotification($token));                                                                                                                                 
    }                                                                                                                                                                                         
}                                                                                                                                                                                             
         
                                                                                                                                                                                              
namespace App;                                                                                                                                                                                
                                                                                                                                                                                              
use Illuminate\Notifications\Notifiable;                                                                                                                                                      
use Illuminate\Foundation\Auth\User as Authenticatable;                                                                                                                                       
use App\Auth\Passwords\CanResetPassword;                                                                                                                                                      
                                                                                                                                                                                              
class User extends Authenticatable                                                                                                                                                            
{                                                                                                                                                                                             
    use Notifiable;                                                                                                                                                                           
    use CanResetPassword;                                                                                                                                                                     
...                         

認証のroutesの設定

日本語化とは関係ないですが、私がこうした方がわかりやすいと思ったことです。

オリジナルのroutes.phpは、

Route::get('/', function () {
    return view('welcome');
});

Auth::routes();

Route::get('/home', 'HomeController@index');

とシンプルですが、Auth::routes()で認証のrouteが隠されてしまって不透明。

ということで、私のLaravelの日本語レポジトリでは、以下のように編集しました。

// 以下は、Auth::routes()の中身を移したもの。将来において変更が可能なように                                                                                                                   
                                                                                                                                                                                              
// Authentication Routes...
Route::get('login', 'Auth\LoginController@showLoginForm')->name('login');
Route::post('login', 'Auth\LoginController@login');
Route::post('logout', 'Auth\LoginController@logout')->name('logout');

// Registration Routes...
Route::get('register', 'Auth\RegisterController@showRegistrationForm')->name('register');
Route::post('register', 'Auth\RegisterController@register');

// Password Reset Routes...
Route::get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm')->name('password.request');
Route::post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail')->name('password.email');
Route::get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm')->name('password.reset');
Route::post('password/reset', 'Auth\ResetPasswordController@reset');                                                                                                                
                                                                                                                                                                                              
Route::get('/home', 'HomeController@index');  

その他

インストールにおいて経験した問題として、

php artisan migrate	

を実行したときに、以下のエラーとなりました。

[Illuminate\Database\QueryException]
SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes (SQL: alter table users add unique users_email_unique(email))

[PDOException]
SQLSTATE[42000]: Syntax error or access violation: 1071 Specified key was too long; max key length is 767 bytes

これは、使用しているデータベースがMariaDBで10.2.2のバージョンより古いか、あるいはMySQLで5.7.7より古いときに起こるエラーです。Laravelは5.4より、絵文字対応のutf8mb4のエンコーディングがデフォルトとなりました。

古いバージョンを使用する場合(大半がそうでは?)は、以下の変更が必要となります。日本語のレポにはこの変更が含まれています。


namespace App\Providers;                                                                                                                                                                                           
                                                                                                                                                                                                                   
use Illuminate\Support\ServiceProvider;                                                                                                                                                                            
use Illuminate\Support\Facades\Schema;                                                                                                                                                                             
                                                                                                                                                                                                                   
class AppServiceProvider extends ServiceProvider                                                                                                                                                                   
{                                                                                                                                                                                                                  
    /**                                                                                                                                                                                                            
     * Bootstrap any application services.                                                                                                                                                                         
     *                                                                                                                                                                                                             
     * @return void                                                                                                                                                                                                
     */                                                                                                                                                                                                            
    public function boot()                                                                                                                                                                                         
    {                                                                                                                                                                                                              
        Schema::defaultStringLength(191);                                                                                                                                                                          
    }                                                                                                                                                                                                              
                                                                                                                                                                                                                   
    /**                                                                                                                                                                                                            
     * Register any application services.                                                                                                                                                                          
     *                                                                                                                                                                                                             
     * @return void                                                                                                                                                                                                
     */                                                                                                                                                                                                            
    public function register()                                                                                                                                                                                     
    {                                                                                                                                                                                                              
        //                                                                                                                                                                                                         
    }                                                                                                                                                                                                              
}   

Laravelの日本語のレポジトリ(Laravel 5.4)

Laravelの日本語のリポジトリをLaravel5.4に更新しました。もうLaravel5.5がリリースされていますが、Laravel5.5と違ってLaravel5.4は、php7でなくphp5.6のバージョンで動作する最後のバージョンなゆえに重要です。前回同様に、以下インストールの手順です。

Laravelにおいて新規のプロジェクト作成はとても簡単。コマンドラインでいくつかのコマンドを実行をちょちょいとすれば完了。しかし、インストールされるのは英語のプロジェクト。テンプレートやメッセージの翻訳をいちいちしなければ日本語のプロジェクトにはならない。

ここのプロセスを簡単にと、Laravelバージョン5.4をもとに、開発者のために日本語化したレポジトリを作成してみました。

このレポジトリには、

  • デフォルトのユーザー認証の機能:会員登録、パスワードリセット、会員ログイン
  • 日本語に翻訳されたデフォルトのテンプレートとEメールメッセージ
  • 日本語に翻訳されたデフォルトの入力エラーメッセージ
  • デバッグのためのDebugbarツール
  • ウェブ解析ツールGoogle Analyticsのトラッキングスクリプト

以上を含みます。

さらに、今回は、実際動作するデモとして以下に用意しました。
https://larajapan.lotsofbytes.com/larajapan

さて、このレポジトリのインストールは以下の手順で簡単にできます。

レポジトリのインストール

SSHを利用しているなら、以下をコマンドラインで実行してレポジトリをインストールします。

$ git clone git@github.com:lotsofbytes/larajapan.git

あるいは、Httpsを使用するなら、以下を実行します。

$ git clone https://github.com/lotsofbytes/larajapan.git

実行後は、larajapanのディレクトリが作成されますが、お好きな名前に改名してもらってOKです。

その後、

cd larajapan

それから、ファイルのパーミッションを与えるべく以下を実行。

$ chmod -R a+w bootstrap/cache
$ chmod -R a+w storage

インストール後は、以下を実行してください。

$ composer install

.envの編集

.env.example をコピーして、.env を作成し編集して以下のように設定します。*****の部分を適切な値に変更してください。

APP_NAME=私のララベル
APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_DATABASE=*****
DB_USERNAME=*****
DB_PASSWORD=*****
MAIL_DRIVER=sendmail

その後、以下を実行して.env内のAPP_KEYを更新します。

$ php artisan key:generate

APP_DEBUG=trueこれによりDebugbarが画面下方に表示されます。Debugbarに関しては、Debugbarで楽々デバッグも読んでください。

また、
ANALYTICS=UA-XXXXXXのようにサイトのためにGoogleから取得したコードを設定すれば、Google Analyticsでウェブでのユーザーの動向が追跡できます。

DBを作成

.envで指定したDBを作成。

$ echo 'CREATE DATABASE larajapan CHARACTER SET utf8' | mysql -u root -p
php artisan migrate

ウェブサーバーの立ち上げ

最後に以下を実行して、ウェブサーバーを立ち上げると、

$ php artisan serve

以下のアドレスでブラウザーからアクセスできます。

http://localhost:8000

以上です。

上の設定は私の開発環境Fedora LinuxとAmazon Linux OSで、7.0のバージョンのPHPで、DBにはMysqlあるいはMaria DBを使用して動作確認しています。しかし、皆さんの環境ではいろいろ異なることがあると思います。問題や指摘があれば、ご連絡ください。

php5.6からphp7.0へ

最近になってようやくphp5.6の環境からphp7.0へと移行をしました。すでにphp7.2RCも登場している昨今、今さらなんて声聞こえますね。

カスタマイスのプログラムを書いてその管理を長い間していると、プログラムは大きくなるし複雑になるし、簡単にphpの更新ができない、いや馴染んだ古巣から新しい世界へ行くのが恐ろしい!

私の言い訳はともかく、世の中の状況はいかにと調べたところ、こんな統計が出てきました。

参照元

去年から今年へと、php7の使用率がphp5.6を抜いています。

phpのサポートはどうなのでしょう?phpのサイトによると、


参照元

緑は「サポート中」、オレンジは「セキュリティ修正のみ」、赤は「終了」です。

5.6のサポートはセキュリティ修正のみが2018年いっぱい(まだ人気が高い証拠?)とまだ使用OKのようですが、8月にリリースされたLaravelの最新版5.5が長期サポート(LTS)のバージョンでは、バグ修正が2年間、セキュリティ修正が3年とサポートがあり、そしてなんと、

PHP 7.0.0以降が必須

となりました。

私のお客さんのプログラムは、最高でLaravel 5.4どまりですぐに更新する必要ないと言えば必要ないですが、php7.0が発表(上のグラフによるとほぼ2年前!)されてからとても気になるパフォーマンスの改善が「どうしても更新しなければならない」と駆り立てます。

これまた古い資料で申し訳ないですが、


参照元

phpで書かれているが非常にスローなWordpressがなんと2倍に近い速さとなるではないですか。

実際、このブログをホストしているサーバーもphp7にしたところサクサク動くようになりました。お客さんのサイトでもapacheのabテストをphp7更新前後でLaravelのプログラムの実行を計測したところ、こちらも1/2の実行時間になりました。バージョンを更新しただけでこの改善は凄い!

さて、php5.6からphp7.0にアップしたときの苦労と言えば、laravelのプログラムに関する限りほとんどゼロに近い変更でした。私の場合は、php7をサポートしていないpearのライブラリを使用した部分の変換が唯一の問題でした。

そして最後に、php7にアップデートしたお陰で、前から注目していて、とても開発に役に立つ、特に大きくて複雑なプログラムには多分必須のツールが使えるようになります。それは次回に紹介しましょう。

Laravelの日本語レポジトリの作成(Laravel 5.3)

前回に作成したLaravelの日本語レポジトリ(Laravel 5.3)。今回はその作成の仕方を説明します。

コマンドの実行

まずは、以下のcomposerのコマンドを実行します。

composer create-project --prefer-dist laravel/laravel larajapan 5.3.*	

上で使用されているコマンドの引数は、

--prefer-dist laravel/laravel

https://packagist.org/packages/laravel/laravelからパッケージをダウンロードすることを指示します。

larajapan

パッケージのダウンロード先。その名前でディレクトリを作成します。このディレクトリ名は、先のコマンドラインで違う名前を指定可能であるし、実行完了してから改名も可能です。

5.3.*

パッケージのバージョンを指定。ここでは、laravelの5.3を使用します。マイナーバージョンを指定したいなら、5.3.30のように指定します。

実行すると、パッケージに含まれるファイル、さらにパッケージが依存するパッケージのファイルが多数ダウンロードされ少々時間がかかります。

最終的には、実行したディレクトリのもとにlarajapanのディレクトリが作成され、ダウンロードされたファイルが収納されます。

larajapan
├── app/
├── bootstrap/
├── config/
├── database/
├── public/
├── resources/
├── routes/
├── storage/
├── tests/
├── vendor/
├── artisan*
├── composer.json
├── composer.lock
├── gulpfile.js
├── package.json
├── phpunit.xml
├── readme.md
└── server.php

次に、ユーザー認証のためのファイル作成を以下の実行で行います。

php artisan make:auth	

この実行により、resources/viewsのディレクトリにおいて、すでにインストールされている以下のコントローラで使用されるbladeファイルが作成されます。

app/Http/Controllers
├── Auth/
│   ├── ForgotPasswordController.php(パスワードのリセットのリンク送信画面)
│   ├── LoginController.php(ログイン画面)
│   ├── RegisterController.php(会員登録画面)
│   └── ResetPasswordController.php(パスワードリセット画面)
├── Controller.php
└── HomeController.php(ログイン後のホーム画面)

最後に以下のコマンドを実行して、先のパスワードのリセットのリンク送信画面から発行されるEメールので使用されるHTMLのテンプレートを作成作成します。

php artisan vendor:publish

日本語化

さて、ここからが日本語化の作業です。

まず、config/app.phpの編集から。

                                                                                                                                                                                                                                                                                                                                                   
...
    'timezone' => 'UTC',                                                                                                                                                                                                                                                                                                               
                                                                                                                                                                                              
    'locale' => 'en',
...

                                                                                                                                                                                                                                                                                                                                                   
...
    'timezone' => 'Asia/Tokyo',                                                                                                                                                                                                                                                                                                               
                                                                                                                                                                                              
    'locale' => 'ja',
...

と変えて保存します。

timezone

これは、通常、プログラム内の日時設定のタイムゾーンとして使用されるもので、PHPの以下の関数で使用されます。

date_default_timezone_set()

ここで設定すれば、後はLaravelが面倒みてくれます。

日本時間の場合は、Aisa/Tokyoの設定だけで十分。

locale

resources/langで言語のファイルが以下のように存在します。これらは、Laravelのプロジェクトでバリデーションのエラーメッセージなどを定義しています。

resources/lang
└── en/
    ├── auth.php
    ├── pagination.php
    ├── passwords.php
    └── validation.php

デフォルトの設定では、英語のenのディレクトリしかありません。日本語の翻訳を作成するには、上で設定したjaと同じ名前のディレクトリをそこに作成します。以下の実行でディレクトリごとコピーしてください。

$ cp -pr en ja

バリデーションに関しては、見米氏のバリデーション(1)Validatorファサードのextend を参照してください。

次は、ユーザー認証画面などで使用されるブレードファイルの翻訳です。

私のLaravelの日本語レポジトリでは、以下は、すべて翻訳してあります。

resources/views
├── auth
│   ├── login.blade.php
│   ├── passwords
│   │   ├── email.blade.php
│   │   └── reset.blade.php
│   └── register.blade.php
├── errors
│   └── 503.blade.php
├── home.blade.php
├── layouts
│   └── app.blade.php
├── vendor
│   ├── notifications
│   │   ├── email.blade.php
│   │   └── email-plain.blade.php
│   └── pagination
│       ├── bootstrap-4.blade.php
│       ├── default.blade.php
│       ├── simple-bootstrap-4.blade.php
│       └── simple-default.blade.php
└── welcome.blade.php

翻訳とは関係ありませんが、app.blade.phpのレイアウトで参照されている、app.cssapp.jsには、url()を入れて、プロジェクトがインストールされるディレクトリが変わっても参照されるようにしてあります。

パスワードリセットで送信されるEメールの翻訳

ここまで来ても、残念ながら、パスワードを忘れたときに送信される、パスワードリセットを含むEメールの内容がまだ翻訳されていません。なぜなら、本文がハードコードされているからです。

これはちょっと頭をひねりましたが、多分以下が最小の変更で対応できると思います。

まず、

vendor/laravel/framework/src/Illuminate/AuthのディレクトリからResetPassword.phpCanResetPassword.phpのファイルを以下の場所にコピーします。

app/Auth
├── Notifications/
│   └── ResetPassword.php
└── Passwords/
    └── CanResetPassword.php

次に、以下のようにファイルを編集します。app/User.phpのファイルも変更必要です。

namespace App\Auth\Notifications;                                                                                                                                                             
                                                                                                                                                                                              
use Illuminate\Notifications\Notification;                                                                                                                                                    
use Illuminate\Notifications\Messages\MailMessage;                                                                                                                                            
                                                                                                                                                                                              
class ResetPassword extends Notification                                                                                                                                                      
{                  
...
    /**                                                                                                                                                                                       
     * Build the mail representation of the notification.                                                                                                                                     
     *                                                                                                                                                                                        
     * @param  mixed  $notifiable                                                                                                                                                             
     * @return \Illuminate\Notifications\Messages\MailMessage                                                                                                                                 
     */                                                                                                                                                                                       
    public function toMail($notifiable)                                                                                                                                                       
    {                                                                                                                                                                                         
        return (new MailMessage)                                                                                                                                                              
            ->subject('パスワードリセット')                                                                                                                                                   
            ->greeting('パスワードリセット')                                                                                                                                                  
            ->line('パスワードリセットリンクの送信のリクエストがありました。')                                                                                                                
            ->action('リセットパスワード', url('password/reset', $this->token))                                                                                                               
            ->line('リクエストされていなかったら、無視してください。');                                                                                                                       
    }     
}
namespace App\Auth\Passwords;                                                                                                                                                                 
                                                                                                                                                                                              
use App\Auth\Notifications\ResetPassword as ResetPasswordNotification;                                                                                                                        
                                                                                                                                                                                              
trait CanResetPassword                                                                                                                                                                        
{                                                                                                                                                                                             
    /**                                                                                                                                                                                       
     * Get the e-mail address where password reset links are sent.                                                                                                                            
     *                                                                                                                                                                                        
     * @return string                                                                                                                                                                         
     */                                                                                                                                                                                       
    public function getEmailForPasswordReset()                                                                                                                                                
    {                                                                                                                                                                                         
        return $this->email;                                                                                                                                                                  
    }                                                                                                                                                                                         
                                                                                                                                                                                              
    /**                                                                                                                                                                                       
     * Send the password reset notification.                                                                                                                                                  
     *                                                                                                                                                                                        
     * @param  string  $token                                                                                                                                                                 
     * @return void                                                                                                                                                                           
     */                                                                                                                                                                                       
    public function sendPasswordResetNotification($token)                                                                                                                                     
    {                                                                                                                                                                                         
        $this->notify(new ResetPasswordNotification($token));                                                                                                                                 
    }                                                                                                                                                                                         
}                                                                                                                                                                                             
         
                                                                                                                                                                                              
namespace App;                                                                                                                                                                                
                                                                                                                                                                                              
use Illuminate\Notifications\Notifiable;                                                                                                                                                      
use Illuminate\Foundation\Auth\User as Authenticatable;                                                                                                                                       
use App\Auth\Passwords\CanResetPassword;                                                                                                                                                      
                                                                                                                                                                                              
class User extends Authenticatable                                                                                                                                                            
{                                                                                                                                                                                             
    use Notifiable;                                                                                                                                                                           
    use CanResetPassword;                                                                                                                                                                     
...                         

認証のroutesの設定

日本語化とは関係ないですが、私がこうした方がわかりやすいと思ったことです。

オリジナルのroutes.phpは、

Route::get('/', function () {
    return view('welcome');
});

Auth::routes();

Route::get('/home', 'HomeController@index');

とシンプルですが、Auth::routes()で認証のrouteが隠されてしまって不透明。

ということで、私のLaravelの日本語レポジトリでは、以下のように編集しました。

// 以下は、Auth::routes()の中身を移したもの。将来において変更が可能なように                                                                                                                   
                                                                                                                                                                                              
// Authentication Routes...                                                                                                                                                                   
Route::get('login', 'Auth\LoginController@showLoginForm')->name('login');                                                                                                                     
Route::post('login', 'Auth\LoginController@login');                                                                                                                                           
Route::post('logout', 'Auth\LoginController@logout')->name('logout');                                                                                                                         
                                                                                                                                                                                              
// Registration Routes...                                                                                                                                                                     
Route::get('register', 'Auth\RegisterController@showRegistrationForm')->name('register');                                                                                                     
Route::post('register', 'Auth\RegisterController@register');                                                                                                                                  
                                                                                                                                                                                              
// Password Reset Routes...                                                                                                                                                                   
Route::get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm');                                                                                                            
Route::post('password/email', 'Auth\ForgotPasswordController@sendResetLinkEmail');                                                                                                            
Route::get('password/reset/{token}', 'Auth\ResetPasswordController@showResetForm');                                                                                                           
Route::post('password/reset', 'Auth\ResetPasswordController@reset');                                                                                                                          
                                                                                                                                                                                              
Route::get('/home', 'HomeController@index');  

最後に

Laravelの最新バージョンは今週に5.3から5.4となりました。今回説明した(前回に作成したLaravelの日本語レポジトリも)はバージョン5.3をベースにしたものです。しかし、バージョン5.4での日本語レポジトリ作成もいくつかファイルは違いますが、基本的には、最初のcomposerの実行で、5.3.*の代わりに、5.4.*と指定すれば、同じような手順で作成できます。

Laravelの日本語レポジトリ(Laravel 5.3)

** Laravelの日本語レポジトリをLaravel 5.4に更新したために手順において更新があります **

Laravelにおいて新規のプロジェクト作成はとても簡単。コマンドラインでいくつかのコマンドを実行をちょちょいとすれば完了。しかし、インストールされるのは英語のプロジェクト。テンプレートやメッセージの翻訳をいちいちしなければ日本語のプロジェクトにはならない。

ここのプロセスを簡単にと、Laravelバージョン5.3をもとに、開発者のために日本語化したレポジトリを作成してみました。

このレポジトリには、

  • デフォルトのユーザー認証の機能:会員登録、パスワードリセット、会員ログイン
  • 日本語に翻訳されたデフォルトのテンプレートとEメールメッセージ
  • 日本語に翻訳されたデフォルトの入力エラーメッセージ
  • デバッグのためのDebugbarツール
  • ウェブ解析ツールGoogle Analyticsのトラッキングスクリプト

以上を含みます。

さらに、今回は、実際動作するデモとして以下に用意しました。
https://larajapan.lotsofbytes.com/larajapan

さて、このレポジトリのインストールは以下の手順で簡単にできます。

レポジトリのインストール

SSHを利用しているなら、以下をコマンドラインで実行してレポジトリをインストールします。

$ git clone -b 5.3 git@github.com:lotsofbytes/larajapan.git

あるいは、Httpsを使用するなら、以下を実行します。

$ git clone -b 5.3 https://github.com/lotsofbytes/larajapan.git

実行後は、larajapanのディレクトリが作成されますが、お好きな名前に改名してもらってOKです。

その後、

cd larajapan

それから、ファイルのパーミッションを与えるべく以下を実行。

$ chmod -R a+w storage

インストール後は、以下を実行してください。

$ composer install

以下も必要かもしれません。

$ composer update

.envの編集

.env.example をコピーして、.env を作成し編集して以下のように設定します。*****の部分を適切な値に変更してください。

APP_ENV=local
APP_KEY=
APP_DEBUG=true
APP_LOG_LEVEL=debug
DB_CONNECTION=mysql
DB_DATABASE=*****
DB_USERNAME=*****
DB_PASSWORD=*****
MAIL_DRIVER=sendmail

その後、以下を実行して.env内のAPP_KEYを更新します。

$ php artisan key:generate

APP_DEBUG=trueこれによりDebugbarが画面下方に表示されます。Debugbarに関しては、Debugbarで楽々デバッグも読んでください。

また、
ANALYTICS=UA-XXXXXXのようにサイトのためにGoogleから取得したコードを設定すれば、Google Analyticsでウェブでのユーザーの動向が追跡できます。

DBを作成

.envで指定したDBを作成。

$ echo 'CREATE DATABASE larajapan CHARACTER SET utf8' | mysql -u root -p
php artisan migrate

ウェブサーバーの立ち上げ

最後に以下を実行して、ウェブサーバーを立ち上げると、

$ php artisan serve

以下のアドレスでブラウザーからアクセスできます。

http://localhost:8000

以上です。

上の設定は私の開発環境Fedora LinuxとAmazon Linux OSで、5.6のバージョンのPHPで、DBにはMysqlあるいはMaria DBを使用して動作確認しています。しかし、皆さんの環境ではいろいろ異なることがあると思います。問題や指摘があれば、ご連絡ください。

Debugbarで楽々デバッグ

以前に紹介した、Debugbar

私には、もうなくてはならないものになりました。対象の画面で実行されたDBのクエリーはすべて見ることができるし、セッションの中身の値も確認できる。

そして、もうひとつ、プログラムの中で自分が見たいという変数をdebug()のヘルパー関数で、以下のように使用すれば、


class UserController extends Controller
{
	public function getUpload()
	{
		return view('user/upload');
	}

	public function postUpload(Request $request)
	{
		debug($request->all());

		$file = $request->file('file');

		$filename = $file->getClientOriginalName();

		$request->file('file')->move(public_path('images'), $filename);
	}
}

以下の画面のように、バーで表示してくれます。

screen1

行をクリックすれば、詳細を表示します。

screen2

Top