- 投稿日:2020-09-16T23:21:54+09:00
PHP、Laravel学習 2
ポートフォリオ作成
ここからポートフォリオ作成に入ります。
献立を考えてくれるWebアプリケーションを開発したいです
私は料理が好きで、よく作るのですがレパートリーがたくさんあるにもかかわらず
いざ何かを作ろうとすると、かなり悩んでしまいます
私は料理を買い物からするので、スーパーに行ったは良いものの、そこで買い物をしながら
悩んでしまったり、いつも作っている物の繰り返しになってしまいます。解決したい問題
1、たくさんレパートリーがあるはずなのに忙しい中で料理をしようと思うとそれを思うように
引き出すことができない問題2、料理の種類が和食、洋食、中華...等、いざ作ろうと思うと混ざってしまったりする問題
3、献立を考えている時間の削減、スーパーで買い物をする時の効率化
4、似通った料理の連続になってしまう問題(例えば、オムライス→炊き込みご飯→カレーの繰り返し等)
欲しい機能
1、献立をランダムで表示してくれる
2、献立に必要な材料の入力、保存ができる
3、献立のレパートリーを入力、保存できる
4、献立をカテゴライズできる
5、献立のカテゴリーを入力、保存できる
6、入力した献立の確認ができる
7、入力したカテゴリーの確認ができる次回から作っていきます。
- 投稿日:2020-09-16T22:12:08+09:00
PHPでログイン機能を実装したら、ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)というエラーでハマった話
https://awesome-linus.com/2019/06/05/laravel-tutorial-todo/
こちらのサイト様の記事を参考にログイン機能を実装していたら、はまりました。
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)パスワードは使用していません。
ルートユーザーでアクセスしたら拒否されたというもの。
ログインと新規登録機能を実装し、アプリをWEBで開いた段階でこのメッセージが表示されました。$ mysql -u root ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)mysql -u rootでログインするとエラーが出るが、
$mysql -u root -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g.このように打ち込むとエラーが出ない。つまり、パスワードをYESにして、設定したパスワードをPHPのファイルのどこかに打ち込む必要があるのではないかと判断。
それっぽいファイルを探してみる。
database.php'connections' => [ 'mysql' => [ 'driver' => 'mysql', 'host' => env('DB_HOST', '127.0.0.1'), 'port' => env('DB_PORT', '3306'), 'database' => env('DB_DATABASE', 'forge'), 'username' => env('DB_USERNAME', 'forge'), 'password' => env('DB_PASSWORD', ''), 'unix_socket' => env('DB_SOCKET', ''), 'charset' => 'utf8mb4', 'collation' => 'utf8mb4_unicode_ci', 'prefix' => '', 'strict' => true, 'engine' => null, ], # 以下省略'password' => env('DB_PASSWORD', ''),
この部分に注目した。
’’の間にパスワードを手動で入れればもしかしたら行けるのでは?
と思ったが、’’の中に手動でパスワードを打ち込んでもうまくいかない。DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=todo DB_USERNAME=root DB_PASSWORD=secretしやここのsecretで設定されているパスワードの部分を書き換えたらいけるのではと思い、
secretの部分を書き換えたりしたがうまくいかず。パスワードをYESで接続するにはどうしたらいいのだ。
仕方ないので方針転換して、rootユーザのパスワードをなしにすることにしました。
$mysql -u root -pパスワードを打ち込んで接続
mysql> SET PASSWORD FOR ユーザ名@ホスト名=password('新しいパスワード');新しいパスワードの部分を空欄にして設定。
パスワードを打ち込んでログインしてみます。無事に成功!
rootユーザのパスワードの設定を使用をYESにして、パスワードを設定する方法はわかりませんでしたが、
パスワードの設定をログインして書き換えることでうまく行きませんでした。またなにかエラーを解決しましたら、引き続きアウトプットしていきます。
- 投稿日:2020-09-16T18:58:29+09:00
クロスサイトに対応する
環境
環境 Laravel7.x HTTPS AWS ALB, EC2 PHPでフレームワークはLaravel7.14を利用しています。
webサーバーはnginxです。(あまり関係ない)
下記に行った順で課題に対するコミットを記述していきます。課題
クロスサイトのPOSTメソッドで指定したオリジンから取得したものをsessionに保存したい。
当プロジェクトでは、あるプラットフォームから取得できるuser_idのようなものを
sessionに保存したい、という課題でした。
しかしながら、解決策はフロントエンドとサーバーサイドでオリジンを分けている際にも対応可能の解決策でした。①HTTPS化する
私が調査した中で、クロスサイトを有効にするには
通信を暗号化(常時ssl化)し、CookieのSecure属性をtrueにする
というのが最初の打開策でした。
まず、はじめにロードバランサーのエンドポイントにssl証明書を発行します。
30分ぐらいで証明書の発行ができます。
参考URLはこちら↓
https://recipe.kc-cloud.jp/archives/11084さらに、常時HTTPS化するには(Laravel7.x)
src/app/Http/Middleware/TrustProxies.php<?php namespace App\Http\Middleware; use Fideloper\Proxy\TrustProxies as Middleware; use Illuminate\Http\Request; class TrustProxies extends Middleware { /** * The trusted proxies for this application. * * @var array|string|null */ protected $proxies = '**'; /** * The headers that should be used to detect proxies. * * @var int */ protected $headers = Request::HEADER_X_FORWARDED_ALL; }で、LBの終端でHTTPS化が可能となります。
CookieのSecure属性をtrueにする。
次に、Samesite=none; Secureにするために
## .env SESSION_SECURE_COOKIE=truesrc/config/session.php/* 〜〜〜〜〜〜〜 〜〜〜〜〜〜〜 */ 'same_site' => 'none',とします。
これでchromeとfireboxでは課題を解決しましたが、
safariではSamesite=none
が実装されていないため
sessionを保存できず、、、、という感じでした。②CORSの設定を編集
そして行き着いたのは、ヘッダーに許容するオリジンを追加するという手段です。
Laravel7.xにはデフォルトでsrc/config/cors.php
があるので
下記のように編集します。src/config/cors.php<?php return [ /* |-------------------------------------------------------------------------- | Cross-Origin Resource Sharing (CORS) Configuration |-------------------------------------------------------------------------- | | Here you may configure your settings for cross-origin resource sharing | or "CORS". This determines what cross-origin operations may execute | in web browsers. You are free to adjust these settings as needed. | | To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS | */ // 許容するmiddlewareのグループを記載する。 // 当プロジェクトでは * にしています。 'paths' => ['*'], // 許容するメソッドを指定する 'allowed_methods' => ['GET', 'POST'], // 許容するオリジンを指定する。 // .envに記載してもいい // 'allowed_origins' => [env('REQUIRE_DMAIN', NULL)], 'allowed_origins' => ['https://<ドメイン>'], 'allowed_origins_patterns' => [], // 許容するヘッダーを記載する 'allowed_headers' => ['X-CSRF-TOKEN', 'Content-Type', 'Origin', 'Authorization', 'Accept', 'X-Requested-With'], 'exposed_headers' => [], 'max_age' => 0, 'supports_credentials' => false, ];上記のようにすることで、safariでもsessionを記載することができました。
Laravel7.x未満でもcors.phpはcomposerでインストールできますのでそちらを参照してください。以上が私が行った解決策です。
allowed_origins
にポート番号も記載すれば異なるポート番号からも許容できるようになります。
もっと良い方法があれば是非、コメントよろしくお願いします。
- 投稿日:2020-09-16T16:55:19+09:00
laravel8系でTarget class [XXXController] does not exist. error
公式コピペ注意報発令中です。
どこもおかしくない、でもわからん原因は?原因
web.phpの書き方が8系になって変わった。
参考:https://laravel.com/docs/8.x/releases
対処例
これまで
web.phpRoute::get('/users', UserController@index);これから
web.phpuse App\Http\Controllers\UserController; Route::get('/users', [UserController::class, 'index']);以上。
- 投稿日:2020-09-16T14:08:22+09:00
[Laravel] LINEログインv2.1を実装する
今回の題
LINEログインをLaravelにて実装しました。
アウトプットとして残します。使用したバージョン
- Laravel 6.8 ※Laravelのインストール手順は省略します。
- LINEログインv2.1
- guzzle7.0
チャネルIDとチャネルシークレットの取得
4ステップに分けて説明していきます。
1. 開発者用のアカウントを作成
以下にアクセスし、LINEのアカウントを使って開発者用のページにログインしてください。
LINE Developers2. プロバイダーの作成
以下のページの左サイドバーのプロバイダーを選択し、画面中央辺りの「作成」を押してください。
LINE Developers コンソール作成するプロバイダー名を求められるので適当に入力し、「作成」を押したらプロバイダーの作成は完了です。
3. チャネルの作成
プロバイダーの作成後、以下のようなページに飛ばされます。
画像内の赤枠の「LINE ログイン」を選択してください。
チャネル作成画面に飛びます。
チャネルの名前や説明などは適宜全て入力し、アプリタイプは「ウェブアプリ」を選択しておいてください。
「作成」を押したらチャネルの作成は完了です。
4. チャネルIDとチャネルシークレットの取得
チャネル作成後、作成したチャネルの設定ページに飛ばされます。
(画像は黒く塗りつぶしています)以上でチャネルIDとチャネルシークレットがGETできました。
コールバックURLの設定
ユーザーが認証を許可した後に、リダイレクトされるURLを指定します。
LINEログイン設定のタブに移動してコールバックURLを入力し更新を押してください。
尚、この記事では後のルーティングでauth/line/callback
というパスを指定するので、記事通りに進めるのであれば、
https://各自のドメイン/auth/line/callback
で設定しておいて下さい。
メールアドレスを取得する設定
ログインしたユーザーのメールアドレスを取得するための設定です。必要な場合のみ設定して下さい。
チャネル基本設定のタブの下の方に「OpenID Connect」という項目があります。
申請ボタンを押すと、申請条件への同意と、メールアドレスの取得と利用についてユーザーに提示する文面のスクリーンショットのアップロードを求められるので済ませます。
必要なライブラリの準備
・Guzzle
リクエストを送信する際に使用します。
公式$ composer require guzzlehttp/guzzleLaravel
ここからコードを書いていきます。
設定
・チャネルID
・チャネルシークレット
・コールバックURL
は.env
に書いておき、config経由で呼び出すことにします。.envLINE_CHANNEL_ID=あなたのチャネルid LINE_CHANNEL_SECRET=あなたのチャネルシークレット LINE_CALLBACK_URL=設定したコールバックURLconfigディレクトリにline.phpというファイルを作り、envから値を受け取るように編集します。
config/line.php<?php return [ 'client_id' => env('LINE_CHANNEL_ID'), 'client_secret' => env('LINE_CHANNEL_SECRET'), 'callback_url' => env('LINE_CALLBACK_URL'), ];ルーティング
routes/web.php
を以下のように設定します。routes/web.php// LINEの認証画面に遷移 Route::get('auth/line', 'Auth\LineOAuthController@redirectToProvider')->name('line.login'); // 認証後にリダイレクトされるURL(コールバックURL) Route::get('auth/line/callback', 'Auth\LineOAuthController@handleProviderCallback');view
この記事ではURLに遷移させるだけの単純なものを書いておきます。
本来は、公式の指定するデザインのログインボタンを設定する必要があります。
LINEログインボタン デザインガイドライン<a href="{{ route('line.login') }}">LINEでログイン</a>コントローラー
以下で作成
$ php artisan make:controller Auth/LineOAuthController編集します。
app/Http/Controllers/Auth/LineOAuthController.php<?php namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; use Illuminate\Http\Request; use Illuminate\Support\Str; use GuzzleHttp\Client; class LineOAuthController extends Controller { private const LINE_OAUTH_URL = 'https://access.line.me/oauth2/v2.1/authorize?'; private const LINE_TOKEN_API_URL = 'https://api.line.me/oauth2/v2.1/'; private const LINE_PROFILE_API_URL = 'https://api.line.me/v2/'; private $client_id; private $client_secret; private $callback_url; public function __construct() { $this->client_id = Config('line.client_id'); $this->client_secret = Config('line.client_secret'); $this->callback_url = Config('line.callback_url'); } public function redirectToProvider() { $csrf_token = Str::random(32); $query_data = [ 'response_type' => 'code', 'client_id' => $this->client_id, 'redirect_uri' => $this->callback_url, 'state' => $csrf_token, 'scope' => 'profile openid', ]; $query_str = http_build_query($query_data, '', '&'); return redirect(self::LINE_OAUTH_URL . $query_str); } public function handleProviderCallback(Request $request) { $code = $request->query('code'); $token_info = $this->fetchTokenInfo($code); $user_info = $this->fetchUserInfo($token_info->access_token); // ログイン処理 } private function fetchUserInfo($access_token) { $base_uri = ['base_uri' => self::LINE_PROFILE_API_URL]; $method = 'GET'; $path = 'profile'; $headers = ['headers' => [ 'Authorization' => 'Bearer ' . $access_token ] ]; $user_info = $this->sendRequest($base_uri, $method, $path, $headers); return $user_info; } private function fetchTokenInfo($code) { $base_uri = ['base_uri' => self::LINE_TOKEN_API_URL]; $method = 'POST'; $path = 'token'; $headers = ['headers' => [ 'Content-Type' => 'application/x-www-form-urlencoded' ] ]; $form_params = ['form_params' => [ 'code' => $code, 'client_id' => $this->client_id, 'client_secret' => $this->client_secret, 'redirect_uri' => $this->callback_url, 'grant_type' => 'authorization_code' ] ]; $token_info = $this->sendRequest($base_uri, $method, $path, $headers, $form_params); return $token_info; } private function sendRequest($base_uri, $method, $path, $headers, $form_params = null) { try { $client = new Client($base_uri); if ($form_params) { $response = $client->request($method, $path, $form_params, $headers); } else { $response = $client->request($method, $path, $headers); } } catch(\Exception $ex) { // 例外処理 } $result_json = $response->getbody()->getcontents(); $result = json_decode($result_json); return $result; } }アクション名が思いつかなかったのでSociliteから流用して使ってます。
handleProviderCallback()の変数、
$token_info
、$user_info
には、
$token_info
→ トークン情報(アクセストークン、リフレッシュトークンなど)$user_info
→ ユーザー情報(ユーザーID、ユーザー名、プロフィール画像、プロフィールメッセージ)が入っていますので、これらを各自のテーブル構成に合わせて保存しログインさせて下さい。
尚、アクセストークンの有効期間は30日ですので、適宜リフレッシュトークンを使って新しいアクセストークンを取得して下さい。一言
ソーシャルログインはSocialiteを使ったTwitterログインしか実装経験はありませんでしたが、LINEでも意外と簡単にできて驚きでした。特に鬼門はないかと思います。
何か間違えなどがあればコメントにお願いいたします。参考
- 投稿日:2020-09-16T14:08:22+09:00
[Laravel] LINEログインを実装する
今回の題
LINEログインをLaravelにて実装しました。
アウトプットとして残します。使用したバージョン
- Laravel 6.8 ※Laravelのインストール手順は省略します。
- LINEログインv2.1
- guzzle7.0
チャネルIDとチャネルシークレットの取得
4ステップに分けて説明していきます。
1. 開発者用のアカウントを作成
以下にアクセスし、LINEのアカウントを使って開発者用のページにログインしてください。
LINE Developers2. プロバイダーの作成
以下のページの左サイドバーのプロバイダーを選択し、画面中央辺りの「作成」を押してください。
LINE Developers コンソール作成するプロバイダー名を求められるので適当に入力し、「作成」を押したらプロバイダーの作成は完了です。
3. チャネルの作成
プロバイダーの作成後、以下のようなページに飛ばされます。
画像内の赤枠の「LINE ログイン」を選択してください。
チャネル作成画面に飛びます。
チャネルの名前や説明などは適宜全て入力し、アプリタイプは「ウェブアプリ」を選択しておいてください。
「作成」を押したらチャネルの作成は完了です。
4. チャネルIDとチャネルシークレットの取得
チャネル作成後、作成したチャネルの設定ページに飛ばされます。
(画像は黒く塗りつぶしています)以上でチャネルIDとチャネルシークレットがGETできました。
コールバックURLの設定
ユーザーが認証を許可した後に、リダイレクトされるURLを指定します。
LINEログイン設定のタブに移動してコールバックURLを入力し更新を押してください。
尚、この記事では後のルーティングでlogin/line/callback
というパスを指定するので、記事通りに進めるのであれば、
https://各自のドメイン/login/line/callback
で設定しておいて下さい。
メールアドレスを取得する設定
ログインしたユーザーのメールアドレスを取得するための設定です。必要な場合のみ設定して下さい。
チャネル基本設定のタブの下の方に「OpenID Connect」という項目があります。
申請ボタンを押すと、申請条件への同意と、メールアドレスの取得と利用についてユーザーに提示する文面のスクリーンショットのアップロードを求められるので済ませます。
必要なライブラリの準備
・Guzzle
リクエストを送信する際に使用します。
公式$ composer require guzzlehttp/guzzleLaravel
ここからコードを書いていきます。
設定
・チャネルID
・チャネルシークレット
・コールバックURL
は.env
に書いておき、config経由で呼び出すことにします。.envLINE_CHANNEL_ID=あなたのチャネルid LINE_CHANNEL_SECRET=あなたのチャネルシークレット LINE_CALLBACK_URL=設定したコールバックURLconfigディレクトリにline.phpというファイルを作り、envから値を受け取るように編集します。
config/line.php<?php return [ 'client_id' => env('LINE_CHANNEL_ID'), 'client_secret' => env('LINE_CHANNEL_SECRET'), 'callback_url' => env('LINE_CALLBACK_URL'), ];ルーティング
routes/web.php
を以下のように設定します。routes/web.php// LINEの認証画面に遷移 Route::get('auth/line', 'Auth\LineOAuthController@redirectToProvider')->name('line.login'); // 認証後にリダイレクトされるURL(コールバックURL) Route::get('auth/line/callback', 'Auth\LineOAuthController@handleProviderCallback');view
この記事ではURLに遷移させるだけの単純なものを書いておきます。
本来は、公式の指定するデザインのログインボタンを設定する必要があります。
LINEログインボタン デザインガイドライン<a href="{{ route('line.login') }}">LINEでログイン</a>コントローラー
以下で作成
$ php artisan make:controller Auth/LineOAuthController編集します。
app/Http/Controllers/Auth/LineOAuthController.php<?php namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; use Illuminate\Http\Request; use Illuminate\Support\Str; use GuzzleHttp\Client; class LineOAuthController extends Controller { private const LINE_OAUTH_URL = 'https://access.line.me/oauth2/v2.1/authorize?'; private const LINE_TOKEN_API_URL = 'https://api.line.me/oauth2/v2.1/'; private const LINE_PROFILE_API_URL = 'https://api.line.me/v2/'; private $client_id; private $client_secret; private $callback_url; public function __construct() { $this->client_id = Config('line.client_id'); $this->client_secret = Config('line.client_secret'); $this->callback_url = Config('line.callback_url'); } public function redirectToProvider() { $csrf_token = Str::random(32); $query_data = [ 'response_type' => 'code', 'client_id' => $this->client_id, 'redirect_uri' => $this->callback_url, 'state' => $csrf_token, 'scope' => 'profile openid', ]; $query_str = http_build_query($query_data, '', '&'); return redirect(self::LINE_OAUTH_URL . $query_str); } public function handleProviderCallback(Request $request) { $code = $request->query('code'); $token_info = $this->fetchAccessToken($code); $user_info = $this->fetchUserInfo($token_info->access_token); // ログイン処理 } private function fetchUserInfo($access_token) { $base_uri = ['base_uri' => self::LINE_PROFILE_API_URL]; $method = 'GET'; $path = 'profile'; $headers = ['headers' => [ 'Authorization' => 'Bearer ' . $access_token ] ]; $user_info = $this->sendRequest($base_uri, $method, $path, $headers); return $user_info; } private function fetchAccessToken($code) { $base_uri = ['base_uri' => self::LINE_TOKEN_API_URL]; $method = 'POST'; $path = 'token'; $headers = ['headers' => [ 'Content-Type' => 'application/x-www-form-urlencoded' ] ]; $form_params = ['form_params' => [ 'code' => $code, 'client_id' => $this->client_id, 'client_secret' => $this->client_secret, 'redirect_uri' => $this->callback_url, 'grant_type' => 'authorization_code' ] ]; $token_info = $this->sendRequest($base_uri, $method, $path, $headers, $form_params); return $token_info; } private function sendRequest($base_uri, $method, $path, $headers, $form_params = null) { try { $client = new Client($base_uri); if ($form_params) { $response = $client->request($method, $path, $form_params, $headers); } else { $response = $client->request($method, $path, $headers); } } catch(\Exception $ex) { // 例外処理 } $result_json = $response->getbody()->getcontents(); $result = json_decode($result_json); return $result; } }handleProviderCallback()の変数、
$token_info
、$user_info
には、
$token_info
→ トークン情報(アクセストークン、リフレッシュトークンなど)$user_info
→ ユーザー情報(ユーザーID、ユーザー名、プロフィール画像、プロフィールメッセージ)が入っていますので、これらを各自のテーブル構成に合わせて保存しログインさせて下さい。
尚、アクセストークンの有効期間は30日ですので、適宜リフレッシュトークンを使って新しいアクセストークンを取得して下さい。一言
ソーシャルログインはSocialiteを使ったTwitterログインしか実装経験はありませんでしたが、LINEでも意外と簡単にできて驚きでした。特に鬼門はないかと思います。
何か間違えなどがあればコメントにお願いいたします。参考
- 投稿日:2020-09-16T12:00:38+09:00
Laravel getFormattedDueDateAttributeでTrailing data のエラーが出た場合
初めに
仕事でLaravelの知識が必要となり、下記リンクの良さげなチュートリアルをやっていまた。
https://www.hypertextcandy.com/laravel-tutorial-todo-app-list-tasksチュートリアル通りにやっていてもエラーってやっぱり起きるのですよね〜。。。
環境
チュートリアルが2018年のものだったので、少し古めのv6でやってみた(この時、もっと詳しく年代別のバージョンを調べていれば・・・)
$ php artisan --version Laravel Framework 6.18.40問題の箇所
上記リンク先のチュートリアルである「日付の表示形式を変更する」の部分
Task.php// この行を追加 use Carbon\Carbon; class Task extends Model { /* 中略 */ /** * 整形した期限日 * @return string */ public function getFormattedDueDateAttribute() { return Carbon::createFromFormat('Y-m-d', $this->attributes['due_date']) ->format('Y/m/d'); } }Carbon ライブラリを使って期限日の値の形式を変更して返す部分で、以下のようなエラーが!
Carbon\Exceptions\InvalidFormatException Trailing data (View: /Users/***/resources/views/tasks/index.blade.php)全てコピペし直したりしたので、どう考えてもスペルミスではない。とすると、、、
バージョンによる違いでした。。
https://stackoverflow.com/questions/50208932/laravel-model-trailing-data-when-save-the-model
上記stackoverflowで見た回答を試すとエラーは解消された。。。
おそらくv5とv6の違いなのでしょう。Task.php/** * 整形した期限日 * @return string */ public function getFormattedDueDateAttribute() { // createFromFormatの中を 'Y-m-d H:i:s'に変更する return Carbon::createFromFormat('Y-m-d H:i:s', $this->attributes['due_date']) ->format('Y/m/d'); }と言うか、チュートリアルのページをよく見ると、以下のようなコメントもありました
お世話になります。
Laravel6.14.0です。
後半の「日付の表示形式を変更する」の項で、getFormattedDueDateAttributeを追加し、テンプレートの修正をしてブラウザを再読み込みしたところ、「trailing error」が出ました。
createFromFormat内を('Y-m-d H:i:s' , ......)と修正したところ、解決しました。
念のため、お知らせしておきます。
- 投稿日:2020-09-16T10:29:59+09:00
Trelloも要らない? Exmentでカンバンボードを運用してみる
BacklogやRedmineが要らないとか煽り記事書いてしまった筆者です。どうもです。
さて、Exmentでガントチャートを作ってしまったら、どうせならカンバンボードもやってみたくなるのが人情ではないでしょうか?
というわけで、やってみました。
Expressでフロントを作る
簡易原価計算の時や、ガントチャートの時と同じく、Express + Pugでフロント画面を作ります。
今回、フロントではSortable.jsというライブラリを用いました。HTMLの要素を自由にドラッグアンドドロップできるようになる魔法のようなライブラリです。
まずは、タスク表示のGET部分です。
app.jsapp.get('/kanban', async(req,res) => { ;(async () => { const exmentToken = JSON.parse(await fs.readFileSync('./exment_tokens.txt')).access_token let tasksData = await axios.get('https://example.com/api/data/tasks/?orderby=start_at', { headers: { 'Authorization': 'Bearer ' + exmentToken } }) .then(res => { return res.data.data }) .catch(err => { console.error(err) }) let tasksArray = [] tasksData.forEach(item => { const id = item.id const name = item.value.title const status = item.value.status tasksArray.push({ id: String(id), name: name, status: status, endAt: item.value.end_at }) }) let statuses = await axios.get('https://example.com/api/column/86', { headers: { 'Authorization': 'Bearer ' + exmentToken } }) .then(res => { return res.data.options.select_item_valtext }) .catch(err => { console.error(err) }) statuses = statuses.split('\r\n') let statusData = [] statuses.forEach(status => { status = status.split(',') statusData.push({ id: status[0], title: status[1] }) }) // console.log(statusData) let payload = [] let i = 1 statusData.forEach(item => { const tasks = _.filter(tasksArray, { status: item.id }) let j = 1 const taskItems = () => { let taskItems = [] tasks.forEach(item => { taskItems.push({ id: `item-id-${j}`, exmentId: item.id, title: item.name, endAt: item.endAt }) j++ }) return taskItems } payload.push({ id: `board-id-${i}`, title: item.title, item: taskItems() }) i++ }) // console.log(payload) res.render('kanban', { payload }) })() })今回も、Exment側からデータを取得して、Sortableのデータ形式に整形しています。なんというか、JSONコネコネ屋さんって感じですね…
続いて、viewです。
kanban.pug<!DOCTYPE html> html(lang="ja") head meta(charset="UTF-8") meta(name="viewport", content="width=device-width, initial-scale=1.0") title Document . <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/8.0.1/normalize.min.css" integrity="sha512-NhSC1YmyruXifcj/KFRWoC561YpHpc5Jtzgvbuzx5VozKpWvQ+4nXhPdFgmx8xqexRcpAglTj9sIBWINXa8x5w==" crossorigin="anonymous" /> style. * { box-sizing: border-box; } [class^="wrapper-"] { background: #ddd; padding: 0; width: 300px; margin-right: 30px; flex: 0 0 auto; height: 100%; } [id^="board-id-"] { padding-left: 0; padding: 15px; } #boards { width: 100vw; overflow-x: scroll; display: flex; height: 100%; } .title { margin-top: 15px; text-align: center; margin-bottom: 0; } .item { background: #f2f2f2; list-style-type: none; padding: 15px; margin-bottom: 15px; margin-left: 0; } .item:last-child { margin-bottom: 0; } .due { font-size: 11px; color: #777; font-weight: bold; } body #boards each board in payload div(class="wrapper-" + board.id) h2.title=board.title ul(id=board.id).sortable if board.item each item in board.item li.item(data-exmentid=item.exmentId) =item.title br span.due=`期日:${item.endAt}` . <script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.10.2/Sortable.min.js" integrity="sha512-ELgdXEUQM5x+vB2mycmnSCsiDZWQYXKwlzh9+p+Hff4f5LA+uf0w2pOp3j7UAuSAajxfEzmYZNOOLQuiotrt9Q==" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.20.0/axios.min.js" integrity="sha512-quHCp3WbBNkwLfYUMd+KwBAgpVukJu5MncuQaWXgCrfgcxCJAq/fo+oqrRKOj+UKEmyMCG3tb8RB63W+EmrOBg==" crossorigin="anonymous"></script> script. var boards = document.querySelectorAll('.sortable') var i = 1 boards.forEach(function(board) { new Sortable(board, { group: 'status', onEnd: function(event) { var newStatus = event.to.id.replace(/[^0-9]/g, '') var itemId = event.item.dataset.exmentid axios.put('/kanban', { id: itemId, status: newStatus }) } }) })これで
http://localhost:3000/kanban
にアクセスしてみましょう。こんな感じになっていれば成功です!
なお、Trello風のUIを実現するCSSは、以下の記事を参考にしました。
カンバン間を移動したらステータスが変更されるようにPUTしよう
ガントチャートの時もそうでしたが、フロントで弄った時に、きちんとバックエンドに通信が飛んでデータが上書きされてほしいですよね。
Sortable.jsにもコールバックの仕組みがきちんと用意されているので、以下の技術を追記します。
kanban.pugscript. var boards = document.querySelectorAll('.sortable') var i = 1 boards.forEach(function(board) { new Sortable(board, { group: 'status', onEnd: function(event) { var newStatus = event.to.id.replace(/[^0-9]/g, '') var itemId = event.item.dataset.exmentid axios.put('/kanban', { id: itemId, status: newStatus }) } }) })オプションのプロパティ
onEnd
が、ドラッグアンドドロップを完了した時のコールバックです。
ここにExment側のAPIから持ってきたExmentのタスクのIDと、ボードのIDを取り込みます。SortableのボードのIDは、
board-id-1
みたいな感じなので、数字以外を取り払ってあげます。Exmentで設定した列設定では、enum型で値は数字を列挙しているので、ID番号=Exment側の値となります。最後にaxiosで、Express側にデータを投げます。
Express側はこんな感じです。
app.jsapp.put('/kanban', async (req, res) => { ;(async () => { const exmentToken = JSON.parse(await fs.readFileSync('./exment_tokens.txt')).access_token // console.log(req.body) await axios.put('https://example.com/api/data/tasks/' + req.body.id, { value: { status: req.body.status } }, { headers: { 'Authorization': 'Bearer ' + exmentToken } }) .then(res => { return res }) .catch(err => { console.error(err.response.data) }) })() })シンプルですね。タスクのIDをエンドポイントに付加して、PUTします。statusも、先程のボードのID番号を指定してあげれば、ステータスの状態と連動します。
動作確認してみよう
Exmentにアクセスし、カンバン上で動かす予定のタスクを確認しておきます。未着手になってますね。
カンバン上でドラッグ&ドロップしてみます。
ドロップ後、リロードしてみましょう。
また、Exment側もリロードして確認してみましょう。
きちんとステータスが変わっているのを確認できました!
課題もあります
まあ、ガントチャートの時ほどではないのですが、課題はあります。
縦方向の並びを変えるのが難しい
無理じゃないとは思うんですが、今回はExment側に並び順のフィールドを作ってないので、並べ替えられません。並べ替えも、並べ替えごとに番号を全部のタスクで書き換えないといけないので、API的にも負荷は大きそうです。
まとめ
というわけで、いかがでしたでしょうか。
この実装ではTrelloほど高機能な実装はしませんでしたが、簡易とはいえ、Exmentでガントチャートも、そしてカンバンも出来るとなると、これでタスク管理したくなってきませんか? もちろん、もっと作り込めば、Backlog/Redmine/Trelloに負けないタスク管理サービスを作ることも可能です。
興味持った方は、ぜひトライしてみてください!
- 投稿日:2020-09-16T10:10:39+09:00
Laravel8 ユーザ認証時にメールアドレス確認の処理を付与する
目的
- LaravelのAuth認証ではメールアドレスはただ入力するだけで、入力されたメールアドレスが正しい物であるかどうかの確認がない
- 入力メールアドレス宛にメールを送信し、初回のログインはメール内のURLからのみ行える様にする
実施環境
- ハードウェア環境
項目 情報 OS macOS Catalina(10.15.5) ハードウェア MacBook Pro (13-inch, 2020, Four Thunderbolt 3 ports) プロセッサ 2GHzクアッドコアIntel Core i5 メモリ 32 GB 3733 MHz LPDDR4 グラフィックス Intel Iris Plus Graphics 1536 MB
- ソフトウェア環境
項目 情報 備考 PHPバージョン 7.4.3 Homwbrewを用いて導入 Laravelバージョン 8.1.0 commposerを用いてこちらの方法で導入→Mac Laravelの環境構築を行う MySQLバージョン 8.0.19 for osx10.13 on x86_64 Homwbrewを用いてこちらの方法で導入→Mac HomebrewでMySQLをインストールする 前提条件
- 前述した実施環境に準ずる環境が整っていること。
- Laravelアプリが作成され、アプリの起動、ブラウザからの確認ができる状態になっていること。
- Authを用いたユーザ認証機能がすでに付与されていること。
前提情報
- 筆者は新規でLaravel8のアプリを作成して本検証を実施する。
読後感
- LaravelのAuth認証にメールアドレスの確認処理を付与することができる。
- ユーザ認証情報入力→メール受信→メール内のURLから初回ログインをしてもらう。
概要
- .envの修正
- モデルファイルの修正
- ルーティング情報の修正
- コントローラファイルの修正
- 確認
詳細
.envの修正
アプリ名ディレクトリで下記コマンドを実行して.envファイルを開く。
$ vi .envMAIL_MAILERの設定を下記の様に修正する。メール送信は行われず、情報がLaravelのログに出力される。
アプリ名ディレクトリ/.envMAIL_MAILER=log下記に修正後の.envファイルの全体の内容を記載する。
アプリ名ディレクトリ/.envAPP_NAME=Laravel APP_ENV=local APP_KEY=アプリキーの記載は各個人で異なります。 APP_DEBUG=true APP_URL=http://localhost LOG_CHANNEL=stack DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE=DB名 DB_USERNAME=root DB_PASSWORD=皆さんの環境のMySQLのrootユーザのパスワード 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_MAILER=log MAIL_HOST=smtp.mailtrap.io MAIL_PORT=2525 MAIL_USERNAME=null MAIL_PASSWORD=null MAIL_ENCRYPTION=null MAIL_FROM_ADDRESS=null MAIL_FROM_NAME="${APP_NAME}" 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}"モデルファイルの修正
アプリ名ディレクトリで下記コマンドを実行してusersテーブルにリンクするモデルファイルを開く。
$ vi app/Models/User.php下記の様に修正する。
アプリ名ディレクトリ/app/Models/User.php<?php namespace App; // 下記を修正する //use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Contracts\Auth\MustVerifyEmail as MustVerifyEmailContract; use Illuminate\Auth\MustVerifyEmail; // 上記までを修正する use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; // 下記を修正する //class User extends Authenticatable class User extends Authenticatable implements MustVerifyEmailContract { // 下記を修正する //use Notifiable; use MustVerifyEmail, Notifiable; /** * The attributes that are mass assignable. * * @var array */ protected $fillable = [ 'name', 'email', 'password', ]; /** * The attributes that should be hidden for arrays. * * @var array */ protected $hidden = [ 'password', 'remember_token', ]; /** * The attributes that should be cast to native types. * * @var array */ protected $casts = [ 'email_verified_at' => 'datetime', ]; }ルーティング情報の修正
アプリ名ディレクトリで下記コマンドを実行してルーティングファイルを開く。
$ vi routes/web.php下記の様に修正を行う。
アプリ名ディレクトリ/routes/web.php<?php use Illuminate\Support\Facades\Route; /* |-------------------------------------------------------------------------- | Web Routes |-------------------------------------------------------------------------- | | Here is where you can register web routes for your application. These | routes are loaded by the RouteServiceProvider within a group which | contains the "web" middleware group. Now create something great! | */ Route::get('/', function () { return view('welcome'); }); // 下記を修正する //Auth::routes(); Auth::routes(['verify' => true]);コントローラファイルの修正
アプリ名ディレクトリで下記コマンドを実行してコントローラファイルを開く。
$ vi app/Http/Controllers/HomeController.php下記の様に修正を行う。
アプリ名ディレクトリ/app/Http/Controllers/HomeController.php<?php namespace App\Http\Controllers; use Illuminate\Http\Request; class HomeController extends Controller { /** * Create a new controller instance. * * @return void */ public function __construct() { // 下記を修正する //$this->middleware('auth'); $this->middleware('verified'); } /** * Show the application dashboard. * * @return \Illuminate\Contracts\Support\Renderable */ public function index() { return view('home'); } }確認
アプリ名ディレクトリで下記コマンドを実行してローカルサーバを起動する。
$ php artisan serve下記リンクにアクセスする。
下記のページの右上の「REGISTER」をクリックする。
任意の情報を入力し「Register」をクリックする。
下記画面が表示されることを確認する。
アプリ名ディレクトリで下記コマンドを実行してLaravelのログファイルを開く。
$ vi storage/logs/laravel.log開いたログファイルの最終行付近に下記の様な記載があることを確認する。
アプリ名ディレクトリ/storage/logs/laravel.log[Laravel](http://localhost) # Hello! Please click the button below to verify your email address. Verify Email Address: http://127.0.0.1:8000/email/verify/6/1107c7572edff3d7050bd4c68404ff6d0919d508?expires=1598068938&signature=a52ea1e97b368ac4b68dd2183cd3ff84d121b608556673e01c6a1aa2a5715161 If you did not create an account, no further action is required. Regards, Laravel If you’re having trouble clicking the "Verify Email Address" button, copy and paste the URL below into your web browser: [http://127.0.0.1:8000/email/verify/6/1107c7572edff3d7050bd4c68404ff6d0919d508?expires=1598068938&signature=a52ea1e97b368ac4b68dd2183cd3ff84d121b608556673e01c6a1aa2a5715161](http://127.0.0.1:8000/email/verify/6/1107c7572edff3d7050bd4c68404ff6d0919d508?expires=1598068938&signature=a52ea1e97b368ac4b68dd2183cd3ff84d121b608556673e01c6a1aa2a5715161) © 2020 Laravel. All rights reserved.みなさんのlogファイルの「Verify Email Address」の後に書かれたURLにアクセスする。(本記事のリンクは筆者の環境の物なのでアクセスしても正常な処理にならない。)
下記の様なページが表示されたらメールアドレスの確認が完了し初回ログインも完了である。
参考文献
- 投稿日:2020-09-16T01:09:06+09:00
VSCodeのdevcontainerでLaravel・Docker環境を構築する
概要
VSCodeのdevcontainerを使ってLaravel環境構築を解説する記事です。
使用ツールさえ用意すれば高速で環境構築でき、Dockerを使用しているのでチームでの統一された開発環境としても使用できます。
とりあえず動かしたい人はリポジトリリンクからどうぞ。リポジトリ
https://github.com/naoyayamamoto/laravel-docker-sample
テンプレートリポジトリとして用意しているので、雛形としても使用できます。使用ツール
- Docker
- Docker-compose
- Visual Studio Code
- Visual Studio Code Remote - Containers
以上のツールを使用します。
Docker
・Visual Studio Code
についてはインストールの詳細を省きます。
Visual Studio Code Remote - Containers
についてはMicrosoft謹製のVSCodeプラグインです。リンクよりVSCodeにインストールできます。使用方法
リポジトリをVSCodeで開く
$ git clone https://github.com/naoyayamamoto/laravel-docker-sample $ code laravel-docker-sample # codeコマンドが入っていなければVSCodeでリポジトリを開いてくださいdevcontanerを使用してVSCodeを開く
- VSCodeに
Visual Studio Code Remote - Containers
が入っていれば左下にアイコンが追加されているので、それを押してダイアログを開くか、Show All Commandscmd + shift + P
でダイアログを開く。Remote-Containers: Reopen in Container
を実行すると環境の準備が始まります。- 初回はDockerの準備を行うため時間がかかりますが、2回目以降は高速で立ち上がります。
アクセス確認
http://localhost:8000
へアクセスでLaravel初期ページが表示されるはずです。解説
devcontainerを開いた際に
.devcontainer/docker-compose.yml
を元にDocker環境が構築されます。
以下のコンテナを使用しており、それぞれ解説していきます。
- nginx:alpine (web)
- mysql:8 (db)
- VSCode用にカスタマイズしたphp:7-fpm (app)
nginx:alpine (web)
- デフォルトイメージを使用
- フロントとして常に起動するので、
php artisan serve
なしでアクセス可能.devcontainer/docker/nginx/default.conf
を読み込ませ、php-fpmへ処理を流しているmysql:8 (db)
- デフォルトイメージを使用
.devcontainer/docker/mysql/my.cnf
を読み込ませている(日本語基本設定)- 環境変数
MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
MYSQL_DATABASE: laravel
を設定しているので、rootパスワードなし、初期テーブルlaravel
が作成されるVSCode用にカスタマイズしたphp:7-fpm (app)
- devcontainerを開いた際の初期接続コンテナ
.devcontainer/docker/php/Dockerfile
を使用してビルドcomposer
・node
・npm
・yarn
が使用可能- php-fpmをsockで実行するために
.devcontainer/docker/php/php-fpm.d/zzz-www.conf
を読み込ませているDB_HOST: db
を設定しているので、.envでの設定よりこちらが優先されてdbコンテナに接続されますおまけ
本番環境用のDockerfile
本番を想定してビルドを行うDockerfileがリポジトリに追加しているので、カスタマイズの元として使用してください。
マルチステージビルドを使用して、jsのコンパイルとcomposerのインストールを先行して行い、最終イメージを極力小さくしています。
GithubActionsでビルドテストだけは通しています。kubernetesで使用する場合
参考までにkubernetesで使用する場合の設定です。
nginxとphpを1PODとして動かすことを想定しています。
参考deployment.yml
--- apiVersion: v1 kind: ConfigMap metadata: name: nginx-conf data: default.conf: | access_log /dev/stdout main; error_log /dev/stderr warn; server { server_tokens off; sendfile on; tcp_nopush on; tcp_nodelay on; gzip on; gzip_http_version 1.0; gzip_disable "msie6"; gzip_proxied any; gzip_min_length 1024; gzip_comp_level 6; gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript; open_file_cache max=100000 inactive=20s; open_file_cache_valid 30s; open_file_cache_min_uses 2; open_file_cache_errors on; listen 80; root /workspace/public; add_header X-Frame-Options "SAMEORIGIN"; add_header X-XSS-Protection "1; mode=block"; add_header X-Content-Type-Options "nosniff"; index index.html index.htm index.php; charset utf-8; location / { try_files $uri $uri/ /index.php?$query_string; } location = /favicon.ico { access_log off; log_not_found off; } location = /robots.txt { access_log off; log_not_found off; } error_page 404 /index.php; location ~ \.php$ { fastcgi_pass unix:/var/run/php-fpm/php-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; include fastcgi_params; } location ~ /\.(?!well-known).* { deny all; } } --- kind: Service apiVersion: v1 metadata: name: laravel-service spec: selector: app: laravel ports: - protocol: TCP port: 80 targetPort: 80 name: laravel-http --- kind: Deployment apiVersion: apps/v1 metadata: name: laravel labels: app: laravel spec: replicas: 1 revisionHistoryLimit: 10 selector: matchLabels: app: laravel template: metadata: labels: app: laravel spec: containers: - name: app image: myimage volumeMounts: - mountPath: /var/run/php-fpm name: php-fpm-socket - mountPath: /shared name: public-contents lifecycle: postStart: exec: command: ["/bin/sh", "-c", "cp -aT /workspace/public /shared"] - name: web image: nginx:alpine ports: - containerPort: 80 volumeMounts: - mountPath: /var/run/php-fpm name: php-fpm-socket - mountPath: /etc/nginx/conf.d name: nginx-conf - mountPath: /workspace/public name: public-contents volumes: - name: php-fpm-socket emptyDir: {} - name: public-contents emptyDir: {} - name: nginx-conf configMap: name: nginx-conf items: - key: default.conf path: default.conf
- 投稿日:2020-09-16T00:49:39+09:00
Gate権限制御 @canでクエリ発行数が増えちゃった話+対処法
なんだかものすごい権限参照のクエリ重複してる...
①対処
blade内の
@can @endcanの記述を
@if @endifへと変更し、判定に用いる権限の値をviewへと渡す。
ループ処理内でcanが使われていた場合、これが原因の可能性が高いですが以下に続きを...
②原因
@canによる判定をおこなう時にFacadeクラスのインスタンス化、権限参照のDBアクセスが発生します。
今回の自分の場合レコード情報と合わせてアイコン表示をする必要がありました。
この時、ループ処理内で表示/非表示の制御をしていたことからレコード数分のDBアクセスが起こっていました。③対処例
自分の作業でおこなった対処の一例です。
1:①の通りbladeの記述を@can→@ifへ変更
2:controller側でユーザーインスタンスから権限、またはそれに相当する値を取得し、compactもしくはwithでviewへ渡す
3:bladeで受け取り判定に使用大したことはしていませんが、手間をかけずにできたのでこの方法で解決できるパターンもあると思います。
参考記事
@Yorinton様
https://qiita.com/Yorinton/items/604c5b1f3445cb23d565公式
https://readouble.com/laravel/5.7/ja/authorization.html
以上となります。
仕様や現状の改善点など調べておりますので、備忘録ではありますが随時修正していきます。
- 投稿日:2020-09-16T00:49:39+09:00
【備忘録】Gate権限制御 @canでクエリ発行数が増えちゃった話+対処法
なんだかものすごい権限参照のクエリ重複してる...
①対処
blade内の
@can @endcanの記述を
@if @endifへと変更し、判定に用いる権限の値をviewへと渡す。
ループ処理内でcanが使われていた場合、これが原因の可能性が高いですが以下に続きを...
②原因
@canによる判定をおこなう時にFacadeクラスのインスタンス化、権限参照のDBアクセスが発生します。
今回の自分の場合レコード情報と合わせてアイコン表示をする必要がありました。
この時、ループ処理内で表示/非表示の制御をしていたことからレコード数分のDBアクセスが起こっていました。③対処例
自分の作業でおこなった対処の一例です。
1:①の通りbladeの記述を@can→@ifへ変更
2:controller側でユーザーインスタンスから権限、またはそれに相当する値を取得し、compactもしくはwithでviewへ渡す
3:bladeで受け取り判定に使用大したことはしていませんが、手間をかけずにできたのでこの方法で解決できるパターンもあると思います。
参考記事
@Yorinton様
https://qiita.com/Yorinton/items/604c5b1f3445cb23d565公式
https://readouble.com/laravel/5.7/ja/authorization.html
以上となります。
仕様や現状の改善点など調べておりますので、備忘録ではありますが随時修正していきます。
- 投稿日:2020-09-16T00:25:42+09:00
【備忘録】Laravelのエラーメッセージを日本語にしたい場合
エラーメッセージ定義があるのはよいけど日本語にならないものか...
1つずつ設定することも可能ですが、公式ドキュメントや偉大なる先人が日本語化したものがあるので簡単に変更できます。
手順
1:resources/lang 配下に日本語メッセージ用ファイルを作成
@ama_keshi様の記事を参考に作成していただくとスムーズです。お世話になりました。
https://qiita.com/ama_keshi/items/27292949d41fdd8bd930Laravel5.7の時点では、以下のコマンド実行で日本語化用ファイルがまとめて生成できます。
https://readouble.com/laravel/5.7/ja/validation-php.htmlこのページの内容を含め、auth.php、passwords.php、pagination.php,validation.phpの4日本語ファイルをまとめて生成するには、以下のコマンドをプロジェクトのルートディレクトリで実行してください。(Windows環境ではまだ試していません。)
php -r "copy('https://readouble.com/laravel/5.7/ja/install-ja-lang-files.php', 'install-ja-lang.php');" php -f install-ja-lang.php php -r "unlink('install-ja-lang.php');"2:メッセージ編集
好きなメッセージへと個別に変更することも可能ですが、日本語にしたいだけであれば一括で書き換えましょう。公式ドキュメント
https://readouble.com/laravel/5.7/ja/validation-php.html
git minoryorg様
https://github.com/minoryorg/laravel-resources-lang-ja