Laravel 5.7 の新機能まとめ

さてさて、前回の記事「Laravel 5.7 のEmail Verification(本登録メール)の使い方」では、リリースされたばかりのLaravel 5.7 から新機能のひとつをお届けしました。

ということで、今回はLaravel 5.7 で追加されたその他の新しい機能をまとめてみたいと思います。

ぜひ効率的なサイト構築に役立ててください。

フォルダ構造が変更になった

小さな変更ですが、Laravel 5.7 では、resources内フォルダ構造が平坦化されました。

バージョン 5.6 の場合は以下のようにassetsが存在していました。

それが今回のアップデートで、以下のように平坦な構造になっています。

Eloquentモデルのエラーメッセージが改善された

例えば以下のような存在しないメソッドを実行する場合です。

\App\User::nothing(); // nothing()は存在しない

これを実行するとLaravel 5.6 の場合は以下のようなエラーを表示していました。

エラー内容を見てみると、Builder::nothing()となっています。「does not exist」と書かれているので、何かが存在しないことは分かるものの肝心の「どこの?」が抜けてしまっていました。

これがLaravel 5.7では以下のように改善されています。

App\User::nothing()というまだ定義されていないメソッドが呼ばれました。」

と、どこの何でエラーが発生しているかを教えてくれるようになりました。

Pagination(ページリンク)で両サイドの件数が指定できるようになった

例えば、以下のようなコードを実行すると、通常のページ送りリンクが表示されます。(見た目はCSSで装飾しています)

echo \App\User::paginate(10)->links();

そして、今回追加されたonEachSide($count)を使うと、この中央の両サイドにあるリンク数を指定することができます。

実際の例で見てみましょう。

echo \App\User::paginate(10)->onEachSide(1)->links();

なお、デフォルト値は3です。

独自Artisanコマンドのテストができるようになった

テスト用の独自コマンドを作る

まず、TestCommandという独自コマンドを作ってみましょう。

php artisan make:command TestCommand

すると、app/Console/CommandsTestCommand.phpが作成されるので、この中に、

  • 好きな食べ物を答えてもらう
  • 好きな音楽ジャンルを選んでもらう

という2つの質問と、その結果を表示するというコードを追加します。

コードは以下のようになります。(太字の部分が追加/変更した場所です)

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class TestCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'command:test';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'テストコマンドです。';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        $food = $this->ask('好きな食べ物は?');
        $music = $this->choice('どの音楽が好きですか?', [
            'ポップ',
            'ロック',
            'クラシック',
        ]);
        $this->line('好きな食べ物は「'. $food .'」、音楽は「'. $music .'」です。');
    }
}

では、php artisan command:testを実行してみましょう。

まず好きな食べ物を答え、

音楽のジャンルを選ぶと、

結果がこのように表示されます。

コマンドをテストする

では、ここからがメインの「Artisanコマンドのテスト」です。実行環境として、PHPUnitを使います。

では、すでに存在しているtests/Unit/ExampleTest.phpを使ってテストを作成してみましょう。

コードは以下になります。

<?php

namespace Tests\Unit;

use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;

class ExampleTest extends TestCase
{
    /**
     * A basic test example.
     *
     * @return void
     */
    public function testBasicTest()
    {
        $this->artisan('command:test')
            ->expectsQuestion('好きな食べ物は?', 'カレー')
            ->expectsQuestion('どの音楽が好きですか?', 'ロック')
            ->expectsOutput('好きな食べ物は「カレー」、音楽は「ロック」です。');
    }
}

まず、テスト実行部分1行目の

$this->artisan('command:test')

で、先ほど作成した独自コマンドcommand:testを実行し、

->expectsQuestion('好きな食べ物は?', 'カレー')
->expectsQuestion('どの音楽が好きですか?', 'ロック')

2つの期待する答えがあっているかチェックします。

->expectsOutput('好きな食べ物は「カレー」、音楽は「ロック」です。')

そして、最後に表示が正しいかチェックします。

では、以下のコマンドでPHPUnitで実行してみましょう。

php vendor/phpunit/phpunit/phpunit

うまくテストを通過したようです。

では、わざと「ロック」を「ポップ」に変更して実行してみます。

->expectsQuestion('どの音楽が好きですか?', 'ポップ')

この場合、以下のようなエラーが表示されることになります。

アウトプットは、

好きな食べ物は「カレー」、音楽は「ロック」です。

が表示されていませんよ!と言っています。

変数の中身を確認するDump-Serverが追加された

dump-serverを使うために、まず以下の起動コマンドを実行します。

php artisan dump-server

すると以下のような内容が表示されます。(終了したい場合はCtr+Cです。)

ではこの状態で以下コードをブラウザから実行してみましょう。

$test = 'xxxxx';

dump($test);

すると、起動中のDump-Serverが以下のような情報を表示してくれます。

GET http://l57.test/dump_server
-------------------------------

------------ -----------------------------------------
date         Thu, 06 Sep 2018 19:12:43 +0000
controller   "HomeController"
source       HomeController.php on line 12
file         app/Http/Controllers/HomeController.php
------------ -----------------------------------------

"xxxxx"

このように、コードを実行している途中に変数の中身をチェックすることができるわけですね。

つまり、以下のようにループごとに変数の中身が変わってしまっても、Dump-Serverを起動していれば変数の中身がどのように変化しているか、その都度表示してくれるので、バグ修正などに役立つことでしょう。

for($i = 0 ; $i < 12 ; $i++) {

    dump($i);

}

action()にクラスを指定してURLを取得できるようになった

まず、web.phpに以下のようなRouteがあるとします。

Route::get('/posts', 'PostsController@index');
Route::get('/posts/{id}', 'PostsController@show');

そして、Laravel 5.6 までは、action()を使って次のようにURLを取得するこができていました。

$url = action('PostsController@index');
$url = action('PostsController@show', ['id' => 1]);

これが 5.7 からは::classを使って直接指定することができるようになりました。

$url = action([PostsController::class, 'index']);
$url = action([PostsController::class, 'show'], ['id' => 1]);

Email Verificationで本登録メールを送信する

これは前回記事、Laravel 5.7 のEmail Verification(本登録メール)の使い方で、詳しく紹介しているのでそちらをご覧ください。

Filesystemにストリーム用のメソッドが追加された

テストとして、storage/app/publictest_1.zipというファイルを用意し、これをtest_2.zipという名前でローカル(つまり、storage/appフォルダ)に保存してみます。

$stream = \Storage::disk('public')->readStream('test_1.zip');
\Storage::disk('local')->writeStream('test_2.zip', $stream);

※ 実際の運用ではS3SFTPなどを使うことになるでしょう。

Notificationの言語を指定できるようになった

サンプルとして、日本語環境でフランス語のメッセージを送信してみましょう。config/app.phplocalejaに設定しておいてください。

翻訳データをつくる

まず、resources/lang内に翻訳データを作成します。

では、以下2つのフォルダにそれぞれの翻訳データを作成しましょう。

(resources/lang/ja)

<?php

return [
    'subject' => '注文を受け付けました',
    'details' => '注文内容:'
];

(resources/lang/fr)

<?php

return [
    'subject' => 'Nous avons accepté votre commande',
    'details' => 'Détails de la commande: '
];

ファイル構造はこのようになります。

Notificationを実装する

Orderedという名前のNotificationを作成します。

php artisan make:notification Ordered

コマンドを実行したら、app/Notifications/Order.phpを開いてtoMail()の中身を以下のように変更してください。

public function toMail($notifiable)
{
    return (new MailMessage)
        ->subject(trans('ordered.subject')) // ここがさっきの翻訳データ
        ->line(trans('ordered.details'));// ここがさっきの翻訳データ
}

Notificationを実行する

では、まずは通常どおりメッセージを送信してみましょう。

<?php

namespace App\Http\Controllers;

use App\Notifications\Ordered;

class HomeController extends Controller
{
    public function notification_localization() {

        $user = \App\User::first();
        $user->notify(new Ordered()); // 日本語メッセージ

    }

すると、以下のようなメッセージが送信されました。

※ ちなみにHello!などの部分は、Notificationの方で日本語化する必要があります。詳しくは、【Laravel5.6】インストール直後にやること3点をご覧ください。

では、次にフランス語を指定してメッセージ送信してみましょう。

$user->notify(
    (new Ordered())->locale('fr')
);

うまくフランス語に切り替わってメッセージ送信することができました。
この方法を使えば動的にNotificationの言語を切り替えることができますね。

(2018年10月9日追記)

さらに、Laravel 5.7.7からはUserモデル内でデフォルトの言語を指定できるようになりました。

この機能を利用するには、まずusersテーブルに言語情報を登録するlocaleフィールドを追加してマイグレーションします。

public function up()
{
    Schema::create('users', function (Blueprint $table) {
        $table->increments('id');
        $table->string('name');
        $table->string('email')->unique();
        $table->timestamp('email_verified_at')->nullable();
        $table->string('password');
        $table->string('locale');
        $table->rememberToken();
        $table->timestamps();
    });
}

テーブル構成は以下のようになります。

そして、app/User.phpHasLocalePreferenceを追加します。

use Illuminate\Contracts\Translation\HasLocalePreference;
class User extends Authenticatable implements MustVerifyEmail, HasLocalePreference
{
    use Notifiable;

    public function preferredLocale()
    {
        return $this->locale; // localeフィールドの言語
    }

    // 省略

}

以上で設定は終わりです。

後は、言語をわざわざ指定しなくてもusersテーブルから自動的に対象となる言語に切り替えられて通知(メール送信)されることになります。

$user->notify(new Ordered()); // ユーザーごとの言語で送信

※適用されるのは以下の赤枠のデータです。

もちろん以下のように言語を指定した場合はそちらが優先されます。

$user->notify(
    (new Ordered())->locale('en') // 英語で送信される
);

おわりに

今回追加された新しい機能の中で地味に効果を発揮しそうなのが、「エラーメッセージの改善」ですね。

ひとつのエラーだけならそれほど時間の短縮にはならないかもしれませんが、これが何十、何百と重ねていくとショートカットできる時間も積もり積もって山となりそうだからです。

あとは、やっぱりDump-Serverですかね。

変数内の状況を全て確認できるので、「どのデータのときにエラーが発生しているか」をチェックしやすくなるのではないでしょうか。

さすがLaravel。
これからもすばらしい作品を楽しみにしています!

ではでは〜。