PHP
laravel
laravel5.1
laravel5.5

Laravel5.1から5.5へアップグレード

タイトルの通り、Laravelのバージョンを5.1から5.5へアップグレードしたので、その時のメモです。
ちなみにですが、5.1から5.5へのアップグレードなんてやるべきではないです。新バージョンが出るたびに細かくアップグレードしていくのがベストです。
小規模なサービスですら対応範囲は多いので、大規模サービスとなるとかなりの工数がかかると思います。

とりあえずバージョンを上げてみる

5.1から5.5となると変更点はかなりありますが、そこまで大きいプロジェクトでもなかったのでとりあえず5.5にバージョンアップしてみました。
Laravelのバージョンアップは、基本的には composer.json を書き換えて composer update を実行します。

composer.json書き換え

まずはcomposer.jsonの書き換えとなります。ほぼ全部書き換えです。
とりあえずアップグレード と言ってますが、5.5からはPHPの対応バージョンが7.0以上となっているので、サーバのPHPバージョンが5系のままだと、そもそもアップグレードができません。
今回、PHPのバージョンは7以上だったので、強行突破してます。

composer.json
diff --git a/composer.json b/composer.json
index cc83e39..14321dd 100644
--- a/composer.json
+++ b/composer.json
@@ -5,15 +5,22 @@
     "license": "MIT",
     "type": "project",
     "require": {
-        "php": ">=5.5.9",
-        "laravel/framework": "5.1.*",
-        "doctrine/dbal": "^2.5"
+        "php": ">=7.0.0",
+        "fideloper/proxy": "~3.3",
+        "laravel/framework": "5.5.*",
+        "laravel/tinker": "~1.0"
     },
     "require-dev": {
+        "filp/whoops": "~2.0",
         "fzaninotto/faker": "~1.4",
         "mockery/mockery": "0.9.*",
-        "phpunit/phpunit": "~4.0",
-        "phpspec/phpspec": "~2.1"
+        "phpunit/phpunit": "~6.0"
+    },
+    "extra": {
+        "laravel": {
+            "dont-discover": [
+            ]
+        }
     },
     "autoload": {
         "classmap": [
@@ -31,18 +38,14 @@
     },
     "scripts": {
         "post-root-package-install": [
-            "php -r \"copy('.env.example', '.env');\""
+            "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
         ],
         "post-create-project-cmd": [
-            "php artisan key:generate"
-        ],
-        "post-install-cmd": [
-            "Illuminate\\Foundation\\ComposerScripts::postInstall",
-            "php artisan optimize"
+            "@php artisan key:generate"
         ],
-        "post-update-cmd": [
-            "Illuminate\\Foundation\\ComposerScripts::postUpdate",
-            "php artisan optimize"
+        "post-autoload-dump": [
+            "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
+            "@php artisan package:discover"
         ]
     },
     "config": {

composer update

composer.json を書き換えたら、composer update を実行します。

$ composer update
Loading composer repositories with package information
Updating dependencies (including require-dev)
...
...
...
Writing lock file
Generating autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover

In ProviderRepository.php line 208:

  Class 'Illuminate\Routing\ControllerServiceProvider' not found


Script @php artisan package:discover handling the post-autoload-dump event returned with error code 1

Class 'Illuminate\Routing\ControllerServiceProvider' not found とエラーが出ました。
これは、5.2へのアップグレード時に対応する内容です。

config/app.php の中の Illuminate\Routing\ControllerServiceProvider::class, を削除して、もう一度 composer update 実行。

$ composer update
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Generating autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover

In EventServiceProvider.php line 8:

  Declaration of App\Providers\EventServiceProvider::boot(Illuminate\Contract
  s\Events\Dispatcher $events) should be compatible with Illuminate\Foundatio
  n\Support\Providers\EventServiceProvider::boot()


Script @php artisan package:discover handling the post-autoload-dump event returned with error code 1

またエラーが出ました。
Declaration of App\Providers\EventServiceProvider::boot(Illuminate\Contracts\Events\Dispatcher $events) should be compatible with Illuminate\Foundation\Support\Providers\EventServiceProvider::boot()
これは、5.3へアップグレードするときの対応内容です。

app/Providers/ 以下の EventServiceProvider.php および RouteServiceProvider.phpboot()メソッドの引数を削除します。

@@ -24,9 +24,9 @@ class EventServiceProvider extends ServiceProvider
      * @param  \Illuminate\Contracts\Events\Dispatcher  $events
      * @return void
      */
-    public function boot(DispatcherContract $events)
+    public function boot()
     {
-        parent::boot($events);
+        parent::boot();

         //
     }
@@ -22,11 +22,11 @@ class RouteServiceProvider extends ServiceProvider
      * @param  \Illuminate\Routing\Router  $router
      * @return void
      */
-    public function boot(Router $router)
+    public function boot()
     {
         //

-        parent::boot($router);
+        parent::boot();
     }

     /**

再度 composer update 実行。

$ composer update
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Generating autoload files
> Illuminate\Foundation\ComposerScripts::postAutoloadDump
> @php artisan package:discover
Discovered Package: fideloper/proxy
Discovered Package: laravel/tinker
Package manifest generated successfully.

成功したようなので、バージョンを確認します。

$ php artisan --version
Laravel Framework 5.5.23

とりあえず動かしてみる

Laravel自体のアップグレードはできたので、プロジェクトのほうを修正していきます。
Laravelの公式ドキュメントは、各バージョンへのアップグレード時に何をする必要があるのかを書いてくれているので、今回のように一気にアップグレードではなく、その都度アップグレードしている場合にはこれに沿って対応してくのが基本です。

今回は、とりあえず動かしてみて、エラーと戦ってきたいと思います。

セッション

Column not found: 1054 Unknown column 'user_id' in 'field list'
セッションをDBに保持するようにしている場合、上記のようなエラーが出ます。
これは5.2へのアップグレード時に対応する内容で、ドキュメントには次のように書かれています。

ユーザID、IPアドレス、ユーザエージェントのような情報をより含む、新しいdatabaseセッションドライバが書かれました。古いドライバーを使い続けたい場合は、session.php設定ファイルへlegacy-databaseドライバを指定してください。
新しいドライバーを使用する場合、セッションのデータベーステーブルへ、user_id (NULL値を許す整数)、ip_address (NULL値を許す整数)、user_agent (テキスト)カラムを追加してください。

ということで、カラムを追加します。

$ php artisan make:migration alter_add_session

/**
 * Run the migrations.
 *
 * @return void
 */
public function up()
{
    Schema::table('sessions', function($t){
        $t->integer('user_id')->nullable();
        $t->integer('ip_address')->nullable();
        $t->string('user_agent');
    });
}

/**
 * Reverse the migrations.
 *
 * @return void
 */
public function down()
{
    Schema::table('sessions', function($t){
        $t->dropColumn(['user_id', 'ip_address', 'user_agent']);
    });
}

$ php artisan migrate
**************************************
*     Application In Production!     *
**************************************

 Do you really wish to run this command? (yes/no) [no]:
 >

アプリケーションが本番だけど、大丈夫?と聞かれてしまいました。.env では APP_ENV=local となっているのに、なぜ?
これも5.2で対応内容ですが、config/app.phpenv のデフォルトオプションが追加されていました。

app.php
diff --git a/config/app.php b/config/app.php
index f013735..3f597f9 100644
--- a/config/app.php
+++ b/config/app.php
@@ -2,6 +2,8 @@

 return [

+    'env' => env('APP_ENV', 'production'),

これで local で確認無しでマイグレーションが通ります。

ルートフィルター

Method [beforeFilter] does not exist on [App\Http\Controllers\***
これは5.1の時点で非推奨、5.2で削除となったルートフィルターの使用によるエラーです。

より好ましいミドルウェアにより、ルートフィルターは非推奨となりました。

とあるように、ミドルウェアへの移行が必要です。まあ、5.1のうちに移行しておくべきだった内容ですが。

ミドルウェアの作成

今回の場合、以下のようにController内にフィルターを書いていました。

HogeController.php
class HogeController extends Controller {

    public function __construct()
    {
        parent::__construct();

        $this->beforeFilter("@filterHoge");
    }

    public function filterHoge()
    {
        // 処理
    }

これを、ミドルウェアに移行します。

$ php artisan make:middleware HogeMiddleware
HogeMiddleware.php
class HogeMiddleware
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        // 処理

        return $next($request);
    }

ヘルパ

  • url()

url()ヘルパ関数が、引数のパスの指定がない場合にIlluminate\Routing\UrlGeneratorを返すようになりました。

例えばプロジェクトのルートパスが https://hogehoge.com だった場合、5.1では

$u = url();

echo $u;

// https://hogehoge.com

でしたが、5.2以降で同じような結果を得るためには

$u = url('/');

echo $u;

// https://hogehoge.com

とする必要があります。
url() のまま放置して、その値に文字列結合をしている場合、Object of class Illuminate\Routing\UrlGenerator could not be converted to string と言われます。

Laravelの恩恵を受けきれていない?

ざっと動かした感じでは致命的なものはこれくらいでした。
おそらく、フレームワークに依存してないのが良かったと思われます。
逆に言うと、Laravelの恩恵を受けきれていないのかもしれません。

何かあれば追記していきます。