- 投稿日:2019-04-15T22:25:48+09:00
laradockでLaravelの環境構築をしてみる(Mac)
ローカル環境はLaradockを利用して開発します。
最終的には以下のような構成になります。/(任意の名前) |-- laradock |-- laravel1.Dockerをインストールする
2.Laradockの入手
$ mkdir 任意のディレクトリ $ cd 任意のディレクトリ $ git clone https://github.com/LaraDock/laradock.git $ cd laradock3.laradockの設定ファイルの編集
最初にenv.exampleファイルをコピーして.envファイルを作成します。この.envファイルに環境構築時の設定情報を書き込んでいく。
$ cp env-example .envDATAの保存する場所を変更する。標準の設定だとルートディレクトリの配下にファイルが永続化されている。
任意のディレクトリ配下のlaradockディレクトリにファイルを保存するように変更します。# .env # Choose storage path on your machine. For all storage systems - DATA_PATH_HOST=~/.laradock/data + DATA_PATH_HOST=.laradock/data続いて、MYSQLの設定をしていく。
# .env ### MYSQL ################################################# MYSQL_VERSION=5.7 # versionを固定 MYSQL_DATABASE=homestead1 #データベース名を変更 MYSQL_USER=homestead1 #ユーザー名を変更 MYSQL_PASSWORD=hogehoge #パスワードを変更 MYSQL_PORT=3306 MYSQL_ROOT_PASSWORD=root MYSQL_ENTRYPOINT_INITDB=./mysql/docker-entrypoint-initdb.d4.Dockerコンテナを起動する。
$ docker-compose up -d nginx mysql phpmyadmin5.composerをインストールする
laradockのディレクトリ内に移動して、workspaceのbashにログインする
$ docker-compose exec --user=laradock workspace bash $ cd training-laravel $ composer install6 laravelの.envの編集
laravelの.envに以下を貼り付ける
APP_NAME=Laravel APP_ENV=local APP_KEY= APP_DEBUG=true APP_URL=http://localhost LOG_CHANNEL=stack DB_CONNECTION=mysql DB_HOST=mysql DB_PORT=3306 DB_DATABASE=homestead1 DB_USERNAME=homestead1 DB_PASSWORD=hogehoge BROADCAST_DRIVER=log CACHE_DRIVER=file QUEUE_CONNECTION=sync SESSION_DRIVER=file SESSION_LIFETIME=120 REDIS_HOST=127.0.0.1 REDIS_PASSWORD=null REDIS_PORT=6379 MAIL_DRIVER=smtp MAIL_HOST=smtp.mailtrap.io MAIL_PORT=2525 MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=null AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= AWS_DEFAULT_REGION=us-east-1 AWS_BUCKET= PUSHER_APP_ID= PUSHER_APP_KEY= PUSHER_APP_SECRET= PUSHER_APP_CLUSTER=mt1 MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"7.APP_KEYの生成と初回のマイグレーションを実行
$ docker-compose exec workspace php artisan migrate $ docker-compose exec workspace php artisan key:generate
- 投稿日:2019-04-15T20:02:41+09:00
Laravelでバリデーションにモックを仕掛ける
バリデーションにモックを仕掛ける
ファサードには下記のようにshouldReceiveメソッドが実装されていて楽にモックを仕掛けることができます。
私のケースではexistsでテーブルに値があるか確認していて、apiのテスト時にDBアクセスを避けたかったので利用しました。shouldReceiveメソッドを使用し、Cacheファサードへの呼び出しをモックできます。これはMockeryインスタンスを返します。ファサードはLaravelのサービスコンテナにより管理され、依存解決されていますので、典型的な静的クラスよりもかなり高いテスタビリティーを持っています。
makeメソッドだけではなく正常系チェック用のpassesメソッドも範囲にして、返り値を設定することです。
existsを使って無い時でも別途バリデーションのテストをしている等であれば使ってもよさそう。ExampleTest.phpValidator::shouldReceive('make->passes') ->once() ->andReturn(true); $response = $this->json('GET', /example, $param);(参考)
- 投稿日:2019-04-15T20:02:41+09:00
Laravel/Lumenでバリデーションにモックを仕掛ける
バリデーションにモックを仕掛ける
ファサードには下記のようにshouldReceiveメソッドが実装されていて楽にモックを仕掛けることができます。
今回、existsルールを使ってテーブルに値があるか確認していて、apiのテスト時にDBアクセスを避けたかったので利用しました。shouldReceiveメソッドを使用し、Cacheファサードへの呼び出しをモックできます。これはMockeryインスタンスを返します。ファサードはLaravelのサービスコンテナにより管理され、依存解決されていますので、典型的な静的クラスよりもかなり高いテスタビリティーを持っています。
https://readouble.com/laravel/5.5/ja/mocking.html
makeメソッドだけではなく正常系チェック用のpassesメソッドも対象にして、返り値を設定することが重要です。
今回みたいなexistsルールを使って無い時に別途バリデーションのテストをしている等であれば、使うこともありそうですね。ExampleTest.phpValidator::shouldReceive('make->passes') ->once() ->andReturn(true); $response = $this->json('GET', /example, $param);
- 投稿日:2019-04-15T20:02:41+09:00
Laravel/LumenでValidatorにモックを仕掛ける
バリデーションにモックを仕掛ける
Laravel/LumenのValidatorクラスにはファサードが用意されています。ファサードには下記の説明にあるように最初からモックできるヘルパがあり、楽にモック化することができます。しかしValidatorに仕掛けるケースの記事がなかったので備忘録として。
Laravelにはイベント、ジョブ、ファサードを最初からモックできるヘルパが準備されています。これらのヘルパは主にMockery上で動作する便利なレイヤーを提供しているので、複雑なMockeryのメソッドコールを自分で作成する必要はありません。
https://readouble.com/laravel/5.5/ja/mocking.html
実装
重要なのはバリデーションのmakeメソッドだけではなく、正常系確認用にあるpassesメソッドもつないで、返り値を設定することでバリデーションが実行される心配がなくなります。
ExampleTest.phpValidator::shouldReceive('make->passes') ->once() ->andReturn(true); $response = $this->json('GET', /example, $param);まとめ
今回はバリデーションでexistsルールを使ってテーブルに値があるか確認していて、APIのテスト時にはDBアクセスを避けたかったのでモックを利用してますが、他にも複雑なカスタムバリデーションなどを組んでいる時などにも良いと考えてます。(もちろん別途テストは必要)
- 投稿日:2019-04-15T13:06:53+09:00
【PHP】CarbonのaddMonth()を使ってハマった話(CarbonとDateTimeクラスの仕様を今一度確認してみる)
TL;DR
特に理由がなければCarbonを用いて月の計算をする場合は
addMonthsNoOverflow()
メソッドを使うようにしたほうがいいDateTimeクラスの仕様に度々ひっかかる
Carbonを用いて月の計算を行っていると、どうやら28~31日のデータを扱うときに挙動がおかしいことに気が付きました
前もこんなのあったなーと思いつつ再現させる
(前:【PHP】ある月の初日と末日を取得する方法(DateTimeクラス & Carbon編)#要注意引数は年月日を渡しましょう - Qiita )検証
例:契約日が15日未満の場合は翌月の27日、15日以降なら翌々月の27日が請求月になる
みたいな場合の処理
(ここでは契約日が2018-01-31
で考えます)
環境としてはLaravelのv5.5系に入っているCarbon(1.36)を使用して以下の例の処理を動かしますuse Carbon\Carbon; $array = []; $contractDate = new Carbon('2018-01-31'); // 5回払い for ($i = 0; $i < 5; $i++) { // 契約日が15日以降か if ((int)$contractDate->format('d') < 15) { $array['date'] = (new Carbon($contractDate))->addMonth($i + 1)->format("Y-m-27"); } else { $array['date'] = (new Carbon($contractDate))->addMonth($i + 2)->format("Y-m-27"); } $array['target_month'] = (new Carbon($array['date']))->format('Y-m'); var_dump($i); var_dump($array); }↓以下、tinker(laravel用REPL)で実行した結果
int(0) array(2) { ["date"]=> string(10) "2018-03-27" ["target_month"]=> string(7) "2018-03" } int(1) array(2) { ["date"]=> string(10) "2018-05-27" ["target_month"]=> string(7) "2018-05" } int(2) array(2) { ["date"]=> string(10) "2018-05-27" ["target_month"]=> string(7) "2018-05" } int(3) array(2) { ["date"]=> string(10) "2018-07-27" ["target_month"]=> string(7) "2018-07" } int(4) array(2) { ["date"]=> string(10) "2018-07-27" ["target_month"]=> string(7) "2018-07" } >>>なんだこれは・・・
Carbonの
addMonth()
について調べてみるCarbonはDateTimeクラスのラッパーなので、基本的にDateTimeクラスの仕様に依存してるみたいです。
んでaddMonth()
の処理がどうなっているかCarbonのソースコード(Carbon/Carbon.php at version-1.36)を見てみるとCarbon/Carbon.php/** * Add a month to the instance * * @param int $value * * @return static */ public function addMonth($value = 1) { return $this->addMonths($value); }
addMonths()
メソッドを呼び、Carbon/Carbon.php/** * Add months to the instance. Positive $value travels forward while * negative $value travels into the past. * * @param int $value * * @return static */ public function addMonths($value) { if (static::shouldOverflowMonths()) { return $this->addMonthsWithOverflow($value); } return $this->addMonthsNoOverflow($value); }
static::shouldOverflowMonths()
で呼ばれているmonthsOverflow
プロパティはデフォルトでtrue
になっているので、
オーバーフローで月を計算するメソッドaddMonthsWithOverflow()
を呼びます。Carbon/Carbon.php/** * Add months to the instance. Positive $value travels forward while * negative $value travels into the past. * * @param int $value * * @return static */ public function addMonthsWithOverflow($value) { return $this->modify((int) $value.' month'); }でその中身はDateTimeクラスの
modify()
メソッドでした。
ドキュメント(PHP: DateTime::modify - Manual)にも書いてある通り、月の加減算には注意ということで以下の処理例が載ってます。例2 月の加減算には注意
<?php $date = new DateTime('2000-12-31'); $date->modify('+1 month'); echo $date->format('Y-m-d') . "\n"; $date->modify('+1 month'); echo $date->format('Y-m-d') . "\n"; ?>上の例の出力は以下となります。
2001-01-31
2001-03-03要は月の計算(加算)時に存在しない日(2月が28日までで、31日がない)になった場合、あふれた日数(3日)を次の月(3月)に+して計算する(=3月3日)という仕様になってるんですね
確認するために
modify()
メソッドを使用して処理を書いてみましたdate_default_timezone_set('Asia/Tokyo'); for ($i = 0; $i < 10; $i++) { $date = new DateTime('2018-01-31'); $date->modify((int) ($i + 2).' month'); var_dump($date->format('Y-m-d H:i') ." → " . $date->format('Y-m-27')); echo PHP_EOL; }以下実行結果
string(35) "2018-03-31 00:00 → 2018-03-27" string(35) "2018-05-01 00:00 → 2018-05-27" string(35) "2018-05-31 00:00 → 2018-05-27" string(35) "2018-07-01 00:00 → 2018-07-27" string(35) "2018-07-31 00:00 → 2018-07-27" string(35) "2018-08-31 00:00 → 2018-08-27" string(35) "2018-10-01 00:00 → 2018-10-27" string(35) "2018-10-31 00:00 → 2018-10-27" string(35) "2018-12-01 00:00 → 2018-12-27" string(35) "2018-12-31 00:00 → 2018-12-27"案の定、最初に書いた処理と同じような結果になりました。
(※4月や6月は30日までしかないので、1日分、次の月に+されてしまってます)オーバーフローしないメソッド
addMonthsNoOverflow()
ならオーバーフローしない計算メソッドを使えばいいんではないか、ということで
CarbonのaddMonthsNoOverflow()
メソッドの処理を見てみます。Carbon/Carbon.php/** * Add months without overflowing to the instance. Positive $value * travels forward while negative $value travels into the past. * * @param int $value * * @return static */ public function addMonthsNoOverflow($value) { $day = $this->day; $this->modify((int) $value.' month'); if ($day !== $this->day) { $this->modify('last day of previous month'); } return $this; }結局は
modify()
メソッドを使用してるみたいですが、'last day of previous month'
(前の月の最後の日)と書かれてるので、オーバーフローした際は前月の最終日を返すようになっているようです。正しい処理になるように修正
ということで、Carbonの
addMonthsNoOverflow()
メソッドを用いて計算するように先ほどの処理を修正してみます。use Carbon\Carbon; $array = []; $contractDate = new Carbon('2018-01-31'); // 5回払い for ($i = 0; $i < 5; $i++) { if ((int)$contractDate->format('d') < 15) { // $array['date'] = (new Carbon($contractDate))->addMonth($i + 1)->format("Y-m-27"); $array['date'] = (new Carbon($contractDate))->addMonthsNoOverflow($i + 1)->format("Y-m-27"); } else { // $array['date'] = (new Carbon($contractDate))->addMonth($i + 2)->format("Y-m-27"); $array['date'] = (new Carbon($contractDate))->addMonthsNoOverflow($i + 2)->format("Y-m-27"); } $array['target_month'] = (new Carbon($array['date']))->format('Y-m'); var_dump($i); var_dump($array); }以下、tinker(laravel用REPL)で実行した結果
int(0) array(2) { ["date"]=> string(10) "2018-03-27" ["target_month"]=> string(7) "2018-03" } int(1) array(2) { ["date"]=> string(10) "2018-04-27" ["target_month"]=> string(7) "2018-04" } int(2) array(2) { ["date"]=> string(10) "2018-05-27" ["target_month"]=> string(7) "2018-05" } int(3) array(2) { ["date"]=> string(10) "2018-06-27" ["target_month"]=> string(7) "2018-06" } int(4) array(2) { ["date"]=> string(10) "2018-07-27" ["target_month"]=> string(7) "2018-07" } >>>期待通りの処理になりました。
ということで特に理由がなければCarbonを用いて月の計算をする場合は
addMonthsNoOverflow()
メソッドを使うようにしたほうがいいかなというお話でした。おわり
- DateTimeクラスの仕様には度々振り回されます・・・
- というかドキュメントちゃんと読みましょう>自分
参考URL
- 投稿日:2019-04-15T10:54:19+09:00
Laravelのクエリビルダーの戻り値を勘違いしていた件について
Laravelのクエリビルダーの戻り値
insert
新規登録が完了したかの正否を論理値で返却する。
つまり、true/false
のboolean
で返してくれる。update
影響を受けたレコード件数を返す。
つまり、1や10などのinteger
で返してくれる。delete
影響を受けたレコード件数を返す。
つまり、1や10などのinteger
で返してくれる。失敗談
updateやdeleteも
boolean
で返却してくれると思って下記のようなコードを書いてデバックしてました。。。確かに0が来ていれば更新はしていないんですが…
$res = DB::table('hoge') ->where('id', $id) ->update([ 'name' => $name ]); // 別クラスで if (!$res) '更新に失敗';