- 投稿日:2020-09-07T22:09:46+09:00
【Laravel】バッチ処理、Cronのタスクスケジュール実行まで
今日も張り切って備忘録。
バッチの理解を深める為、簡単な処理を実装してみた。環境
PHP 7.3.8
Laravel 6.18.35バッチ処理を書く
Userテーブルに登録されているユーザー数を取得するバッチを作成する。
まずコマンドクラスを生成する。php artisan make:command UserCountコマンドを実行すると、ファイルがapp/Console/Commands/の直下に作成される。
UserCountCommand.php<?php namespace App\Console\Commands; use Illuminate\Console\Command; //あとでログを出力する処理もいれるので use Illuminate\Support\Facades\Log; //操作するテーブルを読み込む use App\User; class UserCountCommand extends Command { /** * The name and signature of the console command. * * @var string */ //コマンドの名前を設定 protected $signature = 'user:count'; /** * 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() { //標準出力&ログに出力するメッセージのフォーマット $message = '[' . date('Y-m-d h:i:s') . ']UserCount:' . User::count(); //INFOレベルでメッセージを出力 $this->info( $message ); //ログを書き出す処理はこちら Log::setDefaultDriver('batch'); Log::info( $message ); } }これでバッチが完成
ターミナルでコマンドを実行
php artisan user:countバッチをCronで定時実行する
crontabにエントリーを記述する事で、エントリーの内容にそって定期的にバッチ処理を実行する事が出来ます。
参考:Laravelのタスクスケジュール(cron)を使いこなす
というわけで、crontabをvimコマンドで編集します。
$crontab -e以下の1行を追加するだけでOK
* * * * * php /path_to_your_project/artisan schedule:run 1>> /dev/null 2>&1
$commands
に作成したバッチコマンドを追加するのと、schedule
にバッチのスケジュールを記載する。app/Console/Kernel.phpclass Kernel extends ConsoleKernel { /** * The Artisan commands provided by your application. * * @var array */ protected $commands = [ \App\Console\Commands\UserCountCommand::class ]; /** * Define the application's command schedule. * * @param \Illuminate\Console\Scheduling\Schedule $schedule * @return void */ protected function schedule(Schedule $schedule) { // user:countコマンドを毎分実行する $schedule->command('user:count')->everyMinute(); }ログファイルを見てみると
- 投稿日:2020-09-07T20:04:50+09:00
Laravelでuniqueの対象から自分自身の値を外す
問題
- ユーザー情報の編集や更新をするフォームを作ってバリデーションを設けた
- ユーザー名メールアドレスに
unique
を適用したunique
の対象以外の情報を変更したいのに、unique
に引っかかってしまって、その先に進めない例
unique
が適用されている職種名
のエラーが出てきてしまう。
コード
現在のコード、バリデーションはこのような状態
Works/EditCheckController.php$validator = Validator::make($request->all(), [ 'edit_name' => 'required|max:20|unique:works,works_name', 'edit_flag' => 'boolean', ], [ 'edit_name.required' => '職種名を入力してください。', 'edit_name.max' => '職種名は20字以内で入力してください。', 'edit_name.unique' => 'この職種名は既に登録されています。', 'edit_flag.boolean' => '0か1を入力してください。', ]);解決策
unique
の引数を増やすWorks/EditCheckController.php$validator = Validator::make($request->all(), [ 'edit_name' => 'required|max:20|unique:works,works_name,'.$request -> edit_id.',works_id', 'edit_flag' => 'boolean', ], [ 'edit_name.required' => '職種名を入力してください。', 'edit_name.max' => '職種名は20字以内で入力してください。', 'edit_name.unique' => 'この職種名は既に登録されています。', 'edit_flag.boolean' => '0か1を入力してください。', ]);
unique
の内容は、
unique: uniqueチェックをしたいテーブル名, uniqueチェックをしたいカラム名,.uniqueチェックの対象外にしたいデータがあるレコードの主キー.',uniqueチェックの対象外にしたいデータがあるレコードの主キーのカラム名'
となっている。処理の内容としては、
uniqueチェックをした時の主キー
と対象外にしたいデータがあるレコードの主キー
が一致すれば、unique
の結果を返さないuniqueチェックをした時の主キー
と対象外にしたいデータがあるレコードの主キー
が一致しなければ、unique
の結果を返す(であってるはず)
確認
unique
の対象となっているWORKS_NAME
を変更せずに他のデータを編集できるようになり、
既に登録されているデータは
unique
の対象にすることができた。
参考文献
- 投稿日:2020-09-07T19:08:48+09:00
【Laravale】foreachで回したセレクトボックスの初期値を変数で設定する
概要
例えばキャラクターAの職業を変更したいと仮定する。編集ボタンを押すとキャラクターAの情報が表示され、職業はセレクトボックスで選択できる。セレクトボックスの中身は
foreach
を使って、コントローラから持ってきた職業一覧である$jobs
の中身を表示するが、その初期値をキャラクターAの変更前の職業である戦士
にしたい。リレーション
- charactersテーブルとjobsテーブルがある。
- charactersテーブルには
jobs_id
というインデックスがあり、リレーションを組んでいる。- jobsテーブルの
name
カラムには職業が入っている。魔術師とか竜騎士とか(まあなんでもいい)。セレクトボックスにはこの職業が羅列されることになるセレクトボックス
基本として、これでセレクトボックスに『jobsテーブルに入っている職業』が羅列される。
edit.blade.php<div class=""> <select name="job_id"> @foreach($jobs as $job) <option value="{{ $job->id }}">{{ $job->name }}</option> @endforeach </select> </div>デフォルト値の設定
コントローラからキャラクターの職業を持ってくる
(職業一覧、キャラクターの情報などを取得する部分、渡すべき変数でこの記事に関係ないものは省略する)。CharacterController.php$character_job = $character->job->character_name; return view('character.edit') // 中略 ->with('jobs', $jobs) ->with('character_job', $character_job);あとはセレクトボックスの記述を修正する。
edit.blade.php<div class=""> <select name="job_id"> @foreach($jobs as $job) @if ($job->name === $character_job) <option value="{{ $job->id }}" selected="selected">{{ $job->name }}</option> @else <option value="{{ $job->id }}">{{ $job->name }}</option> @endif @endforeach </select> </div>「セレクトボックスの職業とキャラクターの職業が一致する場合」で
selected
する
(書いたはいいが記事に登場する職業の設定などはほとんど関係がなかった)。
- 投稿日:2020-09-07T19:08:48+09:00
【Laravel】foreachで回したセレクトボックスの初期値を変数で設定する
概要
例えばキャラクターAの職業を変更したいと仮定する。編集ボタンを押すとキャラクターAの情報が表示され、職業はセレクトボックスで選択できる。セレクトボックスの中身は
foreach
を使って、コントローラから持ってきた職業一覧である$jobs
の中身を表示するが、その初期値をキャラクターAの変更前の職業である戦士
にしたい。リレーション
- charactersテーブルとjobsテーブルがある。
- charactersテーブルには
jobs_id
というインデックスがあり、リレーションを組んでいる。- jobsテーブルの
name
カラムには職業が入っている。魔術師とか竜騎士とか(まあなんでもいい)。セレクトボックスにはこの職業が羅列されることになるセレクトボックス
基本として、これでセレクトボックスに『jobsテーブルに入っている職業』が羅列される。
edit.blade.php<div class=""> <select name="job_id"> @foreach($jobs as $job) <option value="{{ $job->id }}">{{ $job->name }}</option> @endforeach </select> </div>デフォルト値の設定
コントローラからキャラクターの職業を持ってくる
(職業一覧、キャラクターの情報などを取得する部分、渡すべき変数でこの記事に関係ないものは省略する)。CharacterController.php$character_job = $character->job->character_name; return view('character.edit') // 中略 ->with('jobs', $jobs) ->with('character_job', $character_job);あとはセレクトボックスの記述を修正する。
edit.blade.php<div class=""> <select name="job_id"> @foreach($jobs as $job) @if ($job->name === $character_job) <option value="{{ $job->id }}" selected="selected">{{ $job->name }}</option> @else <option value="{{ $job->id }}">{{ $job->name }}</option> @endif @endforeach </select> </div>「セレクトボックスの職業とキャラクターの職業が一致する場合」で
selected
する
(書いたはいいが記事に登場する職業の設定などはほとんど関係がなかった)。
- 投稿日:2020-09-07T15:53:13+09:00
LaravelでS3アクセスが403 Forbiddenエラー
Laraelで以下のようなコードを書いた時に
Storage::disk('s3')->put('hoge.csv', 'あいう');Error executing "PutObject" on "https://bucket-name.s3.ap-northeast-1.amazonaws.com/hoge.csv"; AWS HTTP error: Client error: `PUT https://bucket-name.s3.ap-northeast-1.amazonaws.com/hoge.csv` resulted in a `403 Forbidden` response: ...といったエラーが表示された。
S3の権限設定はconfig/filesystems.php
で行うため該当ファイルを見ると、シークレットアクセスキーが間違っていることを確認。修正して再実行。しかしまったく同じエラーが出続けます。
バケットポリシーやIAMの設定を見直しても問題なく、aws cliからPutObjectを試してみるとうまくいくため権限は設定できているようだった。キャッシュが原因
原因はLaravelのキャッシュ設定で
php artisan cache:clear php artisan config:clear php artisan config:cache上記コマンド実行でLaraelのキャッシュをクリアしたところ問題なくPutObjectができるようになった(おそらくどれか一つのコマンドでいいと思うが未検証)。最初に間違ってシークレットアクセスキーを設定してしまったものをキャッシュしてしまい、永遠にエラーが吐かれていたようだった。
- 投稿日:2020-09-07T14:57:37+09:00
Laravel入門 Macでの環境構築の仕方
動作環境
Mac OS Catalina
Laravel 6.18.35
php 7.3.21この記事について
自分がmacでlaravelの環境構築をする際にずいぶんエラーに悩まされたので、同じようにエラーに悩む人のために環境構築手順をまとめておきます。
インストール
はじめに
laravelのインストール手順に関しては二種類あって
- virtual machineを使う仮想間環境を用意する方法
- ローカルのホストに環境を用意する方法
この二種類があって公式からは前者が進められている
virtual machineを使う仮想間環境を用意する方法
ここでは詳細な説明は省きます。
詳しいやり方に関しては下記を参照してください。
https://laravel.com/docs/6.x/homestead#first-stepsローカルのホストに環境を用意する方法
前提条件
前提条件として環境にこれらが必要です。
PHP >= 7.2.0
BCMath PHP Extension
Ctype PHP Extension
Fileinfo PHP extension
JSON PHP Extension
Mbstring PHP Extension
OpenSSL PHP Extension
PDO PHP Extension
Tokenizer PHP Extension
XML PHP Extensionphpのバージョン以外は特に大きくは気にしなくても大丈夫です。
インストール方法
①まずはcompoerをダウンロードする。
laravelをインストールする前にパッケージ管理ソフトである
composerをインストールする必要がある
- macの場合
homebrewで入るのでいつも通りhomebrewで入れましょう。
ターミナルで下記を実行する
brew install composerhomebrewがわからない人へ
下記の記事を参考にmacにhomebrewを入れましょう
今は特に何を入れているのか分からなくても問題ないです
https://qiita.com/zaburo/items/29fe23c1ceb6056109fd
- 他の場合
公式からダウンロードしていれてください
https://getcomposer.org②次にlaravelを入れる
composerが入ったらcomposer経由でlaravelをインストールします
ターミナルで下記を実行する
composer global require "laravel/installer"③ 最後に環境変数の設定、zprofileをいじる
ターミナルで実行する
echo "export PATH=~/.composer/vendor/bin:$PATH" >> ~/.zprofile source ~/.zprofile下記のコマンドで環境変数がきちんと設定されているか確認する
composer global about開発の手順
これでlaravelがインストールされたので、開発が進めれます。
基本的にコマンドラインから実行していきます。プロジェクトの作成
アプリを開発するディレクトリを
mkdir ディレクトリ名これで作成できます
ディレクトリ名は好きにつけてokですcd ディレクトリ名これで作成したディレクトリに移動します
このディレクトリの中でアプリを開発していきます
下記のコマンドでlaravel new laravelappするとzip extentionがないと言われる。
macのcatalinaの場合これでは無理なので代わりに,composer経由で行う
composer create-project --prefer-dist laravel/laravel test_appこれでtest_appの部分をアプリ名にして実行する
公式では「laravel new アプリ名」でできるらしいんですけど、
どうにもmacのcatalinaではうまくいかないらしいです。
まだ解決策が見つかってないらしく外国の方が嘆いてました。下記参照
https://github.com/laravel/framework/issues/30386本題に戻ります。
次にこれをターミナルで実行して、ローカルサーバがたつか確認しましょうcd test_app php artisan servelaravelのページを表示できれば成功
トラブルシューティング
まれにcomposerからでもlaravelを使えないことがあるみたいで、そういう場合はdockerを使うしかない。
下記に一番簡単なlaradockでの環境構築の方法を載せておく。dockerでlaradockで環境構築する
これ見てやるとできる、laradock
https://www.techpit.jp/courses/laravel-line-bot/lectures/11539279mkdir laravel-linebot
cd laravel-linebot
git clone https://github.com/Laradock/laradock.git.envファイルを編集
APP_CODE_PATH_HOST=../laravel
DATA_PATH_HOST=../datacd ~/laravel-linebot/laradock
docker-compose up -d workspace php-fpm nginxdocker-compose exec workspace composer create-project --prefer-dist laravel/laravel . "6.8.*"
参考記事
laravel公式ドキュメント
https://laravel.com/docs/6.x/installationlaravelコマンドで詰まった時に見た記事
https://qiita.com/endeavor/items/77f7017473c95c42fc29
https://iiiso.ti-da.net/e8647248.html
- 投稿日:2020-09-07T13:15:54+09:00
Eloquent のいろんなwhere
単純なwhere
$query->where('col1', $val1) $query->where('col1', '<>', $val1)where でAND
$query->where('col1', $val1)->where('col2', $val2)where でOR
$query->where('col1', $val1)->orWhere('col2', $val2)where で複数値
$query->whereIn('col1', array($val1,$val2)) $query->whereNotIn('col1', array($val1,$val2))where でNull
$query->whereNull('col1') $query->whereNorNull('col1')where で(A AND B) Or (C or D)など
$query->where(function($query) use($val1,$val2){ $query->where('col1',$val1) ->where('col2',$val2) }) ->orWhere(function($query) use($val3,$val4){ $query->where('col3',$val3) ->where('col4',$val4) })
- 投稿日:2020-09-07T12:34:05+09:00
LaravelでClass 'Hoge\FooClass' not found
Laravelに限りませんが、Composerを利用していてClass not foundエラーに遭遇することがあります。
まず対応することはcomposer dump-autoloadのコマンドを打ってautoloadさせます。
それでもなお
Class 'Hoge\FooClass' not foundが継続する場合は
vendor/composer/autoload_classmap.php
ファイルを参照して、上記エラーならFooClass
が存在するか確認します。
おそらく存在しないと考えられます。今回は新規追加したディレクトリを読み込めていないことが原因でした。その場合は
composer.json
を確認をするとcomposer.json"autoload": { "psr-4": { "App\\": "app/" }, "classmap": [ "database/seeds", "database/factories" ] },の記述のclassmapに新規追加したディレクトリ
Hoge
が存在しないことがわかります。composer.json"autoload": { "psr-4": { "App\\": "app/" }, "classmap": [ "database/seeds", "database/factories", "app/Hoge" ←追加行 ] },これで再度
composer dump-autoload
でファイルが読み込まれました。
- 投稿日:2020-09-07T11:23:53+09:00
LaravelをDockerで開発している際におこるPermission Deniedを回避する
LaradockではなくLaravelをDockerでローカル開発している際にPermission Deniedで
/storage/logs
の書き込みが失敗する事象が見られた。
この際の回避方法を残しておきます。おそらくPermission Deniedになる原因はホストとクライアントとでUID、GIDが異なることで発生する。
docker-composeにより構成はNginx > php-fpmとしている場合以下のようにphp-fpm側のDockerfileにNginxのユーザーであるwww-data
に対して任意のUID、GIDが指定できるようにしておく。DockerfileARG PUID=1000 ARG PGID=1000 RUN echo "-> $PUID" RUN echo "-> $PGID" RUN groupmod -o -g $PGID www-data && \ usermod -o -u $PUID -g www-data www-dataこれを
docker-compose build
時に自身のMacのUID、GIDをを差し込む。$ docker-compose build --build-arg PUID=$(id -u) --build-arg PGID=$(id -g) appこれで無事ホストとクライアントのUID、GIDが同じになった。
おまけ
Makefileで上記のコマンドを設定していると
$(id -u)
が正しく解決できなかった。
以下のように書くとうまくいくMakefilebuild_app: $(eval UID := $(shell id -u)) $(eval GID := $(shell id -g)) @docker-compose build --build-arg PUID=$(UID) --build-arg PGID=$(GID) app
- 投稿日:2020-09-07T10:17:05+09:00
Cloud Run上でLaravel×React(Mix使用)のサイトを爆速で動かす - 必要な設定と料金関連
サーバレスVPCで関連サービスと繋ぐ
Laravelをサーバレスで動かすに当たって、最低限使う、かつ、少し考慮が必要と思われるのは
- データベース(MySQL)
- セッション
- ログ
あたりだと思います。ここについては設定方法が公式にありますのでここではその情報だけまとめておきます。
データベース(MySQL)
当然、データベースの情報はどこからでも直接アクセスさせるとマズいですよね。なので、IP制限をするケースなどもあるかと思います。
ただ、Cloud SQLにはローカルIPを付与してCloud RunからそのIPへ向けてアクセスすることも可能です。
設定方法については、Cloud Run(フルマネージド)から Cloud SQL に接続する
を参考にすると良いと思います。
ここで重要担ってくるのが、サーバーレス VPC アクセスの構成です。このサービスは下記のような構成を実現します。(画像は公式ページから引用)
非常にわかりやすい図解ですね。中心にある「サーバレスVPCアクセスコネクタ」を使うことで、Cloud SQLをはじめとするGCPのリソースをグローバルに晒すことなくセキュアに使えます。
セッションはMemory Store(Redis)を使う
サーバレスでログインなどのセッションを使う場合は必須のサービスですね。これをやらないと、コンテナが変わるたびにセッションも無効になるので、ログイン状態が度々無効になってしまいます。。。
こちらもCloud Run(フルマネージド)サービスから Redis インスタンスへの接続として公式に情報があります。
Laravelはセッションに
memcached
やredis
を使う機能が標準で備わっているのでそれを利用しましょう。config
を変更するだけなので非常に簡単に対応できます。HTTP Session(英語)
HTTPセッション(日本語)ログは標準出力にする
コンテナ×サーバレスなので、当然の事ながらコンテナ内に
laravel.log
などを作ったとしても意味はありません。ログを追えるようにするためには、ログの出力方法をファイルから標準出力に変更する必要があります。弊社の場合には下記のような設定をして、ワーニング以上のログを標準出力するようにしています。'custom' => [ 'driver' => 'monolog', 'handler' => StreamHandler::class, 'with' => [ 'stream' => 'php://stdout', ], 'level' => 'warning', ],こうする事でログビューからエラーやログを探すことが簡単になります。
Cloud Runでみた時
Logs Viewerでみた時
上記の構成を実現する上で最低限、必要になると思われる料金
※あくまで現時点で弊社が考える目安です。
サービス名 用途 月額 Cloud Run フロントとバックエンド 従量課金 Memorystore laravelのセッション保持 5,162 Cloud SQL For MySQL MySQL 1,357 Severless VPC Access Connector Cloud RUNとMySQLの接続 1,480 Container Registory デプロイに利用(Cloud Storageに対して課金) 10 Cloud Runは無料枠が大きいのも魅力的ですね。(2020/9/7時点 公式サイトより引用)
どちらかということ、固定費用が必要なのはMy SQL
やRedis
関係のサービス。サーバレスVPCコネクタ
もかかります。
ちなみに、サーバレスVPCコネクタ
はMicro Instance with burstable CPU
として請求が上がるのでこちらも注意が必要です。(最初分からなくてGCPのサポートに問い合わせてしまいました。。。料金に記載はきっちりとあります)
階層 CPU メモリ リクエスト ネットワーキング 無料 最初の 180,000 vCPU 秒は無料 最初の 360,000 GiB 秒は無料 200 万リクエストまで無料 北米内の下り(外向き)は、1 GiB まで無料 まとめ
インフラに対する意識をほとんどすることなく、ここまでスケーラブルかつ低料金でサービスを開始できる
Cloud Run
は本当に素晴らしい製品だと思います。
デプロイまでの手順も非常にシンプルなので、学習コストも低いことが特徴的ですね。(Developer Experience高い)ここまでお付き合いいただきまして、ありがとうございました!
- 投稿日:2020-09-07T09:52:22+09:00
Eloquentのjoin時に条件を追加する方法
- 投稿日:2020-09-07T09:17:22+09:00
Laravel https環境下でCSSが反映されない
目的
- Laravelでオリジナルのcssファイルを作成しビューファイルに反映しようとしても一切されない問題を解決した話をまとめる
原因
- Laravelアプリが可動しているWebサーバがHTTPS化されていたためcssファイルの読み込み方法が異なるため読み込まれなかった。
原因と解決方法
下記の様に
asset()
で記載するとhttps環境下だとCSSファイルを読み込むことができない。<link rel="stylesheet" href="{{ asset('アプリ名ディレクトリ/publicディレクトリからCSSファイルまでのパス') }}">下記の様に
secure_asset()
を用いることによりhttps環境下でもセキュアを保ったままCSSを読み込むことができる。<link rel="stylesheet" href="{{ secure_asset('アプリ名ディレクトリ/publicディレクトリからCSSファイルまでのパス') }}"> <!-- 少しだけ具体的に記載する --> <link rel="stylesheet" href="{{ secure_asset('/css/CSSファイル名') }}">参考文献
- 投稿日:2020-09-07T00:53:02+09:00
[Laravel] Facade作成の流れ
今回の題
Facadeの作成方法を復習がてらメモとして書き残します。
バージョン
Laravel6.8
大まかな流れ
- クラスの作成
- サービスプロバイダーを通し、クラスをサービスコンテナへ登録
- Facadeを作成し呼び出す
クラスの作成
クラスを配置するディレクトリの作成
$ mkdir app/Services
app/Services/
以下にGreetingService.php
を作成し、クラスと適当なメソッドを定義。<?php namespace App\Services; class GreetingService { public function hello() { return 'hello'; } }サービスプロバイダーを通し、クラスをサービスコンテナへ登録
自前のサービスプロバイダーを作成
$ php artisan make:provider GreetingServiceProvider上のコマンドで、
app/Providers/
以下に作成されたGreetingServiceProvider.php
を以下のように編集。<?php namespace App\Providers; use Illuminate\Support\ServiceProvider; class GreetingServiceProvider extends ServiceProvider { /** * Register services. * * @return void */ public function register() { $this->app->bind('greeting', 'App\Services\GreetingService'); // 追記 } /** * Bootstrap services. * * @return void */ public function boot() { // } }サービスプロバイダーを登録
config\app.php
を開き、以下のように追記。'providers' => [ // 略 /* * 自作のサービスプロバイダー */ App\Providers\GreetingServiceProvider::class, // 追記 ],サービコンテナに登録されたか確認する
サービコンテナの実体は、
Illuminate\Foundation\Applicationクラス
。
Illuminate\Foundation\Applicationクラスは、ヘルパ関数app()
で呼び出せるので以下のようにして中身を確認。dd(app());さらに色々出てきた中に、先ほど登録した
greeting
があればOK。
Facadeを作成する
Facadeを配置するディレクトリを作成。
$ mkdir app/Facades
app\Facades
以下にGreeting.php
を作成し以下のように編集。<?php namespace App\Facades; use Illuminate\Support\Facades\Facade; class Greeting extends Facade { protected static function getFacadeAccessor() { return 'greeting'; // サービスコンテナに登録した名前。 } }Facadeのエイリアスを登録
config\app.php
を開き、以下のように追記。'aliases' => [ // 略 /* * 自作のFacade */ 'Greeting' => App\Facades\Greeting::class, // 追記 ],Facadeを呼び出す
Greeting::hello(); // 結果 helloコントローラーなどのnamespaceの中から使おうとする場合は
グローバルプレフィックス演算子\
をつけてあげるか、
(namespace中からグローバルなクラスなどを使おうとする場合に必要。 参考)\Greeting::hello(); // 結果 helloもしくは、
use Greeting; Greeting::hello(); // 結果 helloでOK。