LoginSignup
2
4

More than 5 years have passed since last update.

Laravel 4.2から5.0へのアップグレード

Last updated at Posted at 2016-09-30

概要

L4.2で稼働しているシステムを5.0にアップグレードした際のトラブル事例集。
基本的には Upgrading To 5.0 From 4.2 に従えば良いのだけど、メジャーアップデートだけあって細々いくつか問題が起きた。

移行対象プロジェクト

発生した問題

Case 1: Class 'Predis\Client' not found

詳細

セッションドライバをRedisに変更したらエラーが起きた。

.env
-) SESSION_DRIVER=file
+) SESSION_DRIVER=redis

解決策

predis/predis パッケージをインストール。

composer require predis/predis

Case 2: ReflectionException in Container.php

詳細

IndexController が見つからない。

ReflectionException in Container.php line 779:
Class App\Http\Controllers\App\Controllers\IndexController does not exist

解決策

エラーメッセージをよくよく見ると、名前空間が怪しい。
L5ではコントローラのデフォルト名前空間が RouteServiceProvider に定義されている。

app/Providers/RouteServiceProvider.php
protected $namespace = 'App\Http\Controllers';

L4の時はPSR-4に従いコントローラに名前空間を指定していたので、routes.php は次のような書き方をしていた。

Route::get('/', array('uses' => 'App\ontrollers\IndexController@getIndex', 'as' => 'home'));

デフォルトの名前空間に uses で指定された名前空間が結合されてしまってるのが原因だった。
ルーティングパスから名前空間を取り除くことで解決。

Route::get('/', array('uses' => 'IndexController@getIndex', 'as' => 'home'));

Case 3: Trait 'App\Models\SoftDeletingTrait' not found

詳細

EloquentのSoft Deleteを利用してる場合、SoftDeletingTrait が無いと言われる。

解決策

SoftDeleteの名前空間とトレイトの変更が必要。

App/Models/BaseModel.php
-) use Illuminate\Database\Eloquent\SoftDeletingTrait;
+) use Illuminate\Database\Eloquent\SoftDeletes;
class BaseModel extends \Eloquent {
-) use SoftDeletingTrait
+) use SoftDeletes;

Case 4: Redisの接続に失敗する

接続設定は正しいはずなのに繋がらない。

ConnectionException in AbstractConnection.php line 155:
`SELECT` failed: ERR invalid DB index [tcp://172.16.238.7:6379]

以下設定。

config/database.php
    'redis' => [

        'cluster' => false,

        'default' => [
            'host'     => env('REDIS_HOST', '127.0.0.1'),
            'port'     => env('REDIS_PORT', 6379),
            'database' => env('REDIS_DATABASE', 0),
        ],

    ],
.env
REDIS_HOST=172.16.238.7
REDIS_PORT=6379
REDIS_DATABASE=0

解決策

env('REDIS_DATABASE') が空文字を返してしまう。0番を使いたい場合は文字列形式で指定が必要。

.env
REDIS_DATABASE="0"

Case 5: カスタムバリデータの移行

詳細

L5が提供するサービスプロバイダ形式に変更する。

解決策

app\Validators の下にカスタムバリデータを作成。

app/Validators/NotExistsValidator.php
<?php
namespace App\Validators;

use DB;

class NotExistsValidator extends \Illuminate\Validation\Validator
{
    /**
     * @param $attribute
     * @param $value
     * @param $parameters
     * @return int
     */
    public function validateNotExists($attribute, $value, $parameters)
    {
        $count = DB::table($parameters[0])
            ->where($parameters[1], '=', $value)
            ->whereNull('delete_date')
            ->count();
        if ($count) {
            return false;
        }
        return true;
    }
}

バリデータをプロバイダに登録。

app\Providers\ValidatorServiceProvider.php
<?php
namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class ValidatorServiceProvider extends ServiceProvider
{
    /**
     * @return void
     */
    public function boot()
    {
        \Validator::resolver(function($translator, $data, $rules, $messages) {
            return new \App\Validators\NotExistsValidator($translator, $data, $rules, $messages);
        });
    }
    /**
     * @return void
     */
    public function register()
    {}
}

プロバイダを読み込むよう設定。

config/app.php
'providers' => [
    'App\Providers\ValidatorServiceProvider',
]

Case 6: Class '\App\User' not found

詳細

ユーザ認証時にL5のAuthエラーが起こる。

解決策

モデルの設置場所を App/Models に変更していたので、認証モデルのパスも変更が必要。

config/auth.php
-) 'model' => 'App\User',
+) 'model' => 'App\Models\User',

Case 7: class 'Illuminate\Database\Eloquent\Collection' does not have a method 'getTotal'

詳細

Bladeのページャでエラー。

call_user_func_array() expects parameter 1 to be a valid callback,
class 'Illuminate\Database\Eloquent\Collection' does not have a method 'getTotal'
(View: /data/resources/views/summary/daily/index.blade.php)

解決策

メソッドの呼び出し方が変わってた。

-) @if ($activities->getTotal())
+) @if ($activities->total())

Case 8: Route::resouce()でミドルウェアを利用したい

詳細

routes.yml 上で特定のコントローラにミドルウェアを適用したい。

解決策

Route::group() が使える。

Route::group(['middleware' => 'auth'], function() {
    Route::resource('dashboard', 'DashboardController');
});

Case 9: AJAX通信時にTokenMismatchExceptionが発生

詳細

AJAXでPOSTやDELETEメソッドを実行するとCSRFチェックでエラーが起こる。

production.ERROR: exception 'Illuminate\Session\TokenMismatchException'
in /data/src/vendor/laravel/framework/src/Illuminate/Foundation/Http/Middleware/VerifyCsrfToken.php:46

解決策

L5からはCSRFチェックがすべてのページで有効になったので、AJAX通信時も _token パラメータを送信する必要がある。
すべての通信で _token を追加するのは面倒なので (jQuery限定の対策ではあるが)、<meta> タグにトークンを記述し、AJAX通信時はjQuery経由でMETAからトークンを取得及びサーバへ送信する手段を用いた。

<meta name="csrf-token" content="{{ csrf_token() }}">
$.ajaxSetup({
    headers: {
        'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
    }
});

Case 10: PHPUnitでPOST送信時に "Failed asserting that Symfony\Component\HttpFoundation\Response Object" エラー

詳細

レスポンスオブジェクトをデバッグしてみると、CSRFエラーが起きていることがわかる。

Failed asserting that Symfony\Component\HttpFoundation\Response Object (...)
is an instance of class "Illuminate\Http\RedirectResponse".

解決策

リクエストパラメータにCSRFトークンを追加してあげれば良い。

$params = [
    'contact_name' => 'test',
    'email' => 'test@monelytics.me',
    'contact_type' => '1',
    'contact_message' => 'test',
    '_token' => csrf_token()
];

$this->call('POST', '/contact/send', $params);
$this->assertRedirectedTo('/contact/done');

csrf_token() を利用するに辺り、セッションを開始しておく必要がある。

tests/TestCase.php
public function setup()
{
    parent::setup();

    \Session::start();
}

※L5.2ではテスト時にトークンチェックは無視されるようになりました。

Case 11: PHPUnitでPOST送信時に "InvalidArgumentException: An uploaded file must be an array or an instance of UploadedFile." エラー

詳細

問題のコード。

$this->call(
    'DELETE',
    '/cost/constant/1',
    [],
    [],
    ['HTTP_REFERER' => $http_referer]
);

解決策

call() メソッドの引数4番目に $cookies が追加されてた。

L4.2
Response call(
    string $method,
    string $uri,
    array $parameters = array(),
    array $files = array(),
    array $server = array(),
    string $content = null,
    bool $changeHistory = true
)
L5.0
Response call(
    string $method,
    string $uri,
    array $parameters = array(),
    array $cookies = array(),
    array $files = array(),
    array $server = array(),
    string $content = null
)

修正後のコード。

$this->call(
    'DELETE',
    '/cost/constant/1',
    [],
    [],
    [],
    ['HTTP_REFERER' => $http_referer]
);
2
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
4