Quantcast
Browsing Latest Articles All 61 Live
Mark channel Not-Safe-For-Work? (0 votes)
Are you the publisher? or about this channel.
No ratings yet.
Articles:

laravelをバージョンアップしたらすること

はじめに

お久しぶりです。
今回はStripeを実装しようとしたところ、バージョンが古いからアップデートしてくれ的なエラーメッセージが出たので、
Laravel5.6から5.8にバージョンをあげたときにやったことをメモ程度に書き残します。

以下メモ

composer.jsonを修正
laravel/frameworkを5.7に修正しcompoesr updateを実行。
エラーがなかったら5.8に修正しさらにcomposer updateを実行。

5.8にかえた段階でなんかエラーがたくさん発生

#0 プロジェクト名/laravel/framework/src/Illuminate/Container/Container.php(785): NunoMaduro\Collision\Adapters\Laravel\CollisionServiceProvider->NunoMaduro\Collision\Adapters\Laravel\{closure}(Object(Illuminate\Foundation\Application), Array)
#1 プロジェクト名/vendor/laravel/framework/src/Illuminate/Container/Container.php(667): Illuminate\Container\Container->build(Object(Closure))
#2 プロジェクト名/vendor/laravel/framework/src/Illuminate/Container/Container.php(615): Illuminate\Container\Container->resolve('Illuminate\\Cont...', Array)
#3 プロジェクト名/vendor/nunomaduro/collision/src/Adapters/Laravel/CollisionServiceProvider.php on line 44

とりあえずなんのことだか、さっぱりでしたが#3にヒントっぽいのがありましたので、そこのディレクトリまで移動して中身を確認。
多分ここでエラーが起きてます。。。
エラーの原因はわかったけど、修正の仕方がわからないので、適当にlaravel5.8のプロジェクトを作成しました。

CollisionServiceProvider.phpを修正しました。

CollisionServiceProvider--laravel5.6.php
<?php/**
 * This file is part of Collision.
 *
 * (c) Nuno Maduro <enunomaduro@gmail.com>
 *
 *  For the full copyright and license information, please view the LICENSE
 *  file that was distributed with this source code.
 */namespaceNunoMaduro\Collision\Adapters\Laravel;useNunoMaduro\Collision\Provider;useIlluminate\Support\ServiceProvider;useNunoMaduro\Collision\Adapters\Phpunit\Listener;useNunoMaduro\Collision\Contracts\ProviderasProviderContract;useIlluminate\Contracts\Debug\ExceptionHandlerasExceptionHandlerContract;useNunoMaduro\Collision\Contracts\Adapters\Phpunit\ListenerasListenerContract;/**
 * This is an Collision Laravel Adapter Service Provider implementation.
 *
 * Registers the Error Handler on Laravel.
 *
 * @author Nuno Maduro <enunomaduro@gmail.com>
 */classCollisionServiceProviderextendsServiceProvider{/**
     * {@inheritdoc}
     */publicfunctionregister(){if($this->app->runningInConsole()&&!$this->app->runningUnitTests()){$this->app->singleton(ListenerContract::class,Listener::class);$this->app->bind(ProviderContract::class,Provider::class);$appExceptionHandler=$this->app->make(ExceptionHandlerContract::class);$this->app->singleton(ExceptionHandlerContract::class,function($app)use($appExceptionHandler){returnnewExceptionHandler($app,$appExceptionHandler);});}}}?>
ExceptionHandler--laravel5.6.php
<?php/**
 * This file is part of Collision.
 *
 * (c) Nuno Maduro <enunomaduro@gmail.com>
 *
 *  For the full copyright and license information, please view the LICENSE
 *  file that was distributed with this source code.
 */namespaceNunoMaduro\Collision\Adapters\Laravel;useException;useIlluminate\Contracts\Foundation\Application;useNunoMaduro\Collision\Contracts\ProviderasProviderContract;useIlluminate\Contracts\Debug\ExceptionHandlerasExceptionHandlerContract;useSymfony\Component\Console\Exception\ExceptionInterfaceasSymfonyConsoleExceptionInterface;/**
 * This is an Collision Laravel Adapter ExceptionHandler implementation.
 *
 * Registers the Error Handler on Laravel.
 *
 * @author Nuno Maduro <enunomaduro@gmail.com>
 */classExceptionHandlerimplementsExceptionHandlerContract{/**
     * Holds an instance of the application exception handler.
     *
     * @var \Illuminate\Contracts\Debug\ExceptionHandler
     */protected$appExceptionHandler;/**
     * Holds an instance of the application.
     *
     * @var \Illuminate\Contracts\Foundation\Application
     */protected$app;/**
     * Creates a new instance of the ExceptionHandler.
     *
     * @param \Illuminate\Contracts\Foundation\Application $app
     * @param \Illuminate\Contracts\Debug\ExceptionHandler $appExceptionHandler
     */publicfunction__construct(Application$app,ExceptionHandlerContract$appExceptionHandler){$this->app=$app;$this->appExceptionHandler=$appExceptionHandler;}/**
     * {@inheritdoc}
     */publicfunctionreport(Exception$e){$this->appExceptionHandler->report($e);}/**
     * {@inheritdoc}
     */publicfunctionrender($request,Exception$e){return$this->appExceptionHandler->render($request,$e);}/**
     * {@inheritdoc}
     */publicfunctionrenderForConsole($output,Exception$e){if($einstanceofSymfonyConsoleExceptionInterface){$this->appExceptionHandler->renderForConsole($output,$e);}else{$handler=$this->app->make(ProviderContract::class)->register()->getHandler()->setOutput($output);$handler->setInspector((newInspector($e)));$handler->handle();}}}
CollisionServiceProvider--laravel5.6.php
<?php/**
 * This file is part of Collision.
 *
 * (c) Nuno Maduro <enunomaduro@gmail.com>
 *
 *  For the full copyright and license information, please view the LICENSE
 *  file that was distributed with this source code.
 */namespaceNunoMaduro\Collision\Adapters\Laravel;useNunoMaduro\Collision\Provider;useIlluminate\Support\ServiceProvider;useNunoMaduro\Collision\Adapters\Phpunit\Listener;useNunoMaduro\Collision\Contracts\ProviderasProviderContract;useIlluminate\Contracts\Debug\ExceptionHandlerasExceptionHandlerContract;useNunoMaduro\Collision\Contracts\Adapters\Phpunit\ListenerasListenerContract;/**
 * This is an Collision Laravel Adapter Service Provider implementation.
 *
 * Registers the Error Handler on Laravel.
 *
 * @author Nuno Maduro <enunomaduro@gmail.com>
 */classCollisionServiceProviderextendsServiceProvider{/**
     *
     * {@inheritdoc}
     */protected$defer=true;/**
     *
     * {@inheritdoc}
     */publicfunctionregister(){if($this->app->runningInConsole()&&!$this->app->runningUnitTests()){$this->app->singleton(ListenerContract::class,Listener::class);$this->app->bind(ProviderContract::class,Provider::class);$appExceptionHandler=$this->app->make(ExceptionHandlerContract::class);$this->app->singleton(ExceptionHandlerContract::class,function($app)use($appExceptionHandler){returnnewExceptionHandler($app,$appExceptionHandler);});}}/**
     *
     * {@inheritdoc}
     */publicfunctionprovides(){return[ProviderContract::class];}}
ExceptionHandler--laravel5.8.php
<?php/**
 * This file is part of Collision.
 *
 * (c) Nuno Maduro <enunomaduro@gmail.com>
 *
 *  For the full copyright and license information, please view the LICENSE
 *  file that was distributed with this source code.
 */namespaceNunoMaduro\Collision\Adapters\Laravel;useException;useIlluminate\Contracts\Foundation\Application;useNunoMaduro\Collision\Contracts\ProviderasProviderContract;useIlluminate\Contracts\Debug\ExceptionHandlerasExceptionHandlerContract;useSymfony\Component\Console\Exception\ExceptionInterfaceasSymfonyConsoleExceptionInterface;/**
 * This is an Collision Laravel Adapter ExceptionHandler implementation.
 *
 * Registers the Error Handler on Laravel.
 *
 * @author Nuno Maduro <enunomaduro@gmail.com>
 */classExceptionHandlerimplementsExceptionHandlerContract{/**
     * Holds an instance of the application exception handler.
     *
     * @var \Illuminate\Contracts\Debug\ExceptionHandler
     */protected$appExceptionHandler;/**
     * Holds an instance of the application.
     *
     * @var \Illuminate\Contracts\Foundation\Application
     */protected$app;/**
     * Creates a new instance of the ExceptionHandler.
     *
     * @param \Illuminate\Contracts\Foundation\Application $app
     * @param \Illuminate\Contracts\Debug\ExceptionHandler $appExceptionHandler
     */publicfunction__construct(Application$app,ExceptionHandlerContract$appExceptionHandler){$this->app=$app;$this->appExceptionHandler=$appExceptionHandler;}/**
     *
     * {@inheritdoc}
     */publicfunctionreport(Exception$e){$this->appExceptionHandler->report($e);}/**
     *
     * {@inheritdoc}
     */publicfunctionrender($request,Exception$e){return$this->appExceptionHandler->render($request,$e);}/**
     *
     * {@inheritdoc}
     */publicfunctionrenderForConsole($output,Exception$e){if($einstanceofSymfonyConsoleExceptionInterface){$this->appExceptionHandler->renderForConsole($output,$e);}else{$handler=$this->app->make(ProviderContract::class)->register()->getHandler()->setOutput($output);$handler->setInspector((newInspector($e)));$handler->handle();}}/**
     * Determine if the exception should be reported.
     *
     * @param \Exception $e
     * @return bool
     */publicfunctionshouldReport(Exception$e){return$this->appExceptionHandler->shouldReport($e);}}

とりあえずこの二つのファイルを修正しました。
php artisan を実行してみましょう。
動くはずです。

ざっと書きましたが上記の流れで自分はartisanが実行できるようになりました!
記事が少なくて大変でした。。。

Laravelのフラッシュメッセージをreturn viewの時にもやりたい

概要

エラーメッセージや完了メッセージなどを表示したい時にアラートを表示したりしますが、リロードした時には表示させたくないので、フラッシュメッセージを使いたい場面が多々あると思います。Laravelでも簡単にフラッシュメッセージを出すことができるのですが、コントローラーでのreturn redirectreturn viewではフラッシュメッセージの振る舞いが変わるので、まとめておきます。

動作環境

Laravel 5.8

前提知識

まずControllerからviewにデータを受け渡す方法として、今回の記事では2種類扱っています。

  • Controllerからviewに受け渡す変数
  • PHPのSession

前者はviewでデータを表示させるために普段使うやつです。コードでは以下のような使い方をします。

HomeController.php
publicfunctionindex(){returnview('home.index')->with('message','Hello!');}
home.blade.php
<p>{{ message }}</p>

後者はPHPのセッションを用いてデータをやりとりする方法です。上と同じメッセージをセッションで実現すると下の通りになります。

HomeController.php
publicfunctionindex(){Session::put('message','Hello!');returnview('home.index');}
home.blade.php
@if (Session::has('message'))
    <p>{{ session('message') }}</p>
@endif

本題

フラッシュメッセージを扱う時にはセッションを用います。

Controllerの処理

リダイレクトする場合

HomeController.php
publicfunctionredirect(){returnredirect()->route('home')->with('message','Hello!');}

ビューを返す場合

HomeController.php
publicfunctionindex(){Session::flash('message','Hello!');returnview('home.index');}

Viewの処理

home.blade.php
@if (Session::has('message'))
    <p>{{ session('message') }}</p>
@endif

ややこしいポイント

リダイレクトの際のredirect()->with()withはセッションのフラッシュメッセージを保存するメソッドになっています。しかし、ビューを返す際のview()->with()withは前提知識でも書いたように、変数をセットするメソッドです。なので、ビューを返す場合の処理は2行になっています。逆にリダイレクトの場合でもwithを使わずに下のように書くことも可能です。

HomeController.php
publicfunctionredirect(){Session::flash('message','Hello!');returnredirect()->route('home');}

最後に

ビューを返すときはフラッシュメッセージの内容を変数に入れて表示すれば良いのでは?という方もいそうですが、テンプレートでフラッシュメッセージ表示部分を共通化しているので、ビューを返す時にもセッションを使いたいという気持ちになり、記事を書きました。

参考記事

https://stackoverflow.com/questions/42417865/how-to-return-view-with-flash-message

[初心者コピペ用]LaravelをAWSのEC2へデプロイ

前提

  • 2020年1月4日に確認したものです。
  • AWSのアカウント作成済
  • プロジェクトをGitHubにpush済

環境

  • macOS Catalina 10.15.2
  • Apache 2.4
  • PHP 7.1.33
  • MySQL 5.7
  • Laravel 5.8

1. EC2インスタンスの起動

  • EC2コンソールで作成(「公式チュートリアル」が分かりやすかったです)
  • 基本的に無料利用枠(下記の項目以外はデフォルト値)
設定項目設定した内容
Amazon マシンイメージ (AMI)Amazon Linux 2でなく、Amazon Linux
インスタンスタイプt2.micro
  • Linux2だと、手順が異なる公式ドキュメント

  • インスタンス作成の最後に、SSH接続に必要なキーペアを生成or設定

  • キーペア(pemファイル)は、~/.sshフォルダに入れておくと良い

2. EC2内で作業

2-1. SSHでEC2に接続

SSHとは. SSH(Secure Shell)とは、安全に通信を行って、ネットワークに接続された機器を遠隔操作するための通信手段(プロトコル)の1つ

※ 参照:初心者がSSHについて学ぶ(´・ω・`)

$ ssh -i [キーペア(pemファイル)を置いた場所のパス] ec2-user@[パブリックDNS or パブリックIP]
  • iオプションで、上記で生成or設定したpemファイルを指定する
  • ec2-userは、デフォルトのユーザー名。最初はこのユーザー名を使う
  • パブリックDNS or パブリックIPは、EC2コンソール画面で確認できる
  • 初回接続時のみ、yes/noを聞かれる

2-2. Apache, PHP, MySQLのインストール

  • yumのアップデートは忘れずに!(パッケージを最新にする)
  • yumはyオプションを付けることで、問い合わせ全てにYesと答えるため、yes/noがいちいち聞かれなくなります。
$ sudo yum update -y
$ sudo yum install -y httpd24 php71 php71-mbstring php71-zip php71-fpm php71-mysqlnd mysql57-server

Laravel 5.8のサーバ要件

2-3. Apacheを起動

$ sudo service httpd start

※ Linux2だと、systemctlコマンドが使える

この時点で、ブラウザでパブリック IPにアクセスすると、Apacheの画面が表示される。
EC2コンソール画面のセキュリティグループのインバウンドで、httpの80ポートを空けておくことを忘れずに
スクリーンショット 2020-01-04 18.42.21.png

# システムが再起動するたびにApacheが起動するように設定
$ sudo chkconfig httpd on

# 設定の確認
$ chkconfig --list httpd

2-4. ec2-userのファイル操作の許可を設定

参照:公式チュートリアル

Amazon Linux Apache ドキュメントルートは /var/www/html であり、デフォルトでは root によって所有されます。

そのため、ec2-userにファイル操作の権限を設定する必要がある。

# ec2-userをapacheグループに追加
$ sudo usermod -a -G apache ec2-user

# いったんログアウトして、再度ログイン
$ exit
$ ssh -i [キーペアのpemファイルを置いた場所のパス] ec2-user@[パブリックDNS or パブリックIP]

# /var/www とそのコンテンツのグループ所有権を apache グループに変更
$ sudo chown -R ec2-user:apache /var/www

# グループに、/var/wwwの書き込み許可追加
$ sudo chmod 2775 /var/www

# /var/www とサブディレクトのディレクトリ許可を変更
$ find /var/www -type d -exec sudo chmod 2775 {} \;

# /var/www とサブディレクトリのファイル許可を変更
$ find /var/www -type f -exec sudo chmod 0664 {} \;

2-5. composer自身のインストール

& curl -sS https://getcomposer.org/installer | php

# composerコマンドのパスを通す
$ sudo mv composer.phar /usr/local/bin/composer

# パス確認(/usr/local/bin/composerと表示されればOK)
$ which composer

2-6. Laravelプロジェクトをgit clone

sudo yum install git
cd /var/www
git clone [URL]

2-7. Apacheの設定変更

http.confファイルのDocumentRootの変更

Laravelでは、プロジェクト内のpublicディレクトリをドキュメントルートに指定する必要がある

$ sudo vi /etc/httpd/conf/httpd.conf

DocumentRoot “/var/www/html”の箇所をDocumentRoot “/var/www/プロジェクト名/public”に変更

http.confファイルに以下のDirectoryを追加(.htaccessの有効化)

<Directory /var/www/プロジェクト名/public>
 AllowOverride All
</Directory>

Apacheの設定変更後は、再起動を忘れずに!

$ sudo service httpd restart

2-8. パッケージのインストール

$ cd /var/www/[プロジェクトのフォルダ名]

# composer.jsonファイルがない場合
$ composer init

# vendorディレクトリが生成される
$ composer install

2-9. Laraveプロジェクトの設定

ディレクトリのパーミッション変更

Laravelでは、storage下とbootstrap/cacheディレクトリをWebサーバから書き込み可能にする必要がある

$ sudo chmod -R 777 /var/www/[プロジェクト名]/storage
$ sudo chmod -R 775 /var/www/[プロジェクト名]/bootstrap/cache

.envファイルの作成

cp .env.example .env

# .envファイルにある「APP_KEY」(アプリケーションキー)の生成
php artisan key:generate
APP_NAME=Laravel
APP_ENV=production
APP_KEY=base64:xxxxxxx(php artisan key:generateで生成される)
APP_DEBUG=false
APP_URL=http://[パブリックIP]

2-10. DBの設定、作成

MySQL側

# MySQL起動
$ sudo service mysqld start

# MySQLの初期設定
$ sudo mysql_secure_installation

# システムが再起動するたびにMySQLが起動するように設定
$ sudo chkconfig mysqld on

# データベース作成
$ mysql -u root -p
> 設定したパスワード入力
> create database [データベース名];
> show databases;
mysql_secure_installationコマンドで聞かれること
  • パスワード検証プラグインのセットアップ
  • パスワード
  • 匿名ユーザーアカウントの削除
  • rootユーザーのリモートログインの許否
  • testデータベースの削除

Laravel側

# 「DB_DATABASE」など、DB情報を設定しておく
$ sudo vi .env

DB_DATABASE=[作成したデータベース名]
DB_USERNAME=root
DB_PASSWORD=[設定したパスワード]
$ php artisan migrate
$ php artisan db:seed

参考にさせて頂いた記事

【Laravel】AWSにLaravelをインストールする
Laravel:EC2-PHP72-Apacheで環境を作成する
MySQL5.7 にて root のパスワード変更などの初期設定をする

General error: 1215 Cannot add foreign key constraint のエラー

エラー内容

外部キー制約設定の際に、
php aritisan migrate でこのようなエラーが..

Illuminate\Database\QueryException:SQLSTATE[HY000]:Generalerror:1215Cannotaddforeignkeyconstraint(SQL:altertable`files`addconstraint`files_user_id_foreign`foreignkey(`user_id`)references`users`(`id`))at/var/www/html/kaikei-laravel/vendor/laravel/framework/src/Illuminate/Database/Connection.php:665661|// If an exception occurs when attempting to run a query, we'll format the error662|// message to include the bindings with SQL, which will make this exception a663|// lot more helpful to the developer instead of just the database's errors.664|catch(Exception$e){>665|thrownewQueryException(666|$query,$this->prepareBindings($bindings),$e667|);668|}669|Exceptiontrace:1PDOException::("SQLSTATE[HY000]: General error: 1215 Cannot add foreign key constraint")/var/www/html/kaikei-laravel/vendor/laravel/framework/src/Illuminate/Database/Connection.php:4592PDOStatement::execute()/var/www/html/kaikei-laravel/vendor/laravel/framework/src/Illuminate/Database/Connection.php:459Pleaseusethe argument -v to see more details.

マイグレートファイルのコードはこれ。

2020_01_04_132245_add_user_id_to_files.php
classAddUserIdToFilesextendsMigration{/**
     * Run the migrations.
     *
     * @return void
     */publicfunctionup(){Schema::table('files',function(Blueprint$table){$table->bigInteger('user_id')->unsigned();$table->foreign('user_id')->references('id')->on('users');});}/**
     * Reverse the migrations.
     *
     * @return void
     */publicfunctiondown(){Schema::table('files',function(Blueprint$table){$table->dropColumn('user_id');});}}

いろいろ調べてみると、、
外部キーの参照元のuserテーブルのidがbigIncrementsだった。。
つまり、bigIncrementsとintegerで一致していない。
外部キーを設定する場合、どうやらデータ型が違うとだめらしい。

publicfunctionup(){Schema::create('users',function(Blueprint$table){$table->bigIncrements('id');$table->string('name');$table->string('email')->unique();$table->timestamp('email_verified_at')->nullable();$table->string('password');$table->rememberToken();$table->timestamps();});}

解決法

データ型を合わせるために外部キーに指定するuser_idのintegerをbigIntegerに修正してみる。
(bigIncrementsとbigIntegerで合わせるため)

2020_01_04_132245_add_user_id_to_files.php
classAddUserIdToFilesextendsMigration{/**
     * Run the migrations.
     *
     * @return void
     */publicfunctionup(){Schema::table('files',function(Blueprint$table){$table->bigInteger('user_id')->unsigned();$table->foreign('user_id')->references('id')->on('users');});}/**
     * Reverse the migrations.
     *
     * @return void
     */publicfunctiondown(){Schema::table('files',function(Blueprint$table){$table->dropColumn('user_id');});}}

php artisan migrate:fresh を実行してみるとできました!

# php artisan migrate:fresh
Dropped all tables successfully.
Migration table created successfully.

laravel5.8以降はphp artisan make:migration 〜で作成した場合、
idはbigIncrementsに自動的になってしまうのですね。

参考

https://laraveldaily.com/be-careful-laravel-5-8-added-bigincrements-as-defaults/
https://qiita.com/ktknj/items/6a3e1936da727dbd92b3

dockerを使わず簡単にローカルでLaravel5.8系が動く環境をセットアップする

はじめに

とある初心者の方向けにlaravel環境のセットアップ手順を起こそうとしたが、
dockerで詰まることが多いのでローカルのmacで直接実行できる環境を用意することにした。

必要な動作環境

  • ローカル環境はmacを使用
  • php7.1以上
  • sqlite3を使用

※上記がない場合はhomebrewを使ってインストールしておく

手順書

Laravel5.8系でプロジェクトを作成する

$ composer create-project --prefer-dist laravel/laravel [プロジェクト名] "5.8.*"

とりあえず動いているか動作確認する( http://127.0.0.1:8000/でLaravelが表示されてればOK )

$ cd [プロジェクト名]
$ php artisan serve

ここからはやり直しができるようにGitの初期設定を行っておくと良いだろう

$ git init
$ git add -A
$ git commit -m "Initial Commit"

Laravelでは以下の場所にsqlite用のファイルを作成するとデータベースの設定が完了する

$ touch database/database.sqlite

Laravelのデータベースをデフォルトのmysqlからsqliteへ変更

-DB_CONNECTION=mysql
-DB_HOST=127.0.0.1
-DB_PORT=3306
-DB_DATABASE=laravel
-DB_USERNAME=root
-DB_PASSWORD=
+DB_CONNECTION=sqlite

マイグレートで初回のテーブルを作成する

$ php artisan migrate

sqliteコマンドでテーブルが作成されたか確認をする

$ sqlite3 database/database.sqlite 

sqlite> .tables
migrations       password_resets  users 

※sqliteは .quit で終了できる( その他コマンドも詳しく知りたい場合は .help を参照 )

ついでにログイン機能を作成してみる

$ php artisan make:auth

http://127.0.0.1:8000/にアクセスするとトップ右上にログイン機能ができていることが分かる

参考にしたサイト

https://reffect.co.jp/laravel/laravel_sqlite

【PHP】リクエストセッションの値が取得出来ないことがあるパターン【Laravel】

同僚がハマっていたのでヘルプで入って見たらこうなってたよ っていうメモ

問題

コントローラーで保存したはずのリクエストセッションが取得出来ない。
(そもそも保存出来てない?)

コード

hoge.blade.php
@extends('base')@section('content')<form><inputtype="text"name="name"><inputtype="text"name="age"><buttontype="button"onclick="postNext()">Next</button>
</form>@endsection@section('js')<scripttype="text/javascript"src="js/hoge.js"></script>
@endsection

hoge.js
functionpostNext(){$('form').attr('action',postUrl);$('form').submit();}

色々と端折っているものの、正常に動く形です。

原因

chromeのdeveloper toolsでNextを押下した時のNetworkタブを確認していたら画面遷移直前でerrorのpostを発見。

継承しているbase.blade.phpの中に<form> ~ </form>が存在していた。
その状態でタグ指定でsubmitしていたため、正常なsubmitと不正なsubmitを同時に行っていた。
(成功するパターンもあったのが、ここら辺の判断をどうやっているかまでは追っていない)

修正

本来postされるべきformにIDを付けてJSファイルをタグ指定ではなく、ID指定でsubmitするようにした。

あとがき

同僚がなぜこのような実装したかまでは言及していない。(めちゃくちゃ切迫したスケジュールの中で頑張ってるので聞けなかった)
個人的にはjsでsubmit記述する必要があるのかな?とも思った。

submit前に処理を入れたかったとしても以下の形で良い気がする

$('#hoge_form').submit(function() {
  ~
});

あんまりこういう事はないと思うけど、一体験談として。

【AWS Cloud9 / Laravel 5.8】make:authして認証画面を作ったけどリンク先が反応しない

スクリーンショット 2020-01-13 1.03.27.png

こんにちは、にゃーんです。昨日Laravelを始めたばかりの者です。
たぶん初心者すぎて笑える内容です。どうか温かい目で見てあげてください。
間違いやまずい部分があれば訂正しますので、ご指摘あればお待ちしています。

トラブル内容

以下のサイトを参考にAWS Cloud9に登録して開発環境を整えた。

(バージョンはphp7.1、Laravelは5.8)

参考: AWS Cloud9 で Laravel を使ってみた。【環境構築】

次にデータベースを構築した。

参考: AWS Cloud9 で Laravel を使ってみる【データベース構築】

そのあとLaravelのドキュメント通りに認証機能をつけた。

スクリーンショット 2020-01-13 0.19.42.png

引用元: https://readouble.com/laravel/5.8/ja/authentication.html

そして何事もなく初期画面に"Login"と"Register"が表示された。

スクリーンショット 2020-01-13 0.14.23.png

と思ったら、

"LOGIN"をクリックしても全く反応がない。

調べまくってもみんなログイン画面にちゃんと飛んでいるのに、なぜか飛ばない。
エラーも出ないので、原因が分からなくてしばらく泣いた。

原因

route()で吐き出されるURIが"http:〜"になってたので、Cloud9のプレビューだとリンク先に飛ばない模様。

解決策

常時SSLになるように"app/Providers/AppServiceProvider.php"を書き換えたところ、とりあえずリンクが機能するようになった。

app/Providers/AppServiceProvider.php
useIlluminate\Routing\UrlGenerator;classAppServiceProviderextendsServiceProviderpublicfunctionboot(UrlGenerator$url){$url->forceScheme('https');}}

引用元: [Laravel]常時SSLなアプリケーションでのURL生成のベストプラクティスを考える

ご意見お待ちしています。

エンジニアになって日が浅いのでこれが正しいのか分からないところもあります。色々まずいよーってことがあればぜひお知らせください。

Laravel5.8で都道府県/市区町村データをAPIを使ってmigration/seederで一括紐付け登録

たまに必要になる都道府県と市区町村のマスタデータ登録

APIを使ってmigrationとseederだけで一括紐付け登録が出来るようにしました。

メリット

  • コピペで動く
  • APIで市区町村情報を取得するので実行するたびに最新情報ゲッツ
  • 都道府県コードをIDに変換してるが、都道府県コードをprimary_keyにすることも可能(こっちのが良いかも)

概要

都道府県と市区町村を紐づけてDBにマスタとして登録したかったのですが、
Excelが嫌いなのでプログラムで書くことにした。

やりたいこと

親テーブルの都道府県に子テーブルの市区町村を紐づけてマスタとして使いたかった。

Qiitaに都道府県とか一括で登録するライブラリ作ってる方がいましたが、
とりあえず今回は自作しました。
時間ない方はどうぞ
日本の住所関連のマスターデータをライブラリにしてみた

Migration作成

まずはMigrationから作っていきます。

ターミナルを開いてファイルを作成!

php artisan make:migation create_prefectures_table
php artisan make:migation create_cities_table

都道府県(prefectures)

とりあえずup()だけ

2020_01_01_123456_create_prerfectures_table.php
publicfunctionup(){Schema::create('prefectures',function(Blueprint$table){$table->increments('id');$table->string('name')->comment('都道府県名');});}

市区町(cities)

こっちもup()だけ
prefecturesのidとcitiesのprefecture_idで外部キーを設定してます。

2020_01_01_123456_create_cities_table.php
publicfunctionup(){$table->increments('id');$table->unsignedInteger('prefecture_id');$table->string('city_code')->comment('市区町村コード');$table->string('name')->comment('市区町村名');$table->index('prefecture_id');$table->index('name');$table->foreign('prefecture_id')->references('id')->on('prefectures')->onDelete('cascade')->onUpdate('cascade');}

外部APIを使うので必要なライブラリをインストール

全国地方公共団体というところが提供しているAPIに市区町村コードが入ったものがあったのでそれを使う。
外部APIなのでライブラリをインストールする

Guzzleをインストール

PHPで外部APIを叩けるモジュールであるGuzzleをインストール

ターミナルを開いて...

composer require guzzlehttp/guzzle

これで準備OK

Seederで登録

Seederファイルを作成しましょう。

またターミナル開いて...

php artisan make:seed PrefecturesTableSeeder
php artisan make:seed CitiesTableSeeder

都道府県(prefectures)

ここは数が少ないので配列で順次保存してマスタを作成

※重要 今回idを都道府県コードに見立てて使っていくので順番ずれたら終わりです。
下記の配列通りにしてください。

それか別で都道府県コード持ってもいいです

PrefecturesTableSeeder.php
<?phpuseIlluminate\Database\Seeder;classPrefecturesTableSeederextendsSeeder{private$prefectures=["北海道","青森県","岩手県","宮城県","秋田県","山形県","福島県","茨城県","栃木県","群馬県","埼玉県","千葉県","東京都","神奈川県","新潟県","富山県","石川県","福井県","山梨県","長野県","岐阜県","静岡県","愛知県","三重県","滋賀県","京都府","大阪府","兵庫県","奈良県","和歌山県","鳥取県","島根県","岡山県","広島県","山口県","徳島県","香川県","愛媛県","高知県","福岡県","佐賀県","長崎県","熊本県","大分県","宮崎県","鹿児島県","沖縄県"];/**
     * Run the database seeds.
     *
     * @return void
     */publicfunctionrun(){foreach($this->prefecturesas$prefecture){DB::table("prefectures")->insert(["name"=>$prefecture]);}}}

市区町(cities)

本題の市区町村マスタの登録です。

CitiesTableSeeder.php
<?phpuseIlluminate\Database\Seeder;useGuzzleHttp\Client;useApp\Prefecture;useApp\City;classCitiesTableSeederextendsSeeder{/**
     * Run the database seeds.
     *
     * @return void
     */publicfunctionrun(Client$client,Prefecture$prefecture,City$city){// 都道府県取得$prefectures=$prefecture->all();foreach($prefectures->pluck('id')as$prefecture_id){// 外部API全国地方公共団体コード$api='https://www.land.mlit.go.jp/webland/api/CitySearch?area='.str_pad($prefecture_id,2,0,STR_PAD_LEFT);$respone_datas=$client->request('GET',$api);$respone_bodys=json_decode($respone_datas->getBody()->getContents(),true);// APIのステータスがOKなら実行if($respone_bodys['status']==='OK'){foreach($respone_bodys['data']as$respone_body){// 都道府県の外部キーを指定して市区町村を登録$city->create(['prefecture_id'=>$prefecture_id,'city_code'=>$respone_body['id'],'name'=>$respone_body['name']]);}}}}}

あとはDatabasesSeederに登録

DatabasesSeeder.php
<?phpuseIlluminate\Database\Seeder;classDatabaseSeederextendsSeeder{/**
     * Seed the application's database.
     *
     * @return void
     */publicfunctionrun(){$this->call([PrefecturesTableSeeder::class,CitiesTableSeeder::class]);}}

またターミナルを開いてmigrationとseederを実行!!

php artisan migrate --seed

ちなみにテストはしてないので悪しからず
終わり

laravel マイグレート、シード、MVC 備忘録

ファクトリーファイル役割

カラムに挿入する具体的なデータを定義する。

シードファイル役割

ファクトリーで作成したデータの何件DBに挿入するかを決める

DatabaseSeeder.phpファイルの役割

どのデータをシードするかを決める。

モデルファイルのシードでの役割

DBと接続したり、コントローラー使うスコープをここで、定義したりする。
LaravelのModelには命名規則がある。

例えば、テーブル名がusersの場合、Model名はUserになります。

テーブル名を単数形にしたものがModel名となり、自動的にテーブルを操作することができるようになる。

この命名規則は絶対的なものではなく、テーブル名を明示的に指定することもできる。
参考: https://udemy.benesse.co.jp/development/web/laravel-model.html


database/seeds/DatabaseSeeder.php

<?phpuseIlluminate\Database\Seeder;classDatabaseSeederextendsSeeder{publicfunctionrun(){// $this->call(UsersTableSeeder::class);$this->call(BookmarksTableSeeder::class);//★ここ}}

database/seeds/BookmarksTableSeeder.php

<?phpuseIlluminate\Database\Seeder;classBookmarksTableSeederextendsSeeder{/**
   * Run the database seeds.
   *
   * @return void
   */publicfunctionrun(){factory(App\Models\Bookmark::class,100)->create();//ここ★}}

DBとカラムを作成し、

php artisan migrate

上記4つをやって、シードコマンドを実行するとカラムにデータが保存される。

php artisan db:seed

おまけ

php artisan make:model Models/Bookmark -a

このコマンドでモデルとファクトリー、テーブルが同時に作成される
テーブルは複数形になっている。

Laravelで独自のバリデーションを定義する

カスタムバリデーションを登録する方法

大きくわけて3パターン

  • 1回しか使わない
    • クロージャー
  • 頻繁には使わないけど、ある部分で必要
    • ルールオブジェクト
  • サイト全体で共通のものとして使いたい
    • Validatorを拡張する

ルールオブジェクトとValidatorを拡張するパターンの明確な使い分けがよくわかってない。
ルールオブジェクトの場合はnewで都度呼び出さないといけないので、使用する頻度で分ければ良い?
毎回必要なものでなければルールオブジェクト
頻繁に使うものはValidatorを拡張する

今回はValidatorを使った方法を記載する。

実装

サービスプロバイダーを作成する

php artisan make:provider ValidatorServiceProvider

サービスプロバイダーを登録する

config/app.php
/*
* Application Service Providers...
*/App\Providers\ValidatorServiceProvider::class,

処理を書く

App\Providers\ValidatorServiceProvider.php
<?phpnamespaceApp\Providers;useIlluminate\Support\ServiceProvider;useValidator;classValidatorServiceProviderextendsServiceProvider{/**
     * Register services.
     *
     * @return void
     */publicfunctionregister(){//}/**
     * Bootstrap services.
     *
     * @return void
     */publicfunctionboot(){/**
     * 郵便番号 ハイフンありなしのバリデーション
     *
     * @return bool
     */Validator::extend('zipcode',function($attribute,$value,$parameters,$validator){returnpreg_match('/^[0-9]{3}-?[0-9]{4}$/',$value);});}}

以上で準備完了。

後はformRequest側で呼び出してあげるだけ

formRequest.php
publicfunctionrules():array{return['zipcode'=>'zipcode',];}

バリデーションメッセージを日本語にする場合は言語ファイルに設定しよう。

resource/lang/ja/validation.php
//エラーメッセージ'zipcode'=>':attributeは郵便番号の形式では入力してください'//属性'attributes'=>['zipcode'=>'郵便番号'],

ルールオブジェクトを使う場合

ルールオブジェクトの作成

php artisan make:rule ZipCodeRule
App/Rules/ZipCodeRule.php
<?phpnamespaceApp\Rules;useIlluminate\Contracts\Validation\Rule;classZipCodeRuleimplementsRule{/**
     * バリデーションの成功を判定
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
     */publicfunctionpasses($attribute,$value){returnpreg_match('/^[0-9]{3}-?[0-9]{4}$/',$value);}/**
     * バリデーションエラーメッセージの取得
     *
     * @return string
     */publicfunctionmessage(){// return 'validation.zipcode';//翻訳ファイルからメッセージを取得する場合return':attributeは郵便番号の形式では入力してください';}}

formRequest側で呼び出す時はこんな感じ。
配列にして書かないといけないので注意。

formRequest.php
publicfunctionrules():array{return['zipcode'=>['required',newZipCodeRule],];}

Laravel ソースコードリーディング サービスコンテナ編 part.1 - bindとmake、buildとresolve -

いい加減サービスコンテナをよく知らずに使うのはよくないな〜ということでLaravelコアのコードを読みつつ、記事を書いて投げてみました。同じようなLaravel初心者の方の参考になれば幸い...

ただ、僕もまだまだ未熟ですので、意図の読み違いなどは普通にあり得ると思います。間違いなどありましたらご指摘くだされば幸いです:pray:

基礎知識: サービスコンテナ

DIコンテナとも。Dependency Injection パターンと併用して、依存性を1箇所に集める。
Laravelアプリケーション内における様々なインスタンスの生成を担う。Laravelでは文字通り、これがないとアプリケーションが始まらない。Laravelのコアと言って良い。
FQCNは Illuminate\Foundation\Applicationクラスで、Laravel中ではよく$appというプロパティとして現れる。
Illuminate\Foundation\ApplicationクラスはIlluminate\Container\Containerクラスを継承しており、コンテナの結合処理などを担うのはこちらのクラスなので、最初はこっちを見ることになる。

用語

結合(bind)

キーと解決処理をペアでコンテナに登録すること。
結合にはbindsingletoninstanceなどのメソッドを使う。

呼び出し時に使用されるキーには大抵FQCNが使用されるが、極端な話文字列であればなんでも良い。ファサードの場合はわかりやすく短い文字列が使われやすい(DBファサードならdbとか)。

具象クラスの場合、Laravelは勝手にそのクラスを探し出しインスタンス化してくれるので、結合処理を書く必要性は特にない(はず)。言い換えると、後述の処理内容を見ればわかりますが、コンテナに結合されていないキーで問い合わせた場合、そのキーと一致するクラスを探し、インスタンスを生成しようとする処理が走ることになる。

結合処理は大抵、サービスプロバイダ内で行われる。

解決(resolve)

結合されたインスタンス化の方法を元に、インスタンスを生成すること。
明示的な解決には基本的にmakeメソッドを使用する。

実際の処理

doc以外のコメントは省略しています。

buildによる具象クラスの解決処理

順序的にはまずbuildメソッドの、実際に与えられたクラス名からインスタンスを生成する役割を理解しておくと良さそう。ReflectionClassReflectionParameterに関してはわからない方はPHPマニュアルを参照してください。

Container.php
/**
     * Instantiate a concrete instance of the given type.
     *
     * @param  string  $concrete
     * @return mixed
     *
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
     */publicfunctionbuild($concrete){// $concreteがクロージャなら、実行してその結果を返す。if($concreteinstanceofClosure){return$concrete($this,$this->getLastParameterOverride());}try{$reflector=newReflectionClass($concrete);}catch(ReflectionException$e){thrownewBindingResolutionException("Target class [$concrete] does not exist.",0,$e);}if(!$reflector->isInstantiable()){return$this->notInstantiable($concrete);}$this->buildStack[]=$concrete;$constructor=$reflector->getConstructor();if(is_null($constructor)){array_pop($this->buildStack);returnnew$concrete;}$dependencies=$constructor->getParameters();try{$instances=$this->resolveDependencies($dependencies);}catch(BindingResolutionException$e){array_pop($this->buildStack);throw$e;}array_pop($this->buildStack);return$reflector->newInstanceArgs($instances);}

重要なのは、リフレクションを使った処理以降の部分。後述するbindの処理と合わせて理解して欲しい部分ですが、buildメソッドの$concreteには、具象クラスのFQCNか、解決方法となるクロージャが渡されることが想定されている。
よって、まずインスタンス化できない( = abstract class or interface)場合、notInstantiableメソッドでBindingResolutionExceptionを投げる。

具象クラスのFQCNが渡されている(インスタンス化が可能である)場合、$buildStackの末尾に$concreteをコピーしたのち、$concreteのコンストラクタを取得する。

ここでコンストラクタが取得できない場合(=定義されていない場合)、Laravelでは他のクラスに依存していないことを意味するようで(Laravelコアではコンストラクタインジェクションを使う前提なのですね)、そのまま$concreteをインスタンス化して返却する。

コンストラクタが取得できた場合、他のクラスへの依存関係をもっていると判断し、コンストラクタの引数を配列として取得、それをresolveDependenciesメソッドの引数に与え実行、その結果依存関係をもつクラスのインスタンス群である$instancesが返される。

resolveDependenciesメソッドの実装はコンテキストによる結合とかも絡んできて長くなるので今回はばっさり省略したいところですが、その処理の途中で、引数のタイプヒントがクラスだった場合、そのインスタンス化を行うresolveClassメソッドの中でmakeメソッドが呼ばれるということだけは、後述の処理の関係もあり押さえておいた方が良い。

Container.php
/**
     * Resolve a class based dependency from the container.
     *
     * @param  \ReflectionParameter  $parameter
     * @return mixed
     *
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
     */protectedfunctionresolveClass(ReflectionParameter$parameter){try{return$this->make($parameter->getClass()->name);}catch(BindingResolutionException$e){if($parameter->isOptional()){return$parameter->getDefaultValue();}throw$e;}}

最後に、その$instancesを引数として与えた上で、ReflectionClass::newInstanceArgsメソッドを実行。結果として、$concreteに指定されたクラスのインスタンスが依存性を解決した上で生成、返却され、buildメソッドの処理が終了する。

bind, makeによる結合と解決

bindによる結合

結合の基本となるbindの処理から。

Container.php
/**
     * Register a binding with the container.
     *
     * @param  string  $abstract
     * @param  \Closure|string|null  $concrete
     * @param  bool  $shared
     * @return void
     */publicfunctionbind($abstract,$concrete=null,$shared=false){$this->dropStaleInstances($abstract);if(is_null($concrete)){$concrete=$abstract;}if(!$concreteinstanceofClosure){$concrete=$this->getClosure($abstract,$concrete);}$this->bindings[$abstract]=compact('concrete','shared');if($this->resolved($abstract)){$this->rebound($abstract);}}

docを見るとわかる通り、$concreteには、クロージャか文字列が渡されることが想定されている。クロージャなら解決方法となるインスタンスの生成処理、文字列ならFQCNだと思って良い。

bindの処理の順序としては、まずdropStaleInstancesメソッドで同じキーで登録されているインスタンスとエイリアスをunsetする。

Container.php
/**
     * Drop all of the stale instances and aliases.
     *
     * @param  string  $abstract
     * @return void
     */protectedfunctiondropStaleInstances($abstract){unset($this->instances[$abstract],$this->aliases[$abstract]);}

もし$concreteに何も引数が渡されなかった場合、デフォルトのnullが使用されることになり、$concreteには$abstractの文字列がそのままコピーされる。
(この$abstractに抽象クラスのFQCNを渡しておきながら$concreteに何の解決方法も渡さなかった場合、 この処理が走った上でbuildに処理が移るため、例外が投げられてしまう。)

次に、$concreteがクラス名の場合、getClosureメソッドを使ってインスタンスを生成するためのクロージャを作成する。

Container.php
/**
     * Get the Closure to be used when building a type.
     *
     * @param  string  $abstract
     * @param  string  $concrete
     * @return \Closure
     */protectedfunctiongetClosure($abstract,$concrete){returnfunction($container,$parameters=[])use($abstract,$concrete){if($abstract==$concrete){return$container->build($concrete);}return$container->resolve($concrete,$parameters,$raiseEvents=false);};}

その後、compactで配列を作成し、$bindingsプロパティに格納する。
compactはPHPの標準関数であり、変数名とその値から連想配列を作成する。

makeによる解決

解決処理に使われるmakeメソッドは、resolveメソッドを呼んでその結果を返しているだけ。

Container.php
/**
     * Resolve the given type from the container.
     *
     * @param  string  $abstract
     * @param  array  $parameters
     * @return mixed
     *
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
     */publicfunctionmake($abstract,array$parameters=[]){return$this->resolve($abstract,$parameters);}

resolveによる抽象クラスの解決処理

前述の通り、抽象クラスの場合はbuildメソッドによるインスタンス化はできない。
そこで解決方法をクロージャとして用意し、resolveメソッドの内部でそれを取り出し、改めてbuildに処理を委譲し、インスタンスを返却する。

Container.php
/**
     * Resolve the given type from the container.
     *
     * @param  string  $abstract
     * @param  array  $parameters
     * @param  bool  $raiseEvents
     * @return mixed
     *
     * @throws \Illuminate\Contracts\Container\BindingResolutionException
     */protectedfunctionresolve($abstract,$parameters=[],$raiseEvents=true){$abstract=$this->getAlias($abstract);$needsContextualBuild=!empty($parameters)||!is_null($this->getContextualConcrete($abstract));if(isset($this->instances[$abstract])&&!$needsContextualBuild){return$this->instances[$abstract];}$this->with[]=$parameters;$concrete=$this->getConcrete($abstract);if($this->isBuildable($concrete,$abstract)){$object=$this->build($concrete);}else{$object=$this->make($concrete);}foreach($this->getExtenders($abstract)as$extender){$object=$extender($object,$this);}if($this->isShared($abstract)&&!$needsContextualBuild){$this->instances[$abstract]=$object;}if($raiseEvents){$this->fireResolvingCallbacks($abstract,$object);}$this->resolved[$abstract]=true;array_pop($this->with);return$object;}

やりすぎなぐらいなんか色々やってますが、ここで重要と思われるのは真ん中辺り以降の処理。
まず$parameters$withプロパティに一旦コピーした後、getConcreteメソッドで解決方法となるクロージャを取得する。結合が存在しない場合は$abstractが具象クラスのFQCNであると判断し、そのまま返却する。

Container.php
/**
     * Get the concrete type for a given abstract.
     *
     * @param  string  $abstract
     * @return mixed   $concrete
     */protectedfunctiongetConcrete($abstract){if(!is_null($concrete=$this->getContextualConcrete($abstract))){return$concrete;}// 結合されたクロージャを取り出す。if(isset($this->bindings[$abstract])){return$this->bindings[$abstract]['concrete'];}// bindされていない場合、そのまま $abstract を返す。return$abstract;}

そして、isBuildableメソッドを呼び、インスタンス化をbuildメソッド、makeメソッドのどちらに任せるかを決定する。$concrete$abstractが同じ文字列であるか、$concreteがクロージャである場合、buildメソッドに処理が移り、インスタンス化が行われる。

Container.php
/**
     * Determine if the given concrete is buildable.
     *
     * @param  mixed  $concrete
     * @param  string  $abstract
     * @return bool
     */protectedfunctionisBuildable($concrete,$abstract){return$concrete===$abstract||$concreteinstanceofClosure;}

そしてそのどちらでもなかった場合、makeメソッドに処理が移り、その中で再度resolveメソッドが呼ばれる。つまり、「ネストした依存」をすべてbuildメソッドで解決できるまで、この処理は再帰的に繰り返されることになる。

「ネストした依存」の解決の一例を示すものとしてわかりやすいのが下記のテストケース。
関係する抽象クラスの解決方法を用意し、コンストラクタインジェクションを使用しておけば、依存先のクラスも含めてすべて依存を解決した上でインスタンス化してくれることがわかる。

tests/Container/ContainerTest.php
usePHPUnit\Framework\Testcase;classContainerTestextendsTestCase{// ...publicfunctiontestNestedDependencyResolution(){$container=newContainer;$container->bind(IContainerContractStub::class,ContainerImplementationStub::class);$class=$container->make(ContainerNestedDependentStub::class);$this->assertInstanceOf(ContainerDependentStub::class,$class->inner);$this->assertInstanceOf(ContainerImplementationStub::class,$class->inner->impl);}// ...}interfaceIContainerContractStub{//}classContainerImplementationStubimplementsIContainerContractStub{//}classContainerImplementationStubTwoimplementsIContainerContractStub{//}classContainerDependentStub{public$impl;publicfunction__construct(IContainerContractStub$impl){$this->impl=$impl;}}classContainerNestedDependentStub{public$inner;publicfunction__construct(ContainerDependentStub$inner){$this->inner=$inner;}}

一旦ここまで。
タグとエイリアス、拡張、コンテキストによる結合などは次回以降(があれば)触れたいと思います。
Laravelコアむずかしい。。。

漏れなく入力チェックするためのLaravelバリデーションの使い方まとめ

Laravelのバリデーション機能は多様なチェック機能がデフォルトで備わっておりとても便利です。
ですが、実際に使用していく中で入力チェックの内容に漏れがあったり抜け穴があったりと気を付けなければならない点がありましたので、バリデーションの使い方とともにまとめました。
今回まとめた内容の動作の確認はLaravel5.8にて行っております。

バリデーション機能の使い方

ここではLaravelバリデーションを使用するうえで作成するファイルや設定する箇所等についてまとめております。

バリデーション言語ファイルの設定

バリデーション言語ファイルはバリデーションエラー時のメッセージを定義するファイルになります。
バリデーションエラーメッセージは後で設定するフォームリクエストバリデーションでも設定が可能ですが、多言語化対応を行うとなった場合に対応しやすいようこのファイルで設定するのがベストです。
このファイルを作成しなくてもバリデーションチェックはできますので、使用しないのであれば読み飛ばしてください。

ファイル作成

resources\lang配下に言語ごとのディレクトリを作成し、その中にvalidation.phpを作成します。
日本語であればresources\lang\ja\validation.phpを作成します。
バリデーション言語ファイルはこちらの日本語翻訳をもとにアプリケーションに合わせて文言を設定するとよいです。
(バージョン6のものですが、5.8のものだとルールが不足しているのでこちらを提示しています)
このファイルを作成した状態でconfig/app.phpのlocaleの設定をjaに設定するとエラー時に表示されるようになります。
また、リクエストに応じて言語設定する場合はApp::setLocaleで設定した言語に応じたメッセージが表示されます。

app.php
'locale'=>'ja',

バリデーションメッセージ設定

バリデーション言語行の設定では特定のバリデーションルールでエラーになった場合に表示するメッセージを設定します。
以下はdigitsのメッセージ設定の例になります。

validation.php
'digits'=>':attributeは:digits桁で指定してください。',

上記の「:attribute」には表示する際にチェックする項目の属性名が入ります。
また、「:digits」にはバリデーションチェックの指定時の引数値が入ります。
引数名はバリデーションルールによって異なりますので、resources\lang\en\validation.phpなどを参考に作成します。

形式によって変わるsize系のバリデーションルールは以下のように配列で数値(numeric)、ファイル(file)、文字列(string)、配列(array)の形式ごとにメッセージを指定します。

validation.php
'between'=>['numeric'=>':attributeは:min~:maxで指定してください。','file'=>':attributeは:min~:max kBのファイルで指定してください。','string'=>':attributeは:min~:max文字で指定してください。','array'=>':attributeは:min~:max個で指定してください。',],

カスタムバリデーションメッセージ設定

カスタムバリデーション言語行の設定では特定の属性名の特定のバリデーションルールを使用した際のメッセージを指定します。
例えば、以下のように設定するとupdated_atでexistsのエラーとなった場合に指定したメッセージが表示されるようになります。
バリデーションメッセージ設定と同様に:attributeなどの引数も使用できます。

'custom'=>['updated_at'=>['exists'=>'データが更新されています。はじめから操作をやり直してください。',],],

カスタムバリデーション属性名設定

カスタムバリデーション属性名では:attributeに設定される名前を指定することができます。
例えば、以下のように設定すると、設定しない場合メッセージの:attributeにはemailと設定されるところ、メールアドレスと設定されるようになります。

validation.php
'attributes'=>['email'=>'メールアドレス',],

カスタム値設定

バリデーションで指定する引数の値をメッセージに表示する際に別の値に置き換えることができます。
例えば、以下のように設定すると、:valuesに「クレジットカード」が設定されるようになり、card_noの項目が未入力の場合に「お支払いがクレジットカードの場合、カード番号も指定してください。」となります。

validation.php
'required_if'=>':otherが:valueの場合、:attributeも指定してください。','attributes'=>['card_no'=>'カード番号','payment_type'=>'お支払い',],'values'=>['payment_type'=>['1'=>'クレジットカード'],],
フォームリクエストバリデーションファイル
'card_no'=>'nullable|required_if:payment_type,1|numeric|digits_between:14,16',

バリデーションチェックの設定

バリデーションチェックを行う方法としてはRequestオブジェクトのvalidateメソッドを使う方法とフォームリクエストバリデーションを使用する方法があります。
フォームリクエストバリデーションの方が複雑なチェックを行うことができ、ソースコードもきれいにできるので、フォームリクエストバリデーションでチェックを行うことをおススメします。
そのため、Requestオブジェクトのvalidateメソッドを使う方法については割愛しますが、後に紹介するrulesメソッドとルールの設定方法は基本的に同じとなります。

フォームリクエストバリデーション

フォームリクエストバリデーションはFormRequestクラスを継承したクラスにバリデーションチェックルールを定義し、コントローラーのメソッドの引数のクラスに指定して使用します。
コントローラーの処理が呼ばれる前にバリデーションチェックが行われ、エラーがあった場合はメソッドの処理は行われず自動的に前のページヘリダイレクトするようになっています。

FormRequestファイル作成

作成は「php artisan make:request クラス名」のコマンドで作成します。
(この方法で作成しなくても問題はありません)
app/Http/Request配下に作成されますが、ディレクトリ分けを行う場合はディレクトリも指定します
以下のコマンドを実行するとAdminディレクトリ配下にProductRequest.phpを作成します。

php artisan make:request Admin/ProductRequest

authorizeメソッド

このメソッドではboolean型の値を返し、ユーザーがこのリクエストを行える権限を持っているかを判断するメソッドです。
falseが返るとリクエストが403 Forbiddenとなります。
この処理で権限のチェックを行う必要がない場合はtrueを返すかメソッド自体を実装しないようにしてください。

rulesメソッド

このメソッドではルールを指定した連想配列を返します。

基本は以下のようにチェックする項目名に対してルールごとにパイプ分け書き方になるかと思います。

フォームリクエストバリデーションファイル
return['id'=>'nullable|exists:products,id,deleted_at,NULL','tel'=>'required|numeric|digits_between:10,11',];

regexでパイプを使用する場合やRuleクラスを使用する場合はパイプ分けを行わず、配列で指定します。

フォームリクエストバリデーションファイル
return['id'=>['required','integer',Rule::exists('t_goods','id')->where(function($query){$query->where('type',$this->type);$query->where('deleted_at',NULL);})],'sort_by'=>['required','string','regex:/\A(created_at|amount|price|type)\z/u',],];

配列の中身に対してバリデーションチェックを行う場合は下記のようにドット記法で対象を指定します。
下記は「images[id]」のように指定されたパラメータをチェックする方法になります。

フォームリクエストバリデーションファイル
return['images'=>'nullable|array''images.id'=>'required|image',];

配列の全ての中身に対してバリデーションチェックを行う場合は下記のように「*」を指定します。
下記は「images[]」もしくは「images[0]」のように指定されたパラメータをチェックする方法になります。

フォームリクエストバリデーションファイル
return['images'=>'nullable|array''images.*'=>'required|image',];

連想配列の中身に対してバリデーションチェックを行う場合は下記のように「*」の後に属性名を指定します。
下記は「images[][id]」もしくは「images[0][id]」のように指定されたパラメータをチェックする方法になります。

フォームリクエストバリデーションファイル
return['images'=>'nullable|array''images.*.id'=>'required|distinct|exists:t_images,id,deleted_at,NULL'];

$this->属性名でリクエストの値を取得できるので、送られてきた値によってチェックする内容を変更することも可能です。
(この方法では配列中の値ごとに配列のチェックを変えるといったことはできません)
詳細は後ほど説明しますが、リクエストの種類が複数あるような場合は以下のように種類ごとにチェックルールの指定を変えることで他の種類で扱うデータをコントローラーで扱わなくて済みます。

フォームリクエストバリデーションファイル
$rules=['payment_type'=>'required|integer|between:1,5',];switch($this->type){case1:$rules+=array('card_no'=>'required|numeric|digits_between:14,16',);break;case2:$rules+=array('bank_code'=>'required|numeric|digits:4',);break;}return$rules;

messagesメソッド

このメソッドはメッセージのカスタマイズが必要な場合に実装します。
validation.phpファイルに定義されている場合でもこちらで定義したルールが優先で反映されます。
多言語対応などでvalidation.phpファイルでエラーメッセージを一元管理する場合は実装しないようにします。
このメソッドでは特定のエラーの際のメッセージを指定した配列を返します。
配列の添え字に指定した条件に当てはまる場合に値に設定したメッセージが表示されるようになります。
添え字には基本「ruleで指定した属性名.ルール名」になりますが、属性名のみ、ルール名のみも可能です。

フォームリクエストバリデーションファイル
return['digits_between'=>':attributeは数字:max桁以下にしてください。','phone.digits_between'=>':attributeは数字:min桁以上:max桁以下にしてください。','phone'=>':attributeが正しくありません。','images.*.id'=>':attributeが不正です。''images.*.id.required'=>':attributeは必須です。'];

attributesメソッド

バリデーションメッセージの:attributeに設定される名前を指定することができます。
validation.phpファイルに定義されている場合でもこちらで定義したルールが優先で反映されます。
多言語対応などでvalidation.phpファイルで属性名を一元管理する場合は実装しないようにします。
このメソッドでは属性名に対するカスタム属性名を指定した配列を返します。
添え字の指定の仕方はrulesメソッドと同様です。

フォームリクエストバリデーションファイル
return['goods_cd'=>'商品コード',];

withValidatorメソッド

フォームリクエストでバリデーション実行前のバリデータに対して追加の操作を行う場合に実装します。
特定の入力の際にチェックを追加するsometimesメソッドや、バリデーションルールでできないチェックを行うためのafterフックを追加できます。
ただし、エラーメッセージを直接指定する必要がありますので、多言語化対応が必要な場合はApp::isLocaleの判定で言語ごとにメッセージを定義するか、messages.phpで定義したメッセージを指定することになります。

フォームリクエストバリデーションファイル
publicfunctionwithValidator($validator){// from_dateが入力されている場合のみto_dateとの関係をチェック$validator->sometimes('to_date','after_or_equal:from_date',function(){return!empty($this->from_date);});$validator->after(function($validator){// fieldにバリデーションエラーがない場合のみチェックif(!$validator->errors()->has('field')){if(!isset($this->field)){$validator->errors()->add('field','フィールドが不正です。');return;}}});}

failedValidationメソッド

通常のHTTPリクエストの場合はリダイレクトレスポンス(リクエスト時の画面に戻ってエラーメッセージ表示)が生成され、
AJAXリクエスト時はJSONレスポンスが返されますが、それ以外の形式で失敗時のレスポンスを指定したい場合はこのメソッドを実装します。
下記はエラーをJSON形式でレスポンスする例になります。

フォームリクエストバリデーションファイル
protectedfunctionfailedValidation(Validator$validator){$response['data']=[];$response['status']='NG';$response['summary']='Failed validation.';$response['errors']=$validator->errors()->toArray();thrownewHttpResponseException(response()->json($response,422));}

validationData(protected)メソッド

バリデーションのチェックを行う前に値に何かしらの処理を行いたい場合に実装します。
例えば、年月日が別々に送られてくる場合にこれらを結合した上で存在する日付かをバリデーションチェックで行いたいといったケースで使えます。
validationDataメソッドはバリデーションする対象となるデータを返すメソッドで、スーパークラスでは\$this->all()をそのまま返しています。
これを継承してリクエストの内容を編集することができます。
このメソッドで編集した内容をコントローラ側で取得するには\$request->validated()で取得します。

フォームリクエストバリデーションファイル
protectedfunctionvalidationData(){$data=$this->all();if(isset($data['phone'])){$data['phone']=str_replace("-","",$data['phone']);}return$data;}

FormRequestコントローラ設定

コントローラのリクエストを受け取るメソッドのリクエスト引数のクラスにFormRequestのクラスを指定することでバリデーションが実行されます。
バリデーションに成功した場合のみコントローラのメソッドの処理が呼び出されます。
バリデーションに失敗した場合はリダイレクトレスポンスとなり、AJAXの場合はエラーJSONでエラー内容が返されます。
リクエストの値は\$request->idの形で取得できますが、\$requestには想定していないパラメータも格納されるので注意が必要です。
\$request->validated()でデータを取得することでバリデーションチェックを行った項目のみを取得できるので、不要なデータを扱わずに済みます。
ですので、コントローラでは\$requestから直接データを取得せず\$request->validated()で取得したデータ配列から常に取得することを推奨します。
ただし、配列内の項目についてはチェックしていない項目でも配列の中に入ったままになるので注意が必要です。

コントローラ
publicfunctionstore(ProductRequest$request){// バリデーション済みデータの取得$validated=$request->validated();$id=$validated['id'];}

エラーメッセージ設定

バリデーションチェックでエラーとなった場合にそのメッセージをBladeテンプレートで表示する際の設定になります。
下記のようにanyメソッドでエラーがあるかを判断できます。
全エラーメッセージを一箇所で表示するのであればallメソッドで全エラーメッセージを配列で取得し表示することもできます。

Bladeテンプレート
@if($errors->any())<divclass="alert alert-danger">入力内容に不備がありますエラー内容をご確認ください</div>@endif

下記は指定フィールドにhasメソッドでエラーがあるかを判定し、ある場合にエラーメッセージをfirstメソッドで1件表示する設定になります。
指定フィールドの全エラーメッセージを表示するのであればgetメソッドでエラーメッセージを配列で取得し表示することもできます。
配列形式のフィールドのエラーメッセージはバリデーションの指定時と同じように引数に「*」を使用すれば対象のフィールドのエラーメッセージを取得できます。
配列のエラーメッセージを個別に取得したい場合は「images.0.id」のように指定すれば取得できます。

Bladeテンプレート
@if($errors->has('id'))<smallclass="form-text text-danger">{{$errors->first('id')}}</small>@endif

バリデーション使用する際のノウハウ

ここからはバリデーションチェックの実装を行ってきた中で得たノウハウをまとめております。

バリデーション設定時に考慮すべきこと

Laravelのバリデーションで漏れなく入力チェックを行うために必ずルール設定でやるべきこととして次の3点挙げておきます。

送られてくるパラメータは必ずチェック項目として設定する

FormRequestでバリデーションチェックを行う場合は必ずすべてのリクエスト内容に対してルールを設定します。
前章で記述の通り、ルールを設定しないと\$request->validated()で取得できないからです。
また、ルールを設定していない項目は\$request->validated()で扱われないようにできるので、リクエストの内容をそのままEloquentでデータベースのテーブルを登録・更新するような場合は意図しない項目の変更を防ぐことができます。
(配列項目の場合は想定しない項目もそのまま入ってくるので別途対応が必要です)

必ずrequiredかnullableを最初に指定する

バリデーションチェックを行う項目は必ず以下のいずれかのチェックを最初に入れて下さい。
こちらの記事で確認されているように、チェックルールによっては想定外の動きをする場合があるので、指定を明確にします。

ルール動作
requiredデータがなければエラー
filledフィールドが存在する場合にデータがなければエラー
presentフィールドが存在しなければエラー
nullablenullでもエラーにならない

基本的には必須の場合にrequired、そうでない場合はnullableを指定すれば問題ないです。
required_ifなどの条件必須ルールを使用する場合もnullableを必ず最初に指定してください。

フォームリクエストバリデーションファイル
'card_no'=>'nullable|required_if:payment_type,1|numeric|digits_between:14,16',

必ず型となるルールを指定する

そのリクエストの型を決めるルールを必ず指定してください。
数値であればintegerかnumelic、日付であればdateかdate_format、ファイルであればfileやimage、配列であればarray、それ以外の文字列であればstringを指定します。
フィールドが配列で送られてきた場合にエラーとなるようにするためです。
特に自由な入力を行うような項目の場合、下記のようにstringの設定を入れ忘れがちですが、この設定だとフィールド名がcomment[]と配列でデータが送られてきた場合にエラーとならないので必ず指定しましょう。

NG設定例
'comment'=>'required|max:200',

digitsなどもintegerなどを指定しなくても非数値でエラーとなりますが、配列の指定で送られるとエラーにならないので、numeric等を指定しましょう。
また、size、min、max、between、gt、gte、lt、lteは型によって動作が違うので注意してください。

バリデーションルール例

ここでは入力項目の形式や型に応じたバリデーションルールの設定例を挙げております。

ID系

更新などの際に使用するIDはexistsでテーブルへの存在チェックを行います。
Ruleクラスのexistsメソッドを使えば複雑な存在チェックが可能ですが、existsのルールは以下の形式で指定できるので大半はexistsメソッドを使わないで済む場合がほとんどだと思います。

exists:テーブル名,対象カラム名[,条件カラム,条件値,...]

以下のように基本削除されていないことも確認します。
(削除の場合は削除チェックせずあえてスルーしてもよいかと思います)

フォームリクエストバリデーションファイル
'id'=>'required|exists:t_reports,id,deleted_at,NULL',

区分値

ラジオボタン等の区分値は以下のように想定内の範囲であることを確認します。

フォームリクエストバリデーションファイル
'type'=>'required|integer|between:1,5',

booleanやacceptedは文字列、数値、boolean型と異なる型でチェックを通すので、これらを使う場合は受け入れられる値全てで問題がないことを確認しておく必要があります。

テキスト

基本的なテキスト入力の項目は以下のようになるかと思います。
stringを指定している場合のsize、min、max、betweenの指定は文字数となります。

フォームリクエストバリデーションファイル
'comment'=>'nullable|string|max:200',

alpha、alpha_dash、alpha_numはアルファベット以外の日本語などの文字も対象となる範囲が広いチェックになっているので使用する際は要注意です。
英数字等のチェックする場合は正規表現を使うようにしましょう。

数値入力

数値判定系のものとしては以下のものがあります

ルール動作
integer整数(小数点不可、Long値の範囲内)
numeric数値(is_numeric関数での判定)
digits:桁数指定した桁数の数値(小数点、マイナス不可)
digits_between:最少桁,最大桁最少桁~最大桁の範囲の数値(小数点、マイナス不可)

integerやnumericを指定した場合はsize、min、max、betweenが値の範囲指定になります。

人数などの整数値の場合は以下のような指定になるかと思います。
betweenはminとmax、場合によってはdigits_betweenでも問題ないです。

フォームリクエストバリデーションファイル
'customer_num'=>'nullable|integer|between:0,9999',

ハイフンなしの電話番号のような場合はdigits_betweenのチェックを使います。

フォームリクエストバリデーションファイル
'phone'=>'required|numeric|digits_between:10,11',

小数点以下の桁数等を制御する場合は下記のように正規表現での制御で対応します。

フォームリクエストバリデーションファイル
'distance'=>'nullable|numeric|regex:/\A\d{1,4}(\.\d{1,3})?\z/',

数値の大小を比較する場合はgt、gte、lt、lteを使いますが、比較する対象の値が空の場合にExceptionが発生するので項目が必須でない場合はsometimesメソッドでチェックします。

フォームリクエストバリデーションファイル
$validator->sometimes('to_customer_num','gte:from_customer_num',function(){return!empty($this->from_customer_num);});

日付

日付のチェックはdateとdate_formatがあるが、どちらかしか使えないので注意してください。
期間指定の場合はbefore、before_or_equal、after、after_or_equalで日付の比較を行うようにしましょう。

フォームリクエストバリデーションファイル
'end_date'=>'nullable|date_format:Y/m/d h:i:s|after:start_date'

ファイル

ファイル形式の場合はfileを指定します。
画像の場合はimageだけを指定すれば問題はないです。
これらを指定した場合はsize、min、max、betweenがファイルサイズ(kb)のチェックになります。
ファイルの形式を指定したい場合はmimetypesかmimesでmimetypeのチェックを行うことができます。
基本どちらかを指定すれば問題ないかと思います。
あとは、無制限に大容量のファイルをアップロードできないよう必ずファイルサイズの最大値を指定しましょう。
(サーバーソフトやPHPのアップロード容量上限にもよりますが)

フォームリクエストバリデーションファイル
'image'=>'nullable|image|mimes:jpeg,bmp,png|max:1024'

配列

リクエストが配列の場合はarrayでチェックします。
arrayを指定した場合はsize、min、max、betweenが配列の数になります。
配列の中身は必要でない場合を除きrequiredを指定し、重複が想定されない値についてはdistinctを指定しましょう。

フォームリクエストバリデーションファイル
'image'=>'nullable|array','image.*.id'=>'required|integer|digits_between:1,10|exists:t_images,id',

配列の中のデータの条件に応じてチェックするようなことはできないので、そのようなチェックが必要な場合はafterフックで対応してください。

正規表現

Laravelで用意されているルールでチェックできないような内容は正規表現でのチェックを検討をしましょう。
正規表現でチェックする場合は必ず文字列の開始終端の\A\zで指定します。
日本語等の2バイト文字を使用する場合はパターン修飾子uを指定します。

フォームリクエストバリデーションファイル
'reservation_number'=>'required|string|regex:/\A[a-zA-Z0-9]+\z/|size:8',

ペジネーション

Laravelの標準のペジネーションはオフセット値(表示件数×ページ)がLong最大値を超えるとエラーになるのでそうならないようバリデーションチェックを指定しておくと良いかと思います。
Intの上限値程度を上限で指定しておけば問題はないかと思います。

フォームリクエストバリデーションファイル
'page'=>'nullable|integer|between:1,9999999'

排他制御(楽観ロック)

updated_atで楽観的排他制御を行う方法になります。
ただ、この時点でトランザクションは行われていないので、更新時のトランザクション中に再度チェックを行ったほうが良いかと思います。

フォームリクエストバリデーションファイル
'updated_at'=>['required',Rule::exists('goods','updated_at')->where(function($query){$query->where('id',$this->report_id);$query->where('deleted_at',NULL);})],

その他

バリデーションチェックの処理に関連するノウハウになります。

バリデーションエラー時に特定の項目をフラッシュしない

通常バリデーションエラーが発生した場合はリクエストされた内容がフラッシュ(一時保存)され、入力した内容を入れた状態で元の画面に戻れるようになっています。
パスワードのようにバリデーションエラーが発生した際にその入力内容を未入力状態にするには
App\Exceptions\Handlerクラスに$dontFlashが定義されており、この配列に指定されているフィールド名が指定された場合はフラッシュされません。

Handler.php
protected$dontFlash=['password','password_confirmation',];

リダイレクト先がおかしくなる

バリデーションでエラーになった際、リクエスト時の画面に戻ってエラーメッセージ表示とならない現象が発生することがあります。
過去に遭遇したのは、axiosのGETメソッドでAPIにリクエストを送信した後にフォームリクエストでPOSTメソッドでリクエストした内容がバリデーションエラーとなると、axiosで送った処理にリダイレクトされてしまうというものでした。
原因は、axiosのAPIへのリクエストがLaravelで直前の画面だと判断されてしまっていたことでした。
エラー時のリダイレクト先はIlluminate\Session\Middleware\StartSessionのstoreCurrentUrlの処理で記憶しており、下記の条件文に当てはまる場合に保存されます。

StartSession.php
if($request->method()==='GET'&&$request->route()&&!$request->ajax()&&!$request->prefetch())

この時はaxiosのAPIへのリクエストをPOSTメソッドでの送信に変更するという方法で対処しました。

参考

エラー:Non-static method Illuminate\Http\Request::input() should not be called staticallyの解決法

Illuminate\Http\Requestクラスと、Requestファサードを同時に使う

コントローラを作成して、Request $requestのようにRequestクラスを利用して、ビュー側から送った値を取り出せます。

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class PostController extends Controller
{
    public function index(Request $request)
    {
        //Requestクラスを利用
        $result = $request->post;

        return view('home', compact('result'));
    }
}

しかし、ここでRequestファサードも同時に使いたいときはどうすればよいでしょうか。
単純に下のようにしてみます。

public function index(Request $request)
    {
        $result = $request->post;

     //Requestファサードを利用
        $name = Request::input('name');

        return view('home', compact('result', 'name'));
    }

しかし、これを実行すると、エラーが出てしまいます。

 Non-static method Illuminate\Http\Request::input() should not be called statically

これは、

use Illuminate\Http\Request;

で、Requestクラスを呼んでいるのに、Request::input()という、Staticで使っちゃダメですよってことを言っているわけです。

そこで、下のコードを加えてやると、無事にRequestファサードとして使えるようになるんですが、

use Request;

次は、

Cannot use Request as Request because the name is already in use

というエラーが出てしまいます。
これは、Requestという名前がすでに使われているので、同じ名前は使用できませんという意味です。

解決方法

というわけで、解決方法ですが、Requestファサードの名前をPostRequestに変更して、

use Illuminate\Http\Request;
use Request as PostRequest;
public function index(Request $request)
    {
        $result = $request->post;

     //Requestファサードは、PostRequestに変更
        $name = PostRequest::input('name');

        return view('home', compact('result', 'name'));
    }

とすれば、うまく動きます。

都道府県名をセレクトボックスで使用したい時に、Configファサードを使ってみる。

セレクトボックスを使用したい時に、optionタグを使う方法が思い浮かぶと思いますが、ビュー側のbladeに書くのが憚れるほど要素が多い時、例えば都道府県のように47個も書いてしまうと、それだけでbladeが散らかってしまいます。

そこで、こういったあらかじめ値や文字が決まっている場合は、別の場所にファイルを作って管理する方が便利です。

本記事では、Configファサードを使って呼び出す方法を紹介します。

都道府県のような固定値は、config内でファイルを作って管理しよう

まず、config配下にconstant.phpというファイルを作ります。

config.png

次に、constant.phpでは、都道府県の情報を書き込んでいきます。

return [
   '1' => '北海道',
   '2' => '青森',
   '3' => '岩手',
];

呼び出すときはConfigファサードで

まず、Configファサードを使用する宣言をします。

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Config; //Configファサードの宣言

あとは、使用したいところで呼び出すだけです。

public function post()
    {
      //これでconstant.phpで設定した値が使用できます
      $prefecture = Config::get('constant');

      return $prefecture;
    }

ちなみに、返り値は下記の通り、配列です。

array:3 [▼
  1 => "北海道"
  2 => "青森"
  3 => "岩手"
]

Laravel5.8でログイン失敗時にセッションにのせてデータを返す

例えば、ログインフォームをモーダルで実装していた時とかに、何もしていないとログイン失敗でリダイレクト時にモーダルが隠れている状態になるので return back()->with('login_modal', 'active')でセッションにのせて返す。
blade側で {{ session('login_modal') }}activeが取れるのでclassとかにブチ込んでモーダルを表示させたいというやつです。
ログイン失敗時のLaravelの処理がググっても日本語の結果があまり出てこなかったので、誰かのためになれば。。

ログイン機能はみなさんお馴染みの make:authで作っているので、そこで生成されたものを追っていく。
ログイン処理は LoginControllerで処理されているが、 LoginControllerAuthenticatesUsersをUseしているので、それを見にいく。

vendor/laravel/framework/src/Illuminate/Foundation/Auth/AuthenticatesUsers.php
~~protectedfunctionsendFailedLoginResponse(Request$request){throwValidationException::withMessages([$this->username()=>[trans('auth.failed')],]);}~~

で処理されているっぽい。
ここでは単純にバリデーションをthrowしているだけ。なのでここをよしなに修正する。
流石に vendor内をいじるのは良くないので、 LoginControllerでオーバーライドする。

LoginController.php
protectedfunctionsendFailedLoginResponse(Request$request){// throw ValidationException::withMessages([//     $this->username() => [trans('auth.failed')],// ]);// ↑が元々の記述returnback()->with('login_modal','active')->withErrors([$this->username()=>[trans('auth.failed')],]);}

以上!

モーダルは class="active"で表示状態になるとして、

modal
<divid="modal"class="{{ session('login_modal') }}">~~</div>

としておくと、 sendFailedLoginResponse()で帰ってきたときにモーダルがactiveになる。

以上!

laravel pluckメソッドの挙動について

pluckメソッドでkeyとvalueを取得して、toArrayでコレクションを配列にする。

publicfunctionindex(){$tags=Tag::pluck('title','id')->toArray();dd($tags);}

結果

array:10 [▼
  1 => "タグ1"
  2 => "タグ2"
  3 => "タグ3"
  4 => "タグ4"
  5 => "タグ5"
  6 => "タグ6"
  7 => "タグ7"
  8 => "タグ8"
  9 => "タグ9"
  10 => "タグ10"
]

注意点としては、pluckメソッドの第1引数がvalueで、第2引数がkeyとなる。
試しにpluckメソッドの引数を逆にしてみる。

publicfunctionindex(){$tags=Tag::pluck('id','title')->toArray();dd($tags);}

出力結果

array:10 [▼
  "タグ1" => 1
  "タグ2" => 2
  "タグ3" => 3
  "タグ4" => 4
  "タグ5" => 5
  "タグ6" => 6
  "タグ7" => 7
  "タグ8" => 8
  "タグ9" => 9
  "タグ10" => 10
]

titleがkeyになり、idがvalueとなりました。

何か間違っている点があればアドバイスお願いします。ありがとうございました。

Stripeでの課金@Laravel 5.8

まえがき

個人開発を行っているアプリで課金を行いため、調査を行った。

以前から気にはなっていたがテスト環境での課金が完了したため、記事とする。

日本語情報が少ない中試行錯誤したため更新していくかもしれない。

なお、動作確認を主としているためコードの可読性に関してはご容赦ください。

環境

Windows10 Pro 64bit
PHP7.3
Laravel 5.8

必要な設定

composer

composer require "stripe/stripe-php"

以上。Stripeの色々やってくれる便利なやつ。

.env

STRIPE_KEY=pk_test_***************************************
STRIPE_SECRET=sk_test_***************************************

.envファイルに記載。わかりゃ何でもいいです。

これは、ダッシュボードから確認できるものですね。

単独課金

// コントローラのアクションにベタ書きしてます$token=env('STRIPE_SECRET');// キーの設定。\Stripe\Stripe::setApiKey($token);/* 
入力してもらったカード情報をsubmitすると、"stripeToken"というキーで取得できるトークンを利用。
5000円の課金。
*/$charge=\Stripe\Charge::create(['amount'=>5000,'currency'=>'jpy','description'=>'Example charge','source'=>$request->stripeToken,]);

顧客登録、顧客に基づく課金

// コントローラのアクションにベタ書きしてます$token=env('STRIPE_SECRET');// キーの設定。\Stripe\Stripe::setApiKey($token);/* 
顧客の登録。
配列のキー追加で顧客のアドレス、名称等々設定できますが今は必要最低限のもの。
*/$customer=\Stripe\Customer::create(['source'=>$request->stripeToken,]);// 上記で登録した顧客IDを元に、登録できます。$charge=\Stripe\Charge::create(['amount'=>1000,'currency'=>'jpy','customer'=>$customer->id,]);

登録済み顧客データから課金

前項の例で分かった方はいらっしゃるかと思いますが

// コントローラのアクションにベタ書きしてます$token=env('STRIPE_SECRET');// キーの設定。\Stripe\Stripe::setApiKey($token);// 顧客の取得。適当なモデルに紐付けたstripe_idで課金します。$customer_id=Customer::find(1)->stripe_id;// 上記で登録した顧客IDを元に、登録できます。$charge=\Stripe\Charge::create(['amount'=>1000,'currency'=>'jpy','customer'=>$customer_id,]);

参考

ドキュメント(カード登録んところ):https://stripe.com/docs/saving-cards
ドキュメント(テストカード):https://stripe.com/docs/testing

おわり

雑記のようなものですが備忘録。
希望貧乏なエンジニアなので何卒ご容赦を。

Laravel5.8でFIND_IN_SETを使って、1カラム内のカンマ区切りデータを取得する

タイトルの通りです!!

こんな感じのテーブル

menusテーブル

idmenumaterial
1オムライス卵,ご飯,チキン
2厚焼きたまご
3唐揚げ定食ご飯,チキン

環境

Laravel5.8
mysql5.7

やってみよう

materialに「卵」が入っているmenuを取得する!

$query=DB::table('menus')->whereRaw('FIND_IN_SET("卵",material)')->get();

出力する!

dd($query);

確認する!

スクリーンショット 2020-06-11 22.35.47.png

オムライスと厚焼きたまごをGET!!!

エスケープしてみる

$colname='卵'$query=DB::table('menus');->whereRaw('FIND_IN_SET(?,material)',[$colname])->get();

同様に取得できました。

参考サイト

[MySQL]1カラムに登録されたカンマ区切りデータを検索・抽出する方法(FIND_IN_SET)
find in set in laravel ? example

Immutableとは?Mutableとの違い

Immutableとは

プログラミングにおいてImmutable(イミュータブル)とは「不変」という意味で、オブジェクトの状態が変わらないことを指します。

逆に、元のデータが変更可能なオブジェクトの性質をMutable(ミュータブル)と言います。

ImmutableとMutableの違い

Immutable(イミュータブル)とMutable(ミュータブル)の違いについて、実際にみていきましょう。

まずは、日付操作ライブラリ「Carbon」を使って現在日時を取得してみます。

sample.php
$now=\Carbon\Carbon::now();//2020-05-26 06:57:02.027091

現在日時が取得できました。
次に、addDayメソッドで上記で取得した現在日時に1日加算してみます。

sample.php
$tomorrow=$now->addDay();//2020-05-27 06:59:01.25073

1日加算されています。

ここで$nowの中身を再びみてみます。

sample.php
var_dump($now);//2020-05-27 06:59:01.25073

なんと、\$nowまで1日加算され、\$nowと\$tomorrowが同じ結果となってしまいました。
これは、\$nowと\$tomorrowが同じオブジェクトであるために起こります。
元の状態の変化を防ぐにはcopyメソッドを使います。

copyメソッドで元の状態を保持

copyメソッドで元のオブジェクトを複製して、2つの変数の結果を見てみましょう。

sample.php
$tomorrow=$now->copy->addDay();var_dump($now);//2020-05-26 06:57:02.027091var_dump($tomorrow);//2020-05-27 06:59:01.25073

元のオブジェクトはそのままで、$tomorrowだけ1日加算されました。

Carbonのバージョン2系では、このImmutable(イミュータブル)版が利用できるようになっています。

SQLSTATE[HY000] [1045] Access denied for user エラー

http://localhost:8888にアクセスし、MySQKのデータをlaravelのDBクラスを使って表示するときにでたエラー

実際に出たエラー

SQLSTATE[HY000] [1045] Access denied for user 'root'@'localhost' (using password: YES) (SQL: select * from information_schema.tables where table_schema = laravel-project and table_name = migrations and table_type = 'BASE TABLE')

エラーを出してしまった理由

ターミナル からアクセスしていたmysqlのsocketが必要なのにphpmyadminのsocketを.envのDB_SOCKETに記述してしまっていた。

解決した方法

mysqlコマンドの\sでサーバー情報を表示してsocketを.envのDB_SOCKETにコピーし、
ターミナルに戻り、(一度設定した.envを再編集した場合は)キャッシュをクリアにする
$ php artisan config:cache

参考サイト

MySQLサーバー情報取得
https://phpjavascriptroom.com/?t=mysql&p=info

Browsing Latest Articles All 61 Live