Laravel 4.2で書かれたアプリをLaravel 5.8にする
Laravelは4と5でアーキテクチャが大きく変更された
既存プロジェクトの移行の際につまづきそうな点をまとめる
(長大な文章だが分割していないのは一括で検索できるのが便利なため)
基本的な考え方
基本的にUpgrade Guideをなぞるだけ
Upgrade Guide - Laravel - The PHP Framework For Web Artisans
アップグレードガイド 5.0 Laravel
基礎知識
Laravelのバージョン
2019年3月時点でLaravelの最新版は5.8
Laravelのバージョニングの意味
アップグレードの方針
もし4系の最新4.2のプロジェクトを5.7にあげたいなら
公式のおすすめは4.2->5.0にアップグレードした後にUpgrading guideにそってどんどんあげていく方法っぽい
同じ5系でもマイナーバージョンがあがるごとにUpgradeの手順が必要になる
他の手段としては、composerでlaravel自体をあげてしまい、エラーを潰していく方法がある
Laravelのアプリケーションがどのように初期化されるか
Laravel 4のリクエストのライフサイクル
Laravel 5のリクエストのライフサイクル
特にService providersと、それらがどこで初期化されるのか(Laravel 5のリクエストのライフサイクル#5beec18f3f442500009be09a)はフレームワークのコードを読む際には理解必須
Laravel 5 のディレクトリ構成
Directory Structure - Laravel - The PHP Framework For Web Artisans
Laravel imposes almost no restrictions on where any given class is located - as long as Composer can autoload the class.
自由に配置できる
FilterはMiddlewareになった
Filter消えた
4.2の実態はこれ
Illuminate\Routing\Router | Laravel API
5.1でdeprecatedになった(5.2では削除)
Illuminate\Routing\Router | Laravel API
Middleware増えた
Middleware - Laravel - The PHP Framework For Web Artisans
書き方はこうかわる
4.2まではFilterだった
code:php
// ユーザページの表示をするときに認証をかましている部分のルーティング
Route::get('/users/me', [
'before' => [
'auth', // ユーザ情報を返す前に権限チェックしたい
],
'uses' => UserController::class . '@show',
]);
5.7はmiddlewareを使って同じことができる
code:php
Route::get('/users/me', [
'uses' => UserController::class . '@show',
])->middleware('auth');
Q. 以前はbeforeと指定していたのに、これじゃあbeforeなのかafterなのかわからないのでは?
A. Yes. Routeファサードを見るだけではわからなくなった
Before & After MiddlewareセクションにあるようにMiddlewareのクラス内にその役割が移った
Afterに複数個設定することはできなくなった?(そもそもやらなそう)kadoyau.icon
Controllerの名前空間はデフォルトではApp\Http\Controllers
Controllers - Laravel - The PHP Framework For Web Artisans
Since the RouteServiceProvider loads your route files within a route group that contains the namespace, we only specified the portion of the class name that comes after the App\Http\Controllers portion of the namespace.
RouteServiceProviderでデフォルトのnamespaceが決まっている
問題
Route::get('foo', 'Photos\AdminController@method')のようにルーティングを書くと、デフォルトのコントローラの名前空間が自動的に付与されてApp\Http\Controllers\Photos\AdminControllerを読みに行く
4.2のアプリはディレクトリ構成が違うはずなので死ぬ
対応
RouteServiceProvidersの上記変数$namespaceをつけたい名前空間に書き換えるだけ
何もつけたくないならコメントアウトすればいい
データベース(MySQL)
「DBいじってないのにerror invalid datetime format: 1292 Incorrect datetime value: '0000-00-00 00:00:00'とかでるんだけど/icons/はて?.icon」
答え:date - After upgrade to laravel 5.3 error invalid datetime format: 1292 Incorrect datetime value: '0000-00-00 00:00:00' - Stack Overflow
原因
Laravel 5.2以降デフォルトだとMySQLのsql_modeがstrictモードになっているので↓が実行される
set session sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION'
https://github.com/laravel/framework/blob/af510b80fdf16ea1b6de908d9fdcc98687b9b4bd/src/Illuminate/Database/Connectors/MySqlConnector.php#L183
set sessionはセッション中のみに有効な変数の設定
MySQL :: MySQL 5.6 リファレンスマニュアル :: 13.7.4 SET 構文
対応
1. (根本対応)スキーマ定義をなおす
2. (一時対応)config/database.phpのstrictモードの設定をfalseにする
クエリログはデフォルトではoffになった
最新
2014年10月にコミットされた
Laravel 5でクエリログを取得したい
DBから取り出した値がObjectになる
FetchモードがObject固定になっています
Laravel 4だとconfigで指定できていました
code:database.php
'fetch' => PDO::FETCH_ASSOC
Laravel 5ではIlluminate\Database\Events\StatementPreparedイベントをフックして変更することができます
https://laravel.com/docs/5.4/upgrade#upgrade-5.4.0
素直にobjを使うように書き換えるのがよさそうkadoyau.icon
参考:laravel5.4でDBのFetchModeを配列に切り替える - Qiita
KVS(Redis)
Redis - Laravel - The PHP Framework For Web Artisans(和訳)
predisがない
原因
Laravelではデフォルトドライバがpredis
Redis 5.4 Laravel
対応
composerでアプリケーションに明示的に入れる
Redis::connection()の返り値の型が変更されている
原因
4.2では\Predis\ClientInterfaceを返していた
https://laravel.com/api/4.2/Illuminate/Redis/Database.html#method_connection
5.7ではIlluminate\Redis\Connections\Connectionが返る
Illuminate\Redis\RedisManager | Laravel API
対応
RedisManagerはclientを持っているので単にプロパティを呼び出せばよい
4.2 Redis::connection()
5.7 Redis::connection()->client
キューの取扱がかなり変わった
Laravel 4.2とLaravel 5.7のキューの取扱の違い
設定値
設定 4.2 Laravel
設定 5.7 Laravel
Config::get()で取得できる設定値の設定はさほど変わっていない
4.2 app/config/app.phpのこの設定値にアクセスしたいとき
Config::get('app.timezone')
5.7 config/app.phpのこの設定値にアクセスしたいとき
Config::get('app.name')
$app['config']['hoge']で取れるものはconfig/hoge.phpで返しているということ
ConfigのServiceProviderで初期設定される
環境に左右される箇所はenv()を利用して読み込む
code:php
'connections' => [
'mysql' => [
'password' => env('DB_PYASSWORD', '') // local, development, productionで.envに記載される情報が異なるのでenv()で取得する
]
]
秘密の設定を入れたいときは環境変数を使うが、アクセス方法が変わった
4.2 Laravel独自の.envの仕組みを使っていた
5.7 dotenvを使うようになった
.envに秘匿情報を書く
code:.env
// key=value 形式で秘匿情報を書く
DB_PASSWORD=SUPERSTRONGPASS
.envファイルは何らかの方法で環境ごとにデプロイするkadoyau.icon
ビュー
テンプレートに値を渡したいとき
4.2 Views & Responses - Laravel - The PHP Framework For Web Artisans
code:php
Route::get('/', function()
{
// app/views/greeting.phpに変数を渡す
return View::make('greeting', array('name' => 'Taylor'));
});
テンプレートのパスはapp/config/view.phpに設定を書くと配置を自由に変えられる
5.7 Views - Laravel - The PHP Framework For Web Artisans
code:php
Route::get('/', function () {
// resources/views/greeting.blade.phpに変数を渡す
return view('greeting', 'name' => 'James');
});
テンプレートのパスconfig/view.php
table:viewの違い
Laravelのバージョン 4.2 5.7
デフォルトパス app/views resources/views
デフォルトパスの設定値 app/config/view.php config/view.php
エスケープの挙動
4.2 {{ /* エスケープされない */ }}
5.7は同じ記法で、デフォルトでエスケープ(htmlspecialchars())される
{!! /* 5.7ではこれでエスケープされない */ !!}
ルーティング
4.2->5.0ではroutes.phpは単にディレクトリを移動するだけ
アプリケーションのbootの際にRoutingServiceProviderあたりがここを読むようになっている
5.3以降routes.phpは消えた
routes.phpはどこに消えたのか(5.3) - 新さっぽろ IT キャリア研究室
例外に応じて返すレスポンス
4.2 https://laravel.com/docs/4.2/errors (和訳)
例外に対してどのようなハンドリングをするかをapp/start/global.phpの中で指定していた
code:exceptionHandlerConfig.php
App::error(function(Exception $exception) { Log::error($exception); });
App::error(function(InvalidUserException $exception){ return createResponseForInvalidUserException } // 例外ごとにviewを変更する
App::missing(function(){}) // 404エラーのハンドリング
App::down(function(){})
App::down()はメンテナンスモードのイベントが発動したときに呼ばれる
App::missing()で指定する関数はNotForundHttpExceptionが投げられたときに呼ばれる
App::error のAPIリファレンス
Illuminate\Foundation\Application | Laravel API
5.7にはerrorなどのメソッドは存在しない
Illuminate\Foundation\Application | Laravel API
5.7 https://laravel.com/docs/5.7/errors#the-exception-handler(和訳)
App\Exceptions\Handlerが次の役割を持っている
例外を記録&ログを外部サービスに投げるreport()
例外発生時にユーザへのエラー表示(HTTPレスポンス)render()
Handlerクラスでコントロールできること
特定の例外に対してreport()を呼び出したくない(ログを記録したくない)
dontReport配列に例外のクラス名を指定する
validadationの例外でflashしたくないキーを指定する
kadoyau.icon 多分例外ハンドリング時にflushされるのだろう。入力されたpasswordをviewから消したくないとかにつかいそう
dontFlash配列に例外タイプを指定する
Laravel 4の記法との比較
例外によってブラウザにメッセージを出し分けたいとき
code:php
// App::error(function(InvalidUserException $exception){ return createResponseForInvalidUserException } の代わり
public function render($request, Exception $exception)
{
if ($exception instanceof CustomException) {
return response()->view('errors.custom', [], 500);
}
return parent::render($request, $exception);
}
// App::missing()の代わり
if ($exception instanceof NotFoundHttpException) {
retuern createResponse(); // HTTPレスポンスは自由に作る
}
APP_NAMEの影響箇所
LaravelのAPP_NAMEは何に利用されるのか
session cookieの名前が変更になる
4.2 laravel_session
5.8 APP_NAME_session
cacheのprefix
4.2 laravel
5.8 APP_NAME_cache
テスト
テスト環境であることをフレームワークに設定する方法
4.2
https://github.com/laravel/laravel/blob/v4.2.11/app/tests/TestCase.php#L12-L14 でフラグを立てると
https://github.com/laravel/framework/blob/7d9e7068c49f945385673014d4cba4de28accd5e/src/Illuminate/Foundation/start.php#L75-L78 で設定される
5.7
phpunit.xmlで設定する
ドキュメントに書いてある:Testing: Getting Started - Laravel - The PHP Framework For Web Artisans
新規でアプリを作るとデフォルトではこんな感じ
Laravel Tinker
5.4で変更があったのでrequireしておく必要がある。 Upgrade Guideのとおりに導入すると動く
ただし、このあとにphp artisan config:cacheをする必要がある
laravelディレクトリ以下にcomposerのvendorがあることが前提になっている(Laravelの標準的な構成)
なぜハードコードなのか聞いている人がいるが、標準的な場所だからとしてcloseされている
Vendor path is hardcoded · Issue #65 · laravel/tinker
未整理
アプリケーションの初期化スクリプトが抽象化された
全体的にベタ書きの印象があるlaravel4 の初期化スクリプトは、laravel5になって大幅に整理された
アプリケーションのカーネルがリクエストを受け取ってレスポンスを返す形に抽象化された
ユーザは気に食わなければカーネルを差し替えることができる
リクエストをハンドルする際に、Service Providersなどで初期化が行われる
詳しくはLaravel 5のリクエストのライフサイクルにまとめた
なのでここで色々独自な処理をしていると全てうまくいかなくなった
特に重要なものはフレームワークのApplication基底クラスで初期化されるので、そこに手を入れたいという要件ではFWのコードをforkすることになる
Laravelにのっかることを検討してください
forkしたくないよね?未来にも同じ苦しみが待ってる。今なら間に合う
フレームワークのコードは5のほうがスッキリ抽象化されていて綺麗kadoyau.icon
変更点
bootstrap/autoload.php -> public/index.php
bootstrap/start.php -> bootstrap/app.php
Laravel 4のリクエストのライフサイクル#5bebf9253f44250000ba6dc0
5系のマイナーアップデートで影響が大きそうなもの
アップグレードマニュアルから影響が大きそうなものを抜粋する
ただし、実際に作業する際にはマニュアル全部眺めるべき
特に、アップグレードマニュアルの「非推奨」はハマるので全部読むこと
4.2から5.7にあげようとしている場合、5.1で非推奨、5.2で消えたみたいなものがあるとハマる
https://laravel.com/docs/5.8/upgrade
Laravel 5.8
https://readouble.com/laravel/5.7/ja/upgrade.html
アップグレード時間の見積もり:10分から15分
cacheのディレクトリがstorage/framework/cacheになったのでディレクトリを作成する必要がある
Route::redirectは302を返すようになった
https://readouble.com/laravel/5.6/ja/upgrade.html
見積もり時間:10〜30分
Cookieをシリアライズ化しなくなった
Symfony 4系
PHPUnit 7系
ログの設定はconfig/logging.phpになった
5.5のconfig/app.phpに記述されていたこの設定は削除された
https://github.com/laravel/laravel/blob/3a50f43c3be644f6168bf888af272425efb8f65c/config/app.php#L111-L126
Illuminate\Log\Writerクラスは、Illuminate\Log\Loggerに名前が変更
Log::getMonolog()は廃止
Laravel5.6 での ログ設定について - Qiita
https://readouble.com/laravel/5.5/ja/upgrade.html
アップデートの見積もり時間:1時間
セキュリティ
クッキーのシリアライズが無効に
Artisanコマンド
fire() -> handle()へリネーム
https://laravel.com/docs/5.4/upgrade (和訳)
アップグレード見積もり時間 1〜2時間
bootstrap/cache/compiled.phpの廃止
コレクション
collectionのメソッドがrenameされた
コンテナ
bind()の第一引数にエイリアスが登録できなくなった
make()の第二引数に配列を受け取らなくなった
テスト
laravel5.4テストレイヤを使用して書かれた新しいテストは、TestCaseクラスを拡張します
テスト名前空間を作れ
読み込んだ.envファイルのAPP_ENV変数を使用します。
キュー
failed_jobsテーブルを使ったらカラムを追加する必要がある。やらないとジョブが失敗したときにログの挿入ができなくなる(fatalになる)
Laravel 5のQueueが失敗したときのログをインサートするテーブル
https://readouble.com/laravel/5.3/ja/upgrade.html
アップデート見積もり時間:2時間から3時間
キュー
configのttrがretry_afterに
In your queue configuration, all expire configuration items should be renamed to retry_after. Likewise, the Beanstalk configuration's ttr item should be renamed to retry_after. This name change provides more clarity on the purpose of this configuration option.
queue:workの際に--daemonが不要に
ジョブがデータの情報を持たなくなった
$event->job->payload()で取得する必要がある
Queue::failing
キュー
タイムアウトの設定をしないと、実行時間のかかるjobがある場合に(デフォルトのタイムアウト60秒にひっかかって)必ず失敗する
https://github.com/laravel/framework/blob/5.3/src/Illuminate/Queue/Console/WorkCommand.php
データベース
クエリビルダがArrayではなくIlluminate\Support\Collectionをかえすようになった
次のようなコードで突然死ぬ
code:php
// いままでArrayが帰っていたのにCollectionが帰ってくるようになるメソッド
public function fetchUserData() {
$rows = DB::table('table_name')->where($user_id)->limit(5);
return array_map(function($row){
return $this->dataProsessing($row);
}, $rows);
}
対応
mapでarrayにキャストするとかする
中身はobject (PHP)なのでキャストしないといけない
https://readouble.com/laravel/5.2/ja/upgrade.html
アップデートにかかる時間の見積もり:1時間以下
認証
config/auth.phpが新しくなった
Auth::extendを使っていたらAuth::providerに書き換えた上でapp.phpのprovidersに追記する
環境変数
app.phpのenvにデフォルト値が追加された'env' => env('APP_ENV', 'production'),
サービスプロバイダーの廃止
app.phpから次のプロバイダを消す
Illuminate\Foundation\Providers\ArtisanServiceProvider
Illuminate\Routing\ControllerServiceProvider
App\Providers\BusServiceProvider(5.0.16)
App\Providers\ConfigServiceProvider(5.0.16)
bootstrap/autoload.phpの変更
$compiledPath = __DIR__.'/../vendor/compiled.php';(5.0.16)
非推奨
Illuminate\Contracts\Routing\Middlewareが非推奨に
データベース
MySQLのデフォルトモードの変更
MySQL5.7以降では、strictモードがデフォルトで有効になったため、0000-00-00 00:00:00が有効な日付として取り扱われなくなりました。
lists()メソッドがpluck()にrenameされた
4.2 https://laravel.com/api/4.2/Illuminate/Database/Query/Builder.html#method_lists
5.7には存在しない。代わりにpluckを使う
Laravel 5.2でdeprecatedになった
The lists method on the Collection, query builder and Eloquent query builder objects has been renamed to pluck. The method signature remains the same.
https://readouble.com/laravel/5.1/ja/upgrade.html
アップデートにかかる時間の見積もり:1時間以下
認可とポリシーの追加(オプショナル)
app/Commandsディレクトリーはapp/Jobsへ名前が変更されました。しかし、コマンドを全部新しい場所へ移動する必要はありませんし、make:commandとhandler:command Artisanコマンドも続けて使用できます。
同様に、app/Handlersディレクトリーはapp/Listenersに名前が変更され、イベントリスナーだけが含まれるようになりました。しかし、既に存在するコマンドとイベントハンドラーを移動したり、名前を変えたりする必要はありません。handler:eventコマンドもイベントハンドラーを生成するために続けて使用できます。
テスト
tests/TestCase.phpファイルにprotectedの$baseUrlプロパティーを追加してください。
PHPのmcrypt extensionは不要になり、openssl extensionが利用されるようになった
4.2 -> 5.0
Artisan コマンド
5.2でmake:consoleからmake:commandになった
Move all of your command classes from your old app/commands directory to the new app/Console/Commands directory. Next, add the app/Console/Commands directory to the classmap directive of your composer.json file
5.7で新規にアプリを作成してもclassmapにapp/Console/Commandは指定されていない
Laravel 5.5からCommandもAutoloadされるようになったため
In Laravel 5.5, Artisan can automatically discover commands so that you do not have to manually register them in your kernel. To take advantage of this new feature, you should add the following line to the commands method of your App\Console\Kernel class:
$this->load(__DIR__.'/Commands');
Upgrade Guide - Laravel - The PHP Framework For Web Artisans
Then, copy your list of Artisan commands from start/artisan.php into the commands array of the app/Console/Kernel.php file.
Upgrade Guide - Laravel - The PHP Framework For Web Artisans
5.7でphp artisan make:commandでコマンドを作成してもConsole/Kernel.phpの配列には追加されない
環境変数の移行
#Laravel