- 投稿日:2019-12-23T21:43:39+09:00
【Laravel】Requestによるバリデーションは効かせつつ、他は共通エラー
Requestによるバリデーションチェックによるエラー表示は機能させつつ、
他のエラー発生時は共通エラー画面に遷移させる方法です。環境
- PHP:バージョン7.3.7
- Laravel:バージョン5.8
- OS:Windows10
はじめにやったこと
エラーが発生したら、とりあえずエラー画面に遷移させればいいんだよね、
というわけで、デフォルトの記述をコメントアウトし、以下のように変更。
※リダイレクト先は、\routes\web.php で制御しています。\app\Exceptions\Handler.phppublic function render($request, Exception $e) { return redirect('/'); /* 以下はデフォルトの記述 return parent::render($request, $e); */ }問題発生
ところが、問題発生。
Requestを使ったバリデーションチェックで、エラー発生時にも共通エラー画面に遷移してしまった。
この場合はエラー内容を自画面に表示させたいので、これでは困る・・・。解決方法
結論から言うと、\app\Exceptions\Handler.phpで、以下のように記述してあげればOK。
\app\Exceptions\Handler.php<?php namespace App\Exceptions; use Exception; use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; // 以下を追記 use Illuminate\Validation\ValidationException; class Handler extends ExceptionHandler { ## ## 省略 ## public function render($request, Exception $e) { if($e instanceof ValidationException) { // バリデーションエラー発生時 return $this->convertValidationExceptionToResponse($e, $request); }else{ // それ以外は共通エラー画面へ(リダイレクト先は\routes\web.phpで指定) return redirect('/'); } } }use Illuminate\Validation\ValidationException;
して、バリデーションエラーの場合のみ、
return $this->convertValidationExceptionToResponse($e, $request);
で、バリデーションエラーが出るようにする。
それ以外は共通エラー画面へ。もう少し詳しく見てみる
\app\Exceptions\Handler.phpで読み込まれていた、
\vendor\laravel\framework\src\Illuminate\Foundation\Exceptions\Handler.php
を見てみましょう。\vendor\laravel\framework\src\Illuminate\Foundation\Exceptions\Handler.php## ## 省略 ## use Illuminate\Validation\ValidationException; ## ## 省略 ## class Handler implements ExceptionHandlerContract { ## ## 省略 ## // \app\Exceptions\Handler.phpのrenderファンクションの親に当たるメソッド public function render($request, Exception $e) { if (method_exists($e, 'render') && $response = $e->render($request)) { return Router::toResponse($request, $response); } elseif ($e instanceof Responsable) { return $e->toResponse($request); } $e = $this->prepareException($e); if ($e instanceof HttpResponseException) { return $e->getResponse(); } elseif ($e instanceof AuthenticationException) { return $this->unauthenticated($request, $e); } elseif ($e instanceof ValidationException) { // この部分を拝借 return $this->convertValidationExceptionToResponse($e, $request); } return $request->expectsJson() ? $this->prepareJsonResponse($request, $e) : $this->prepareResponse($request, $e); } ## ## 省略 ##上のように、バリデーションエラーに関する記述があったので、その部分を拝借しました。
※「ValidationException」で検索してみてください。感想
うまく条件分岐させれば、他のエラー発生時にも、応用効きそう。
そのうちやってみようと思います。参考
以下を参考にさせていただきました。
ありがとうございました!
- 投稿日:2019-12-23T19:21:26+09:00
Laravel Mixで出力されたJavaScriptファイルのパッケージ構成比をsource-map-explorerで調べる
Laravel Mixで出力されたJavaScriptファイルのファイルサイズが大きくて困っているとき、
まずはどのパッケージがどれぐらいのサイズを占めているか調べると思います。
そこでどうやって構成比を調べるかを「webpack バンドル サイズ」などで検索したところ、source-map-explorerというパッケージを見つけました。https://github.com/danvk/source-map-explorer
Getting started
サクッと使ってみましょう。
まず用意するのはLaravelアプリで、最新の6を落としてきてnpm install
まで済ませたところから始めます。
次に今回の肝であるsource-map-explorerをインストールします。$ npm install --save-dev source-map-explorer
package.json
にanalyze
コマンドを追加しましょう。{ ... "scripts": { ... "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", "analyze": "source-map-explorer 'public/js/*.js'" }, ... }そしてJavaScriptファイルを出力した後に
analyze
を実行します。$ npm run dev $ npm run analyze > @ analyze > source-map-explorer 'public/js/*.js' public/js/app.js Unable to find a source map. See https://github.com/danvk/source-map-explorer/blob/master/README.md#generating-source-mapsソースマップがないと怒られました
webpack.mix.js
を修正します。mix.js('resources/js/app.js', 'public/js') .sass('resources/sass/app.scss', 'public/css') .sourceMaps();
npm run production
でないとソースマップが出力されません。$ npm run production $ npm run analyze実行するとブラウザで上記のような画面が表示されます。
要素をクリックするとさらに詳しく調べられたりします。
シンプルでわかりやすいですね参考URL
https://qiita.com/qrusadorz/items/cd05ffe3ad08754b9e65
https://create-react-app.dev/docs/analyzing-the-bundle-size
https://github.com/danvk/source-map-explorer
- 投稿日:2019-12-23T15:36:29+09:00
TechTrain開発がワクワクする5つの理由
株式会社TechBowl が運営している TechTrain の開発ディレクションを担当しています手島(@tejitak)です。【Mentor Ver.】TechTrain Advent Calendar 2019 23日目の記事です。
今回の記事では、TechTrain自体の開発の内側について少し書きます。普段TechTrainを使っていただいているユーザーの皆様/ご利用を検討中の方々/メンターの方々向けに、サービスの開発チームが普段どのようにサービス開発を進めているか、少し知っていただければ幸いです。そして、あわよくばTechBowlの開発チームに興味を持っていただいたらジョインも...是非!
2019年後半開発チームが行ってきたこと
2019年7月にチーム体制が出来上がり本格的に開発が始まりました。それから現在に至るまでの半年間ほど主に以下のようなタスクに従事していました。
- AWS + Laravel + Nuxt + TypeScriptによる技術スタックの変更
- ミッション機能の開発
- 新機能の開発
- 管理画面/Slack連携など運用チーム向けの開発
- Redashを用いたデータ可視化ツールの構築
- ユーザー・メンターヒアリングに基づく継続的改善活動
今現在の開発メンバーはフルタイム1名 + パートタイム3名(私含む)。フロント、サーバー、フルスタックと、それぞれ強みがうまく分かれている開発メンバーが自律的に動き、改善活動と新機能開発が活発、Trainならぬ機関車のごとくワクワクする開発現場となっています。
そんなTechTrainの開発がなぜワクワクするか?その理由を5つ記載します!
ワクワクする理由1 - 技術スタック「AWS + Laravel + Nuxt + TypeScript」が良い
今現在はLaravel + Nuxt + TypeScriptの技術スタックで快適に開発を進められています。この環境になるまで長い道のりがありました。
TechTrain開発秘話
私が開発に参画した頃には2019年春にβ版として既にサービスは稼働していました。
その時点ではAPIはPythonベース、ユーザー向け画面はNuxt、管理画面はReactで作られており、完成度は中途半端な状況でした。実はβ版の時点で多くの問題が発覚しており、βの開発をパートタイムで担当されていた方も忙しく、それを私が引き継ぐ形になりました。例えば、既に発覚していた致命的な問題…
- Firebase 認証に関連し一部のユーザーが永遠にログインできない
- ユーザー登録時の一部のフォーム情報が全くDBに保存されていない
- 管理画面が一部の情報の閲覧しかできず、更新機能がない
その他開発者・ユーザー共に悩ませる問題が多数あることが発覚…
「この開発案件は…もしかして…やばい?」
“誰がなぜそんなことをしてしまったのか。
なってしまったものは、なってしまったもの。
責任追求はせずに、改善をすることに決めた。“以下技術スタックの変遷についてまとめます
APIはPython Flask → PHP Laravelへ
開発チーム体制を作り、まず取り組んだことはLaravelの導入でした。
PHP/LaravelはWebサーバーサイドを実装するにあたって、エンジニア間での共通認識度が高いフレームワークであり、情報も多く担当できるエンジニアの人数も多いです。元々β版のAPIはPython/Flaskで作られていましたが、今後のエンジニア採用も見据えると、β版から正式版にリリースするタイミングで書き換えてしまう方が良いという判断をし、2019年7月技術スタックの大きな変更に着手を始めました。管理画面はReact → Laravel Adminへ
運営チームの使う管理画面はβの時点ではReactで作りかけのものがありましたが、実際に使って運用できるレベルのものではありませんでした。そこでAPIをLaravelに置き換えるのと同時に管理画面はLaravelの管理画面ジェネレーターを使う判断をしました。
管理画面ジェネレーターは多く存在しますが、これまでいくつかLaravelの管理画面ジェネレーターを使ってきた中でオススメのLaravel Adminを使うことにしました。簡単にDBと紐づいた一覧ページや編集フォームページを作ることができ、かつ、カスタマイズ性が高いものです。2019年8月には、めでたく管理画面とAPIの部分がLaravelに移行完了しました。GCPからAWSへ
GCPによりデプロイを行なっていましたが、開発メンバーとしてRDS、S3、Elastickbeanstalk/ECSなどを利用したい気持ちが強く、また、今のところGCPの強みであるBigQueryやコンピューティング周りは使用しないサービスであるためAWSへのインフラの移行を行いました。
幸運にもAWSのスタートアップ支援のクレジットも獲得でき、元々β版をホスティングしていたGCPからAWSへ移行しました。
JavaScript → TypeScriptへ
TechTrainのユーザーの皆様が使っているフロント側の実装はβ時点でNuxtで作られていまして、そのソースコードを引き継いで開発をすることにしました。
そこでTechTrain開発に参画しているフロントエンドのエキスパート@y_temp4さんが以下をまるっと行なってくださいました。
- TypeScript化 / Vuex-guardianの導入
- Vueコンポーネントの疎結合化 / 複雑化していた実装のリファクタリング
- デプロイ周りの対応
- DevTools (Lint, Prettierなど)の導入
- Jestによるフロントエンドテストの導入
- などなど
TS化の恩恵で、型推論によりコードを実際にブラウザで実行する前に多くのコーディングミスの検知が可能になり、堅牢性も高く、生産性も高くなりました!
SSR(サーバーサイドレンダリング)の導入へ
よりSEO的に効果の高いレンダリング方式であるSSR形式でのデプロイもできるようになりました。
少々ハマりどころも多いですが、TechTrainのSEOやパフォーマンス向上を実現できました。常に新しい技術の取り組みも積極的に行なっています。DX(Developer Experience)の向上
このような技術的変遷が数ヶ月で実現され、現在はメンバー自身が快適&自律的に気持ちよく働きたくなる環境が出来上がりました。
毎日githubのPRがバンバン作られ、レビューがされ今のTechTrainの改善活動・新機能開発が行われています。ワクワクする理由2 - フレキシブルな働き方で進められる
プロフェッショナルが集まったリモート開発チーム
現在Techbowlの開発メンバーはフルタイム社員メンバー1名以外の3名はリモートで開発を行っています(今のところ)。
Slack上の本日宣言のルール(参考: 毎朝のSlack一言発言で劇的に生産性が上がる話 )は必須としていますが、稼働時の厳密なコアタイムは設けておらず、働く場所も自由です。開発タスクはGithubのProjectとIssueで管理。
開発定例は2週間に一度のペースでzoomにて行い、各開発メンバーが以下のフォーマットでこれから着手するタスクの明確化・発言をしてもらっています。
- What I did (着手したこと)
- What I will do (これから着手すること)
- Blocking issues (何か困っているところ・課題があれば共有)
特に3つ目が重要で、各自困っているところがあれば随時共有して解決策をディスカッションしたりもしています。
現在の開発メンバーはスキルが高く、自律的に動けるプロフェッショナルなメンバーが揃っているため、リモートワークであってもフレキシブルな働き方を実現でき、圧倒的に高い生産性も実現できています。
参考: リモートワークがメインのエンジニアが普段意識していること (フロントエンド担当の@y_temp4さんの記事)
ワクワクする理由3 - 新機能立ち上げの「仮説→検証フェーズ」に携われる
TechTrainでは「ユーザーヒアリング→仮説を立てる→実装する→検証する」を、おざまささん率いるビジネスチームと一緒に開発を迅速に進めています。
TechTrainの開発ではサービスをグロースさせること・成功させることを第一に考えていますので、顧客の意見を反映させていく「仮説→検証フェーズ」は常に意識しなくてはなりません。
スタートアップのサービスに関わる開発者は、開発そのもの自体が目的になってしまわないように注意が必要です。TechTrainでは随時上がってくるビジネス要件やユーザーヒアリングの結果を開発チームがきちんと拾うことを心がけ、優先度を決めて開発タスクに落とし込んでいきます。
例えば2019年10月にリリースしたミッションという機能。ビジネスチームのヒアリングの結果、MVP(Minimum Viable Product)として実装されました。
機能の検証結果を早く知りたいということに注力してリリースを急ぎました。正直いうとリリース時は開発者としてはデザインや細かい点はまだまだ直せるなという点があると認識しつつのリリースです。
どこかのシリコンバレーの格言引用
“自分がリリースしたものが恥ずかしくないなら、リリースが遅すぎるということを肝に銘じよう。“結果、ミッションの事業としての仮説は大きく間違っておらず(もちろん改善案は見つかものの)、現在は機能を利用するユーザーも企業側も増え、TechTrainのメイン機能として進めています。
仮に当初の仮説が大きく外れていたとしても、その機能は優先度を落とし別の優先度の高いタスクを強めていけば良い、という「仮説→検証」の考え方に基づいた繰り返しの早い開発を今後も意識的に行っていきます。
ワクワクする理由4 - サービスが成長しているのを体感できる
ミッションなどの新機能の追加後特に、口コミやビジネスチームの広報アクティビティのおかげもあり、日々ユーザー(ユーザーもメンターもクライアント様)が着実に増えています。
開発メンバーの半数は実はTechTrain上のメンターとしても登録されています。ミッション機能を公開した時期から、私への面談の数も増えて、自分の作っているサービスが使われているな、という実感が持てています。
開発者たるもの、自分が作ったものを誰かに使ってもらえることが楽しみですよね。
ワクワクする理由5 - メンターと定期的に出会える場が提供されている!
現在TechTrain上に登録されているメンターの数は80名近くいます。こちらからメンター一覧を見ることができます。
TechTrainに登録されている豪華メンター陣は著名な大企業/メガベンチャー/スタートアップに所属している方々がほとんどです。メンターこそがTechTrainの大きなアセットであり、メンター同士のシナジーも生み出したいと考えています。
普段開発担当をしている際にはヒアリングやお知らせなどを通じてオンライン上の付き合いがメインですが、実は定期的に、メンター同士がオフラインで会えるメンター向けmeetupが開催されています。そのほかにもHackbowlのメンターとしてメンター同士交流している方も多いです。
メンター同士の横のつながりが強くなる仕掛けも来年以降より増えていくと思います。
最後に
TechTrainの開発がどのように進んでいるかについて、現在開発のディレクションを行なっている立場から少しご紹介をしました。来年以降も引き続き機関車のごとく開発を進めていきますのでよろしくお願いします!
サービスに興味を持った方はぜひユーザー登録をしてみてください。
そして、、TechTrainの開発に興味を持っていただいたエンジニアの方はフルスタック・フロント・サーバー全方面で募集中ですので、ぜひお気軽に一声お願いします!
- 投稿日:2019-12-23T12:28:58+09:00
【laravel】sitemap.xmlの更新をしたくない気持ち(※未解決)
それはどんな気持ちか
- ルートを変更する度にsitemapの更新したくないな
- 特定のミドルウェアルートグループのgetルートが全てsitemap.xmlに勝手に登録されたらいいのにな
※ 未解決
全てのルートの取得方法の確認も合わせてやってみます。
やってみる
想定する流れ
- ルートを取得する
- sitemap.xmlに出力
ルートを取得する
参考
このコマンドでやっていることが参考になりそうなので、参考にしてみる。
$ php artisan route:list
Illuminate\Foundation\Console\RouteListCommand
を読む。RouteListCommand// ※ use Illuminate\Routing\Router; /** * Create a new route command instance. * * @param \Illuminate\Routing\Router $router * @return void */ public function __construct(Router $router) { parent::__construct(); $this->router = $router; } /** * Execute the console command. * * @return void */ public function handle() { if (empty($this->router->getRoutes())) { return $this->error("Your application doesn't have any routes."); } if (empty($routes = $this->getRoutes())) { return $this->error("Your application doesn't have any routes matching the given criteria."); } $this->displayRoutes($routes); }
$this->router->getRoutes()
で全てのルートを取得し、ルート毎に属性(name,middlewareなど)をそれぞれ処理している。今回はミドルウェアとメソッドが欲しいのでそれらを確認してみます。
ミドルウェアの取得は
RouteListCommand/** * Get before filters. * * @param \Illuminate\Routing\Route $route * @return string */ protected function getMiddleware($route) { return collect($route->gatherMiddleware())->map(function ($middleware) { return $middleware instanceof Closure ? 'Closure' : $middleware; })->implode(','); }ルート毎にミドルウェアを取得(gatherMiddleware)してクロージャかの判断などしつつコレクション化しているようです。
メソッドの取得は
RouteListCommand/** * Get the route information for a given route. * * @param \Illuminate\Routing\Route $route * @return array */ protected function getRouteInformation(Route $route) { return $this->filterRoute([ 'domain' => $route->domain(), 'method' => implode('|', $route->methods()), 'uri' => $route->uri(), 'name' => $route->getName(), 'action' => ltrim($route->getActionName(), '\\'), 'middleware' => $this->getMiddleware($route), ]); }
$route->methods()
で取れるようです。取得してみる
とりあえずコントローラに直接書いてみます。
SiteMapController<?php namespace App\Http\Controllers; use Illuminate\Routing\Router; class SiteMapController extends Controller { /** * @var Router */ private $router; public function __construct(Router $router) { $this->router = $router; } public function index() { $uris = []; foreach ($this->router->getRoutes() as $route) { if (!in_array('front', $route->gatherMiddleware()) || !in_array('GET', $route->methods())) continue; $uris[] = $route->uri; } } }取得出来る$urisはこんなかんじになる
適当な例[ 0 => "/" 1 => "article/{Article}" 2 => "neko" 3 => "inu" ]問題
ルートパラメータの存在するルートを全て取得する方法を見つけられていない、のでここでは一度無視する?
モデル結合ルートに限れば、既に存在するURIは定義されているということになるはずなので、その辺でうまいこと取得出来ないだろうかと考えています。
もう少し検証しますが、いい方法があったら教えて欲しいです。sitemap.xmlに出す
SiteMapController<?php namespace App\Http\Controllers; use Illuminate\Routing\Router; use Illuminate\Support\Facades\Response; class SiteMapController extends Controller { /** * @var Router */ private $router; public function __construct(Router $router) { $this->router = $router; } public function index() { $uris = []; foreach ($this->router->getRoutes() as $key => $route) { if (!in_array('front', $route->gatherMiddleware()) || !in_array('GET', $route->methods())) continue; $uris[] = $route->uri; } $view = view('site_map.index', compact('uris')); return Response::make($view, '200')->header('Content-Type', 'application/xml'); } }site_map.index<?php echo '<?xml version="1.0" encoding="UTF-8" ?>' . "\n"; ?> <urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> @foreach($uris as $uri) <url> <loc>{{ url($uri) }}</loc> <changefreq>daily</changefreq> </url> @endforeach </urlset>おわり
楽したいのでもっとうまい方法考えます。
ただ些細な更新ストレスを減らすための行為で制限を増やすと逆説的にストレスが増えちゃいそうなので、laravelが素直に出来る範囲ぐらいの程々で諦めるべきかもしれません。
- 投稿日:2019-12-23T11:00:09+09:00
レンタルサーバーでLaravelを動かす際の環境構築手順とTips
はじめに
この記事はLaravel Advent Calendar 2019の23日目の記事です
過去に培ったレンサバ(さくらサーバ、バリューサーバ)でLaravelを動かすためのアレコレを書いていきます.
.
.という予定だったんですが、さくらサーバーでの環境構築手順メモしか手元に残ってなかったのでほぼさくらサーバーでの環境構築手順になります(主語でかで申し訳ないです)
AWSとかわからんからレンサバでLaravelを動かす
というのはまあ半分冗談で、大体がコストになるとか、先方指定とかで最終的にレンタルサーバーにしてくださいって言われるので、世間的にもそうなのかな?と思いつつ記事書いていきます。
このTipsが誰かの役に立てれば我幸です。情報の鮮度的に19年9月ごろまでの話で今現在以下の通りでない可能性も無きにも非ずです
あとここで想定しているLaravelのバージョンは5.7~5.8系です。さくらのレンタルサーバーで環境構築
※ローカルで作成してプロジェクトがGitホスティングサービス(GitLabとかGithub)上に上がってる前提です。
1,sshでログイン
おそらく借りた直後にssh接続すると以下のような画面になると思います。
FreeBSD 11.2-RELEASE-p5 (GENERIC) #0: Tue Nov 27 09:33:52 UTC 2018 Welcome to FreeBSD! % % ls -la total 56 drwx------ 9 testtest users 512 Dec 13 19:23 . drwxr-xr-x 160 root wheel 3584 Jan 18 10:34 .. -rw-r--r-- 1 testtest users 773 Nov 29 17:01 .cshrc -rw-r--r-- 1 testtest users 258 Nov 29 17:01 .login -rw-r--r-- 1 testtest users 167 Nov 29 17:01 .login_conf -rw-r--r-- 1 testtest users 0 Nov 29 17:01 .php.module -rw-r--r-- 1 testtest users 762 Nov 29 17:01 .profile -rw-r--r-- 1 testtest users 980 Nov 29 17:01 .shrc drwx------ 2 testtest users 512 Nov 18 2009 .spamassassin drwx------ 2 testtest users 512 Nov 18 2009 .ssh drwx------ 3 testtest users 512 Nov 18 2009 MailBox drwx------ 2 testtest users 512 Nov 18 2009 db drwxr-xr-x 2 testtest users 512 Jan 28 2015 sakura_pocket drwxr-xr-x 2 testtest users 512 Nov 18 2009 sblo_files drwxr-xr-x 2 testtest users 512 Nov 18 2009 www2,リポジトリ用のディレクトリを生成する
root
ディレクトリで生成する。以下、ディレクトリ名
repos
で作成した例% mkdir /home/testtest/repos % ls -la total 60 drwx------ 10 testtest users 512 Jan 18 16:10 . drwxr-xr-x 160 root wheel 3584 Jan 18 10:34 .. -rw-r--r-- 1 testtest users 773 Nov 29 17:01 .cshrc -rw-r--r-- 1 testtest users 258 Nov 29 17:01 .login -rw-r--r-- 1 testtest users 167 Nov 29 17:01 .login_conf -rw-r--r-- 1 testtest users 0 Nov 29 17:01 .php.module -rw-r--r-- 1 testtest users 762 Nov 29 17:01 .profile -rw-r--r-- 1 testtest users 980 Nov 29 17:01 .shrc drwx------ 2 testtest users 512 Nov 18 2009 .spamassassin drwx------ 2 testtest users 512 Nov 18 2009 .ssh drwx------ 3 testtest users 512 Nov 18 2009 MailBox drwx------ 2 testtest users 512 Nov 18 2009 db drwxr-xr-x 2 testtest users 512 Jan 18 16:10 repos // これ drwxr-xr-x 2 testtest users 512 Jan 28 2015 sakura_pocket drwxr-xr-x 2 testtest users 512 Nov 18 2009 sblo_files drwxr-xr-x 2 testtest users 512 Nov 18 2009 www3,秘密鍵を生成
サーバーで作成した鍵を各々使っているGitホスティングサービス(GitlabやらGithubやら)から
pull
するのに秘密鍵を生成する。
ssh
ディレクトリがある場所でssh-keygen -t rsa -b 4096 -C "メールアドレス"
を実行その後、
.ssh/id_rsa.pub
の中身を確認し、GitLabやGithubのプロジェクトへ登録する% ssh-keygen -t rsa -b 4096 -C "hoge@example.com" Generating public/private rsa key pair. Enter file in which to save the key (/home/testtest/.ssh/id_rsa): Enter passphrase (empty for no passphrase): Enter same passphrase again: Your identification has been saved in /home/testtest/.ssh/id_rsa. Your public key has been saved in /home/testtest/.ssh/id_rsa.pub. The key fingerprint is: SHA256:************************************************** hoge@example.com The key's randomart image is: +---[RSA 4096]----+ | B .B..o++B=+| | .+o. o.=.+=| |B .o o. . .| |. o o . B . | |. o o . o. | | . oo . oo.| |. B .o. . . +.| |. . + . ++| | B . B ..*| +----[SHA256]-----+ % % ls -la total 60 drwx------ 10 testtest users 512 Jan 18 16:10 . drwxr-xr-x 160 root wheel 3584 Jan 18 10:34 .. -rw-r--r-- 1 testtest users 773 Nov 29 17:01 .cshrc -rw-r--r-- 1 testtest users 258 Nov 29 17:01 .login -rw-r--r-- 1 testtest users 167 Nov 29 17:01 .login_conf -rw-r--r-- 1 testtest users 0 Nov 29 17:01 .php.module -rw-r--r-- 1 testtest users 762 Nov 29 17:01 .profile -rw-r--r-- 1 testtest users 980 Nov 29 17:01 .shrc drwx------ 2 testtest users 512 Nov 18 2009 .spamassassin drwx------ 2 testtest users 512 Jan 18 16:13 .ssh drwx------ 3 testtest users 512 Nov 18 2009 MailBox drwx------ 2 testtest users 512 Nov 18 2009 db drwxr-xr-x 2 testtest users 512 Jan 18 16:10 repos drwxr-xr-x 2 testtest users 512 Jan 28 2015 sakura_pocket drwxr-xr-x 2 testtest users 512 Nov 18 2009 sblo_files drwxr-xr-x 2 testtest users 512 Nov 18 2009 www
.ssh
へ移動して生成されたか確認% cd .ssh/ % ls -la total 16 drwx------ 2 testtest users 512 Jan 18 16:13 . drwx------ 10 testtest users 512 Jan 18 16:10 .. -rw------- 1 testtest users 3247 Jan 18 16:13 id_rsa -rw-r--r-- 1 testtest users 742 Jan 18 16:13 id_rsa.pub % cat ~/.ssh/id_rsa.pub ssh-rsa AAAAB3NzaC1yc2EAAAAE/0H( 略 )fdg/tFm4WqQ== hoge@example.com4,秘密鍵を各々のサービスのプロジェクトへ登録
各々使っているGitホスティングサービス(GitlabやらGithubやら)上へ登録する。
GitLabの場合、
プロジェクトを開き、メニューから「Settings」→「Repository」→「Deploy Keys」のタブを開き、「key」に
.ssh/id_rsa.pub
の中身をコピー&ペーストする。
「title」にはわかりやすい名前(サーバー名)を、「Write access allowed」のチェックボックスに関してはそのままで「Add Key」をクリックして完了以下はGitHubの画面だがほぼ同じなので参考になる
5,
git clone
するターミナルに戻り、先程作ったreposディレクトリへ移動し、
git clone
コマンドを実行% cd repos/ % ls -la total 8 drwxr-xr-x 2 testtest users 512 Jan 18 16:10 . drwx------ 10 testtest users 512 Jan 18 16:10 .. % git clone git@gitlab.com:*******/test-project.git Cloning into 'test-project'... The authenticity of host 'gitlab.com (35.231.145.151)' can't be established. ECDSA key fingerprint is SHA256:HbW3g8zUjNSksFbqTiUWPWg2Bq1x8xdGUrliXFzSnUw. No matching host key fingerprint found in DNS. Are you sure you want to continue connecting (yes/no)? yes Warning: Permanently added 'gitlab.com' (ECDSA) to the list of known hosts. remote: Enumerating objects: 1145, done. remote: Counting objects: 100% (1145/1145), done. remote: Compressing objects: 100% (416/416), done. remote: Total 1145 (delta 756), reused 1078 (delta 695) Receiving objects: 100% (1145/1145), 452.28 KiB | 640.00 KiB/s, done. Resolving deltas: 100% (756/756), done. % ls -la total 12 drwxr-xr-x 3 testtest users 512 Jan 18 16:46 . drwx------ 10 testtest users 512 Jan 18 16:10 .. drwxr-xr-x 4 testtest users 512 Jan 18 16:46 test-projectちなみにサーバーに二つの環境(ステージング環境・本番環境)作る場合は以下の例のように、
/repos
ディレクトリ以下にstaging
とproduction
のディレクトリを作りそれぞれでgit clone
する。[testtest@www4043 ~/repos]$ mkdir stg [testtest@www4043 ~/repos]$ mkdir production [testtest@www4043 ~/repos]$ ls -la total 16 drwxr-xr-x 4 testtest users 512 Jan 18 18:53 . drwx------ 12 testtest users 512 Jan 18 18:03 .. drwxr-xr-x 2 testtest users 512 Jan 18 18:53 production drwxr-xr-x 3 testtest users 512 Jan 18 18:40 stgここからは主に
production
ディレクトリ上での作業を例として説明していきます。【番外】シェルをbashに変更する
個人的に
bash
に慣れているのでchsh
から変更する方法も載せておきます。
which
コマンドでbashの実行ファイルを確認。
chsh
コマンドでオプション-s
(--shell)を指定し、ログインユーザーのシェルを変更する。% which bash /usr/local/bin/bash % chsh -s /usr/local/bin/bash Password: chsh: user information updated今後もこのbashを使用するためのファイルを作る。
root
へ移動してコマンドを叩く% vi .bash_profile以下を記述する
PATH=$PATH:$HOME/bin:$HOME/usr/local/bin export PATH保存(
!wq
)してssh
ログアウト→再ログインする[testtest@www4043 ~]$ cat .bash_profile PATH=$PATH:$HOME/bin:$HOME/usr/local/bin export PATHこれでお馴染みのbashが使えるようになる
6,composerをインストールする
さくらサーバーはComposer(PHPのパッケージ管理システム)が入ってない1
% composer -h composer: Command not found.ちなみにComposerとは
Composer は、PHP のプロジェクトが必要とするライブラリやパッケージを管理する「ライブラリ依存管理ツール」です。
root
へ移動してコマンドを叩く// rootへ移動 $ cd ~ // ディレクトリ生成 //「-p」オプションには「ディレクトリが存在しない場合は、自動で作成される」かつ「作ろうとしているディレクトリが存在していてもエラーにしない」 $ mkdir -p usr/local/bin/ // curl コマンドでcomposerをダウンロード&インストール // 「-sS」でエラーメッセージ表示 // 「--install-dir」で指定のディレクトリへインストールできる $ curl -sS https://getcomposer.org/installer | php -- --install-dir=usr/local/bin/ // composer.pharを上階層のcomposerディレクトリへ移動 $ mv usr/local/bin/composer.phar usr/local/bin/composerインストール後、入ってることを確認する(※
.bash_profile
にパスを通しておくこと)[testtest@www4043 ~]$ composer -v ______ / ____/___ ____ ___ ____ ____ ________ _____ / / / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/ / /___/ /_/ / / / / / / /_/ / /_/ (__ ) __/ / \____/\____/_/ /_/ /_/ .___/\____/____/\___/_/ /_/ Composer version 1.8.0 2018-12-03 10:31:167,プロジェクト内で
composer install
するプロジェクトのディレクトリへ移動後、
composer install --no-dev
コマンドを実行[testtest@www4043 ~/repos/production/test-project/laravel]$ composer install --no-dev Loading composer repositories with package information Installing dependencies from lock file Package operations: 41 installs, 0 updates, 0 removals - Installing doctrine/event-manager (v1.0.0): Loading from cache - Installing doctrine/cache (v1.8.0): Loading from cache // 省略 - Installing psy/psysh (v0.9.9): Loading from cache - Installing laravel/tinker (v1.0.8): Loading from cache Generating optimized autoload files > Illuminate\Foundation\ComposerScripts::postAutoloadDump > @php artisan package:discover Discovered Package: fideloper/proxy Discovered Package: laravel/tinker Discovered Package: nesbot/carbon Package manifest generated successfully.※レンタルサーバーによってはPHPのモジュールがインストールされてなかったりしてエラーが出る場合があります。
リリース前に十分な余裕をもって事前に確認しておきましょう。8,環境設定ファイル(.env)生成
Laravelの標準に
.env.example
があるのでそれをこの環境用に設置するためにコピーコマンドで複製する[testtest@www4043 ~/repos/production/test-project/laravel]$ cp .env.example .env
.env
の中身の例APP_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=homestead DB_USERNAME=homestead DB_PASSWORD=secret // 以下略9,
key:generate
でアプリケーションのキーを発行する
.env
ファイルを作成後、同ディレクトリでphp artisan key:generate
コマンドを実行してください。
Laravelが内部で暗号化に用いるキーになるため、環境ごとに実行するようにしてください。APP_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=homestead DB_USERNAME=homestead DB_PASSWORD=secret // 以下略10,環境ごとの設定を
.env
に書き込むアプリケーションのキーが生成されたことを確認後、
vi
などで必要箇所(DBの設定や環境等)を編集してください。
(黒い画面慣れてないならFTPでつないで編集しても問題ないです)
ここでDBなどの情報が間違っていると次のフェーズでうまく動きませんあと絶対に
.env
ファイルをpublic
以下(外部から参照できる場所)に配置しないようにしてくださいDon't ? put ? your ? .env ? files ? in ? the ? web-server ?directory https://t.co/DWHmT6r6E3 pic.twitter.com/Qp5tun3CZh
— svbl (@svblxyz) September 26, 2018意外と多いみたいです
そして、構築している環境によって
APP_ENV
とAPP_DEBUG
を適切に設定するようにしてください。
(例:本番環境ならAPP_ENV=production
、APP_DEBUG=false
)特に
APP_DEBUG
が本番環境なのにtrue
に設定していて、バグを踏んだ際にWhoopsが表示されるWebアプリを今までに何個も発見しています。
どれだけ.env
ファイルを参照できない場所へ設置していてもWhoopsが表示されて環境変数を見れる状態だとなんの意味もないです。11,DBを作成(
php artisan migrate
)諸々と環境変数を設定後、
artisan
コマンドを実行してDBを構築[testtest@www4043 ~/repos/production/test-project/laravel]$ php artisan migrate ************************************** * Application In Production! * ************************************** Do you really wish to run this command? (yes/no) [no]: > yes Migration table created successfully. Migrating: 2014_10_12_000000_create_users_table Migrated: 2014_10_12_000000_create_users_table Migrating: 2014_10_12_100000_create_password_resets_table Migrated: 2014_10_12_100000_create_password_resets_table // 省略 // php artisan migrate:statusでできてるか確認 [testtest@www4043 ~/repos/production/test-project/laravel]$ php artisan migrate:status +------+-------------------------------------------------------------------------------------+ | Ran? | Migration | +------+-------------------------------------------------------------------------------------+ | Y | 2014_10_12_000000_create_users_table | | Y | 2014_10_12_100000_create_password_resets_table | | Y | 2018_12_20_122201_create_categories_table | +------+-------------------------------------------------------------------------------------+12,リポジトリとpublicとでシンボリックリンクを張る
root
へ戻ってwww
へ移動し、シンボリックリンクを貼ります。[testtest@www4043 ~/www]$ ln -s /home/testtest/repos/production/test-project/laravel/public/ production [testtest@www4043 ~/www]$ ls -la total 8 drwxr-xr-x 2 testtest users 512 Jan 18 19:23 . drwx------ 12 testtest users 512 Jan 18 19:02 .. lrwxr-xr-x 1 testtest users 64 Jan 18 19:23 production -> /home/testtest/repos/production/test-project/laravel/public/たまにうまくパスを指定していなくて失敗することがある(本人談)ので要注意
13、DBに初期値入れていく(※必要あるなら)
DBへマスタデータなど入れておく必要があるなら
Seeder
を実行しておく[testtest@www4043 ~/repos/production/test-project/laravel]$ php artisan db:seed --class=DatabaseSeeder ************************************** * Application In Production! * ************************************** Do you really wish to run this command? (yes/no) [no]: > yes Seeding: BasesTableSeeder Seeding: CategoriesTableSeeder [testtest@www4043 ~/repos/production/test-project/laravel]$これで一通りの準備ができたはずです。
URLへアクセスして正常に動いていることを確認してください。(ちなみにここまでしなくても「hello, world」したいだけなら10の段階ででできるはずです)
バリューサーバーで動かす(おまけ)
本当はバリューサーバーでの環境構築手順も書く予定でしたが、自分用にまとめた資料がどっか逝ったので覚えている限りのTipsを書いておきます。
PHPのバージョンをコンパネから7.2以上へ変更しておく
おそらくPHPのバージョンがデフォルトで5.6になっている1ので、コントロールパネルから7.2以上のバージョンへ変更してください。
でないとLaravelがそもそも動かないです
(確か5.4系だとPHP5.6でも動いたはずですがおすすめしません)
php
のエイリアスを張っておくデフォルトでコマンドをたたく際はコンパネから設定した
PHPバージョン
+実行したいコマンド
を打つ必要があるので、環境構築の初めにphpのエイリアスを張っておきましょう。
でないと、以下のようにphp56cli composer.phar -Vと、
php[version]cli
と書かないとコマンド実行できません。
xmlwriter
がインストールされてないPHPの拡張機能
xmlwriter
が入っていないため、PhpSpreadsheetなどのライブラリが動きませんでした。1
これに関してはレンタルサーバー側が対応してくれるまでどうしょうもないので、xmlwriter
機能を使うライブラリを仕様する場合は注意が必要かなとおわり
- 何回も行っておきますが情報の鮮度的に19年9月ごろまでの話で今現在上記の通りでない可能性も無きにしです。
- ちなみに自分が携わったプロジェクトに関して言えば8割くらい、業務システムはほぼ100%レンサバ指定でした
- ちなみにちなみに明日は前職の後輩@namizatopが記事書くようですよ(お
参考
- さくらのレンタルサーバーでcomposerめも - Qiita
- さくらのレンタルサーバにComposerをインストールする方法 – かきくけ子のブログ
- さくらのレンタルサーバのシェルをbashに変更する方法 – かきくけ子のブログ
2019年9月頃の話ですので現在は異なるかもしれません ↩
- 投稿日:2019-12-23T07:50:36+09:00
モバイル環境でNetwork Errorでハマった。「EC2にDocker Caddy Laravel, S3にVuejsの構成」
指摘などウェルカムです。
クロスオリジン(CORS)に関連するエラーなんだと思うけどPCでは動くが、携帯 IOS Iphone7(古い)からのブラウザ(最新)ではsafariもchromeも動かないって現象で結構ハマった。環境
大体の構成
route53から分岐
-> CloudFront+S3にvuejs
-> ALBからEC2(Ubuntu)にDocker Caddy Laravel解決策
サーバー側のクロスオリジンの設定をちゃんと設定する。
Access-Control-Allow-Origin
Access-Control-Allow-Headers
をざっくり設定して * としていた。
これが原因だった。
ちゃんとフロントs3側のホスト名を指定したら動いた。
この記事で必須と書いてあるので、ん?と思って設定したら動いた。:OK.php <?php namespace App\Http\Middleware; use Closure; class Cors { public function handle($request, Closure $next) { return $next($request) //ちゃんとホスティングしてるs3のドメインを指定 ->header(‘Access-Control-Allow-Origin’, ‘https://hoge.s3.com’) ->header(‘Access-Control-Allow-Methods’, ‘GET, POST, PUT, DELETE, OPTIONS’) ->header(‘Access-Control-Allow-Headers’, ‘X-Requested-With, Content-Type, X-Token-Auth, Authorization’); } }駄目.php<?php namespace App\Http\Middleware; use Closure; class Cors { public function handle($request, Closure $next) { return $next($request) ->header(‘Access-Control-Allow-Origin’, ‘*’) ->header(‘Access-Control-Allow-Methods’, ‘GET, POST, PUT, DELETE, OPTIONS’) ->header(‘Access-Control-Allow-Headers’, ‘X-Requested-With, Content-Type, X-Token-Auth, Authorization’); } }https://whatsupguys.net/programming-learning-135/
調べた履歴
Laravelのルーティングについて結構大事だと思う。
axiosでget, post以外にoptionも使うのでルーティング注意。Route::match(['options', 'patch']'/{test_id}', 'TestController@update'); Route::match(['options', 'delete'], '/{test_id}', 'TestController@destroy');CORSを許可したLaravel製APIサーバーでput, patch, deleteが出来なくて泣いてたけど、ようやく解決出来た話
CSRFが問題か?
X-Requested-Withヘッダか?Laravel/Vue SPAs: How to send AJAX requests and not run into CSRF token mismatch exceptions
https://medium.com/@serhii.matrunchyk/laravel-vue-spas-how-to-send-ajax-requests-and-not-run-into-csrf-tokens-mismatch-exceptions-da3b71b287ab
https://www.techalyst.com/posts/vuejs-axios-laravel-x-csrf-token-x-xsrf-token-csrf-protectionaxiosが悪いのか?
https://qiita.com/terrierscript/items/ccb56b6fc05aa7821c42crossのエラーぽいが。
https://stackoverflow.com/questions/50873764/cross-origin-read-blocking-corb
https://stackoverflow.com/questions/38749605/cors-access-control-allow-origin-on-laravel
https://stackoverflow.com/questions/20035101/why-does-my-javascript-code-get-a-no-access-control-allow-origin-header-is-pr
https://github.com/laravel/framework/issues/13643
https://medium.com/@petehouston/allow-cors-in-laravel-2b574c51d0c1サーバの.htaccessが悪いのか?
https://forum.laragon.org/topic/1435/access-control-allow-origin-is-already-set/6モバイルではそもそも動かないのか?
Axios doesn't work with Android (emulator) raising a Network Error
https://github.com/axios/axios/issues/973
- 投稿日:2019-12-23T04:46:12+09:00
Laravel6系 に Vuetify を入れてみる
こんにばんわ! @ktoshi です。
今回は私が困ったときに縋りつく Laravel についてお話します。
ちなみに私はインフラエンジニアです。メインは。目的
Laravel6 に Vuetify を導入したい。
Laravel6 より Vue.js などが標準ライブラリから外れたため、Laravel5 以前の記事ではそのまま導入ができなくなりました。
毎度導入しているときに複数の記事を見ながら、導入しているので備忘録もかねて。Vuetify とは
公式HP
Vue.js のコンポーネント集です。
ボタンやテーブルなどを描画する際のコンポーネントがあつまっているので、
モダンなデザインを容易に作ることができます。環境
OS: Windows 10 Pro
PHP: 7.3.10Composer インストール
Laravel で使用するパッケージ管理に利用します。
みなさんご存じですよね。多くは語りません。
Composerインストール手順(Windows) を参考されたし。node.js インストール
node.js 自体をサーバとして利用も可能ですが、Laravel では主に Vue.js の
ビルドを目的に利用します。
みなさんご存じですよね。多くは語りません。
Node.js / npmをインストールする(for Windows) を参考されたし。
私の環境ではバージョンは下記でした。node --version v12.13.0 npm --version 6.12.0Laravel インストール
みなさんご存じですよね。多くは語りません。
composer create-project laravel/laravel --prefer-dist <PROJECT NAME>これでカレントディレクトリ以下に PROJECT_NAME のフォルダが作成され、
その配下に Laravel が導入された状態となっています。Vue.js のインストール
先でも述べたように Laravel5 以前であれば、この状態で
npm install
をしてやると
Vue.js が導入できましたが、 Laravel6 以降ではひと手間必要です。
ひと手間が料理をおいしくすると同じくひと手間が Laravel をおいしくします。(知りません# プロジェクトフォルダへ移動 cd <PROJECT NAME> # 必要なライブラリをインストール composer require laravel/ui # 私は Vue を使うねん!と宣言 php artisan ui vue # おら! npm installこれで Vue.js の導入が完了です。
Laravel5 以前でnpm install
を叩いた状態と同じですね。Vuetify インストール
Vuetify は npm(Node.js のパッケージ管理ツール)を用いてインストールします。
(いや、そのコマンドさっき使ってたのに今更説明かい。)# Vuetify の最新パッケージのインストール npm install vuetifyインストールが終われば、Laravel 側で Vuetify を利用するように設定します。
resoureces/js/app.js/** * First we will load all of this project's JavaScript dependencies which * includes Vue and other libraries. It is a great starting point when * building robust, powerful web applications using Vue and Laravel. */ require('./bootstrap'); window.Vue = require('vue'); import Vuetify from 'vuetify'; import 'vuetify/dist/vuetify.min.css'; Vue.use(Vuetify); /** * The following block of code may be used to automatically register your * Vue components. It will recursively scan this directory for the Vue * components and automatically register them with their "basename". * * Eg. ./components/ExampleComponent.vue -> <example-component></example-component> */ // const files = require.context('./', true, /\.vue$/i) // files.keys().map(key => Vue.component(key.split('/').pop().split('.')[0], files(key).default)) Vue.component('example-component', require('./components/ExampleComponent.vue').default); /** * Next, we will create a fresh Vue application instance and attach it to * the page. Then, you may begin adding components to this application * or customize the JavaScript scaffolding to fit your unique needs. */ const app = new Vue({ el: '#app', vuetify: new Vuetify(), });これで Laravel にてVuetify を利用できる状態となりました。
実践
では、実際にVuetify を使ったページを作ってみましょう。
元々用意されているサンプルのファイルを書き換えてみます。resources\js\components\ExampleComponent.vue<template> <v-app> <v-container> <v-row> <v-col> <v-card class="mx-auto"> <v-card-text> <p class="text--primary"> Hello Vuetify World!! </p> </v-card-text> <v-divider></v-divider> <v-card-actions> <v-btn>やったね!!</v-btn> </v-card-actions> </v-card> </v-col> </v-row> </v-container> </v-app> </template>resources\views\welcome.blade.php<!doctype html> <html lang="{{ app()->getLocale() }}"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <!-- CSRF Token --> <meta name="csrf-token" content="{{ csrf_token() }}"> <title>{{ config('app.name', 'Laravel') }}</title> <!-- Styles --> <link rel="stylesheet" href="{{ mix('css/app.css') }}"> </head> <body> <div id="app"> <v-app> <!-- これがさっき修正したファイル --> <example-component></example-component> </v-app> </div> <!-- Scripts --> <script src="{{ mix('js/app.js') }}"></script> </body> </html>次に Vue.js のビルドと Laravel を起動させます。
なお、各コマンドともに実行状態が続くので二つのプロンプトで作業をしてください。# Laravel の起動 php artisan serve Laravel development server started: http://127.0.0.1:8000 # これが出ればOK# ビルドコマンドを発行。watch にしておくと変更を検知して勝手にビルドしてくれるので楽。 npm run watch DONE Compiled successfully in XXXms # これが出ればOKそれぞれが起動すれば
http://localhost:8000/
へ接続してみましょう。
下記のようなページが表示されていればOKです。
まとめ
Laravel6 から標準でなくなった Vue.js のインストールまで行ってしまえば、
後は Laravel5 と同様の方法で Vuetify を利用することが可能です。私は今まで element-ui を主に使っていましたが、今回は Vuetify を利用する、という記事を書いてみました。
なんで変えたかって?飽きたからです。
ただ、実際に Vuetify を使ってみて思ったのはデザインなどは非常に好みでした。
テーブルに関しても element-ui より柔軟でよかったです。
ただ、timepicker のデザインが不満だったのと element-ui の datetimepicker がマジ神だなと感じました。それでは皆様、よい Vuetify ライブをお送りください。
- 投稿日:2019-12-23T00:51:06+09:00
メール認証(アクティベーション)機能を自前で実装【Laravel】
この記事は、認証認可技術 Advent Calendar 2019 の23日目の記事です。
1. はじめに
Laravelって便利ですよね。
Laravel5.7からは、
make:auth
コマンドやMustVerifyEmail
インターフェースを使えばすぐにメール認証機能の実装もできるようになりました。(Laravel6.0からはmake:auth
廃止されましたが)ただ便利な反面、途中から機能追加したり、変更頻度が多いことを見越して自前で実装して柔軟に対応できるようにしておきたいことがあります。
こういったことがあって自前で実装したのでその時の備忘録です。
(APIサーバをつくるときの話で、LaravelはフルスタックフレームワークなのでAPIサーバのみの利用はあまり無かった)少し前の実装なのでうる覚えな部分はありますがご容赦ください。
2. やりたいこと
タイトル通り、メール認証機能の実装です。
私が思っているメール認証機能の定義と流れは以下の通りです。
2.1. メール認証機能の定義
Webサービスでよくある新規登録時にほぼ間違いなくするメールアドレスの有効化のことです。
(メール認証==メール有効化==メールアクティベーション
だと思っています。。。色んな言い方をしてすみません。)この機能がないと偽りのメールアドレスを使って登録することができてしまうのでよく実装されています。(この他にもSMS認証とか)
要するにWebサービスを開発するときの必須機能の一つです。2.2. メール認証機能の流れ
- ユーザがサーバにメールアドレスを入力して送信
- サーバがユーザに入力されたメールアドレスに認証メールを送信
- ユーザが認証メール内のURLにアクセス
- サーバがURLの正誤判定
- 正しい場合、サーバがデータベースにメールアドレスを登録
一般的な流れはこんな感じでしょうか。
誰でも一度はユーザの立場でやったことがある流れだと思います。3. 実装方針
認証コードの作成・確認
認証コードの作成は、メールアドレスとサーバで定義しておいた秘密鍵を用いてハッシュ化をして行います。
確認は、
ユーザがアクセスしたURL内の認証コード==サーバ側に仮保存している認証コード
で行います。認証コードとメールアドレスの紐付け
考える必要があるのは紐付け方法です。
様々な紐付け方法がありますが、今回はデータベースにメール認証用のテーブルを作って、メールアドレスと認証コードをセットで保存するという方法をとります。
メールアドレスの認証が確認できた時点で、このテーブルのレコードは消していきます。おまけ: 様々な紐付け方法
- アクセス先のページでメールアドレスを再入力
- ユーザが手間(=UX悪い)なので却下
- URLにクエリとしてメールアドレスを付与
- ださいので却下、セキュリティ的にも心配(?)
- メールアドレスのセッション保存
- 他端末で認証できないので却下
- 認証ファイルの作成
- 個人的に実装が手間だったので迷った末に却下
- データベース使わなくて済むが小規模なサービスの場合だと誤差
4. 開発環境
- PHP7.3
- Laravel6.0(5.8でも動作確認済)
- MySQL8.0
5. 実装
5.1. テーブル作成(マイグレーション&モデル)
最初に、ユーザ用のテーブルとは別に、メール認証用のテーブルをつくっておきます。
MigrationとModelをつくります。$ php artisan make:model MailVerification -mMigration
mail_authentication
は認証コードを格納するカラムで、
database/migrations/2019_12_23_000000_create_mail_verifications_table.php<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreateMailVerificationsTable extends Migration { public function up() { Schema::create('mail_verifications', function (Blueprint $table) { $table->bigIncrements('id'); $table->string('mail_authentication'); $table->string('mail')->unique(); $table->timestamps(); }); } // 略 }Model
app/Models/MailVerification.php<?php namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Support\Facades\DB; class MailVerification extends Model { protected $table = 'mail_verifications'; protected $guarded = array('id'); public $timestamps = true; protected $fillable =[ 'mail_authentication' , 'mail' ]; }5.2. コントローラ作成
次に、コントローラをつくります。
Controller
store
メソッドで認証コードとメールアドレスを仮保存しておいて、ユーザに認証メールを送ります。
認証メールのURLにアクセスしたらverify
メソッドで確認して、仮保存しておいたレコードを削除し、認証完了メールを送ります。
createActivationCode
メソッドで、認証コードを作成しています。
ACTIVATE_SALT
が秘密鍵です。app/Http/Controllers/Api/MailVerificationsController.php<?php namespace App\Http\Controllers\Api; use Illuminate\Http\Request; use Illuminate\Support\Facades\Mail; use App\Models\MailVerification; use App\Models\User; use App\Mail\MailVerification as MailVerificationMail; use App\Mail\MailVerificationConfimComplete as MailVerificationConfimCompleteMail; use App\Http\Controllers\Controller; define('ACTIVATE_SALT', '[秘密鍵]'); class MailVerificationsController extends Controller { public function store($mail) { $mail_verification = new MailVerification(); $mail_verification->mail_authentication = self::createActivationCode($mail); $mail_verification->mail = $mail; $mail_verification->save(); self::sendMailVerification($mail_verification); } public function verify($active_code) { $mail_verification = self::getMailInfoFromActiveCode($active_code); $mail = $mail_verification[0]->mail; $user = new User(); $user->isVerified($mail); self::destroy($mail_verification[0]->id); self::sendMailVerificationComplete($mail); } public function destroy($id) { $mail_verification = MailVerification::find($id); $mail_verification->delete(); } private function createActivationCode($mail) { return hash_hmac('sha256', $mail, ACTIVATE_SALT); } private function sendMailVerification($mail_verification) { Mail::to($mail_verification->mail) ->send(new MailVerificationMail($mail_verification->mail_authentication)); } private function sendMailVerificationComplete($mail) { Mail::to($mail) ->send(new MailVerificationConfimCompleteMail()); } private function getMailInfoFromActiveCode($active_code) { return MailVerification::where('mail_authentication', $active_code)->get(); } }念のため、Userコントローラも。
store
メソッドでユーザ登録処理をしています。
app()->make()
で別コントローラを呼ぶという力技・・・。app/Http/Controllers/Api/UsersController.php<?php namespace App\Http\Controllers\Api; use Illuminate\Http\Request; use Illuminate\Support\Facades\Hash; use App\Models\User; use App\Http\Controllers\Controller; class UsersController extends Controller { public function store(Request $request) { $user = new User(); $user->name = $request->input('name'); $user->mail = $request->input('mail'); $user->password = bcrypt($request->input('password')); $user->save(); self::callMailVerificationStore($user); return response([], 201); } private function callMailVerificationStore($user) { $mail_varification = app()->make('App\Http\Controllers\Api\MailVerificationsController'); $mail_varification->store($user->mail); } }5.3. ルーティング記述
この当たりで関係がある部分のルーティングを書いておきます。
routes/api.php<?php use Illuminate\Http\Request; Route::group(["middleware" => "api"], function () { Route::post('/user', 'Api\UsersController@store'); Route::get('/verify/{active_code}', 'Api\MailVerificationsController@verify'); });5.4. メール
必要ないかもしれませんがメモ程度に。
app/Mail/MailVerification.php<?php namespace App\Mail; use Illuminate\Bus\Queueable; use Illuminate\Mail\Mailable; use Illuminate\Queue\SerializesModels; use Illuminate\Contracts\Queue\ShouldQueue; define('MAIL_VERIFICATION_SUBJECT', '[メールタイトル]'); class MailVerification extends Mailable { use Queueable, SerializesModels; protected $mail_verification; public function __construct($_mail_verification) { $this->mail_verification = $_mail_verification; } public function build() { $auth_url = config('const.BASE_URL') . '/verify/' . $this->mail_verification; return $this->from(config('mail.from.address')) ->subject(MAIL_VERIFICATION_SUBJECT) ->view('mails.verification', compact('auth_url')); } }簡単にですがBladeの方も。
resources/views/mails/verification.blade.php@extends('layouts.app') @section('content') <div class="container"> <div class="row justify-content-center"> <div class="col-md-8"> <div class="card"> <div class="card-header">メールアドレス認証</div> <div class="card-body"> <a href='{{$auth_url}}'>こちらのリンク</a>をクリックして、メールを認証してください。 </div> </div> </div> </div> </div> @endsection6. おわりに
以上、Laravelでメール認証機能を自前で実装してみた備忘録でした。
ご閲覧ありがとうございました!
質問やアドバイス等ありましたらコメントしてください。