20210611のlaravelに関する記事は10件です。

【Laravel】Laravelの電話番号のバリデーション(編集中)

$request->validate([ 'phone' => ['nullable','regex:/^(0{1}\d{1,3}-{0,1}\d{1,4}-{0,1}\d{4})$/'], ]); 先頭の1桁は0固定 ^(0{1} 先頭は1〜3桁 0固定 \d{1,3}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】Laravel課題:9-11 電話番号のバリデーション

$request->validate([ 'phone' => ['nullable', 'regex:/^[0][0-9]{1,2}-{0,1}[0-9]{3,4}-{0,1}[0-9]{3,4}$/'], ]); 先頭の1桁は0固定 ^(0{1} 先頭は1〜3桁 0固定 \d{1,3}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MAMP環境でLaravelをインストール

こんにちは。 タイトル通りMAMP環境で Laravelをインストールする手順を 書いて行きます。 はじめに かなり初歩的な内容ですが、 今回はQiitaの投稿に慣れていきたい という事で書いてみました。 開発環境 前提に MAMP Composer 上記がインストール済みで それ以降のLaravelインストールの お話しになります。 インストール方法 Laravelのインストールには、 2種類あるようで composer経由 Laravel installer経由 があるようですが、 ここではcomposer経由を書いて行きます。 まず最初に ターミナルで cd /Applications/MAMP/htdocs/ を打ち htdocsへと移動します。 そこで composer create-project laravel/laravel --prefer-dist プロジェクト名 これでLaravelのインストールを開始します。 最後に successfully. と出たらインストール完了です。 ちなみに、 上記のコマンドだとLaravelの最新バージョンが インストールされます。 バージョン指定したい場合 例えば バージョンを6.0系にしたい場合 composer create-project "laravel/laravel=6.*" --prefer-dist プロジェクト名 これでバージョン6.0系になります。 以上です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

laravel セッションのサンプル

ルーティング /routes/web.php <?php Route::get('/session_sample', 'SampleController@sessionSample'); Route::post('/session_sample', 'SampleController@sessionDelete'); コントローラー app/Http/Controllers/SampleController.php <?php namespace App\Http\Controllers; use Illuminate\Http\Request; class SampleController extends Controller{ public function sessionSample(){ $count = session()->get('count', 0); $count++; session()->put('count', $count); return view('samples.counter_sample', [ 'title' => 'セッションを利用するサンプル', 'count' => $count, ]); } public function sessionDelete(){ session()->forget('count'); return 'セッションデータを削除しました。'; } } ビュー resources/views/samples/counter_sample.blade.phpp @extends('layouts.default') @section('title', $title) @section('content') <h1>{{ $title }}</h1> @if($count === 1) <p>初めての訪問です。</p> @else <p>{{ $count }}回目の訪問です。 @endif <form method="post"> @csrf <input type="submit" value="履歴を削除"> </form> @endsection 共通レイアウトファイル <!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>@yield('title')</title> <style> .header_nav { display: flex; list-style: none; padding-left: 0; } .header_nav li { margin-right: 30px; } /* エラーメッセージ用のスタイル */ .error { color: red; } </style> </head> <body> @yield('header') {{-- エラーメッセージを出力 --}} @foreach($errors->all() as $error) <p class="error">{{ $error }}</p> @endforeach @yield('content') </body> </html>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LaravelでAPIを作って簡単なCRUDを作成する

LaravelでAPIを作って簡単なCRUDを作成する LaravelでAPIを作って簡単なCRUD機能を作成。エンドポイントのテストはポストマンで行う。 API resourcesを使ってAPIをつくる。 LaravelにはAPIを簡単に作れるようAPI resources機能があります。 すでにPostモデルとUserモデルは作成済み。Seederを使ってUserとPostのテストデータは作成済み Post.php //データを入力できるようfillableを指定。 protected $fillable = ['user_id', 'title', 'body', 'image']; public function user() { return $this->belongsTo(User::class); } User.php public function posts() { return $this->hasMany(Post::class); } APIルートを作るために apiResource()をつかう。 routes/api.php に下記を追加。 api.php use App\Http\Controllers\PostController; Route::apiResource('posts', PostController::class); Postモデルにresourceクラスを作成する。resourceクラスをつくることでPostモデルからデータを取り出して、JSON形式でデータを返す。 php artisan make:resource PostResource app/Http/Resources/PostResource.php が作成されるので修正。 toArray()メソッドがPostのリソースを配列に変換する。 JSON形式で返して欲しいものを記述。 PostResource.php public function toArray($request) { return [ 'id' => $this->id, 'title' => $this->title, 'body' => $this->body, 'image' => $this->image, 'created_at' => (string) $this->created_at, 'updated_at' => (string) $this->updated_at, ]; } resourceクラスができたのでPostControllerでPostResourceのインスタンスが使えるようになりました。 use App\Models\Post; use Illuminate\Http\Request; //PostResource use App\Http\Resources\PostResource; use Illuminate\Support\Str; //すべてのポストを取得 public function index() { return PostResource::collection(Post::latest()->paginate(5)); } public function store(Request $request) { //Validation $this->validate($request, [ 'title' => 'required', 'body' => 'required', 'user_id' => 'required', 'image' => 'required|mimes:jpeg,png,jpg,gif,svg', ]); $post = new Post; if ($request->hasFile('image')) { $image = $request->file('image'); $name = Str::slug($request->title).'.'.$image->getClientOriginalExtension(); $destinationPath = public_path('/uploads/posts'); $imagePath = $destinationPath . "/" . $name; $image->move($destinationPath, $name); $post->image = $name; } //Postを作成 $post->user_id = $request->user_id; $post->title = $request->title; $post->body = $request->body; $post->save(); //PostをPostResourceとしてJSON形式で返す return new PostResource($post); }  http://127.0.0.1:800/api/posts  にアクセスするとindex()メソッドが呼び出される。apiなので/postsではなく/api/postsにアクセス。 showモデルで特定のポストを呼び出す。show(Post $post)はルートモデルバインディング。 ルートモデルバインディングについてはこちら。 //app/Http/Controllers/PostController.php public function show(Post $post) { return new PostResource($post); } updateメソッドを追加 //app/Http/Controllers/PostController.php public function update(Request $request, Post $post) { $this->validate($request, [ 'title' => 'required', 'body' => 'required', ]); $post->update($request->only(['title', 'body'])); return new PostResource($post); } destroyメソッド。データの削除。そして204を返す。 //app/Http/Controllers/PostController.php public function destroy(Post $post) { $post->delete(); return response()->json(null, 204); } 完成。ポストマンを使ってテストをする。 参考
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelのbladeを使って困った話

Laravelで開発をするときにSEO対策をしっかりしたいということで、フロントをSPAではなくbladeを使って開発することになったのですが、bladeの知見がなかったので色々試しながら開発したことについてまとめてみました。 viewロジックを書く場所に困った まず初めにviewロジックをeloquentモデルにアクセサに記述していたのですが、viewロジックを分離させた方がいいということでrobclancy/presenterを採用することにしました。robclancy/presenterは簡単に言うと、eloquentモデルをDecoratorパターンで拡張してviewロジックを切り離して書けるようにするライブラリです。 eloquentモデル自体にはアクセサなどを増やすことなく、eloquentモデルを拡張するような形でviewロジックを書けることにありがたみを感じていましたが、開発が進むにつれてfatになっていきメンテナンスが難しくなってく印象がありました。 また、viewロジックはrobclancy/presenterで書いていけばいいやという感じで進めていましたが、複数のモデルと関連した画面固有のロジックはどこに書けばいいのだろうと悩んだりしたが、画面固有のviewロジックを記述するオブジェクトをviewに渡してもよかったかもしれないです。eloquentモデルに紐づいているviewロジックだったとしても、一つの画面でしか使わないし他で使い回すべきではないロジックなどは画面固有のオブジェクトに移せばfatになっていくこともある程度は防止できると思います。 フロントからのリクエストデータの構造に影響を受けてしまい困った コントローラに直接ビジネスロジックを書くべきではないということで、サービス層を設けてそこにビジネスロジックを書くようにしていたのですが $data = $request->all(); $service->execute($data); 上記のように配列をそのままサービス層へ渡してしまい、フロントから送出されるデータ構造に影響を受けてしまい、フロントの変更に伴ってサービス層も修正しないといけないという問題が発生していました。 コントローラからサービス層を呼び出すときは、以下のようにリクエストデータをDTOなどに詰め直してそのオブジェクトを渡した方がいいと思います。 $data = new Data($request->all()); $service->execute($data); こうすることでフロントから送出されるデータ構造に変更があっても、データをDTOに詰め直す部分に修正を加えるだけで済み、サービス層への影響は考えなくて良くなります。 フロントとの適切な作業分担うまくいかず困った フロントエンドとバックエンドで担当するエンジニアが異なっていたのですが、テンプレートのコーディングをほぼほぼフロントエンジニアが担当していて、動的な値の埋め込みや条件分岐による表示といったこともフロントエンジニアが作業するといった状態でした。 何が問題かというとテンプレートで使える変数がテンプレートからだと分からなかったり、バックエンドの処理を追う必要が出てしまいフロントエンジニアの作業量が膨らんでしまうので、バックエンドの実装の知識が必要になってくるような箇所についてはバックエンド側で対応した方がいいと思います。 フロントエンジニアにはcssなどが適用されたHTMLファイルを作成してもらい、そのHTMLファイルを元にバックエンドエンジニアがbladeファイルを作成し動的表示を実装するという流れだとフロントエンジニアがbladeやlaravelについて学習する必要が少なくなると思います。 index.html <header> <div class="navi"> ナビメニュー </div> </header> <div class="content"> ユーザー名:田中 太郎 </div> このようなHTMLファイルがあったとすると、ユーザー名などの動的な部分を置き換えてあげて、ナビメニューのような重複する内容の場合は@includeを使って再利用すると楽になると思います。 index.blade.php <header> @include('navi') </header> <div class="content"> ユーザー名:{{ $user->name }} </div> navi.blade.php <div class="navi"> ナビメニュー </div> view composerの使い所に困った view composerを使うとviewに何が渡されているかわかりづらくなるので積極的に使うべきではないという印象ですが、リクエストの内容によらずviewに必ず渡すデータなどはview composerを使った方がいいと思います。 良くあるのがナビメニューやヘッダーにユーザー情報などを表示する場合で、基本的にどの画面でも必要なデータでかつリクエストの内容に影響を受けない場合がほとんどだと思うので使い所になります。 NaviComposer.php <?php namespace App\Http\ViewComposers; use Auth; use Illuminate\View\View; class NaviComposer { /** * @param View $view */ public function compose(View $view) { $view->with([ 'user' => Auth::user(), ]); } } viewを出力する際にControllerとは隔離されて上記処理が行われviewにデータが受け渡されるようになるため、Controllerではルーティング固有の処理に専念することができます。 index.blade.php @include('navi') <div>コンテンツ</div> navi.blade.php {{ $user->name }} ナビメニューのbladeファイルは予め分割しておくといいと思います。 bladeのコーディングに困った IDEはphpstormを使っていたのですが、bladeでコード補完をする方法が分からず開発初期からしばらくはコード補完なしで進めていました。しかし、タイポや変数名が分からず調べるといったことが度々発生してしまい、効率が悪いと感じたので調べたところコード補完を効かせる方法を見つけました。 phpstromを使っている場合は、以下のようにbladeファイル上で変数を定義しておくとコード補完が効くようになります。タイポも減るしコーディングのスピードも向上するので特に理由がなければ記述しておくことをおすすめします。 index.blade.php @php /** @var App\Models\User $user */ @endphp
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.js + Laravel8.4でAxiosのエラーハンドリングしてみる

【開発環境】 Windows 10 HOME PHP 7.4.7 MySQL Laravel Framework 8.4 vue : 2.6.12 vue-router : 3.5.1 vee-validate : 3.4.9 vue-axios-interceptors : 0.3.2 【目次】   項目      - はじめに - 前提条件 - 使い方 - 最後に - 参考 はじめに Vue.js + Laravel8.4でAxios(Ajax)のエラーハンドリングしてみました。 Vue.js + Laravelの勉強をしており、 下記の記事を見て動かしてみましたが CRUDの最小構成のシステムで もっと機能を追加したという事で、今回の記事にしたという経緯です。 (2021年6月11日現在) Vue.js + LaravelでシンプルなSPA構築チュートリアル:概要編 Vue.jsとLaravelによるSPA記事で下記の記事で構成しています。 (未リンクの記事はこれから作成予定) ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ Vue.js + Laravel8.4でVeeValidateを使ってバリデーションしてみる Vue.js + Laravel8.4でAxiosのエラーハンドリングしてみる ↑↑↑↑↑↑今回はここです↑↑↑↑↑↑ [Vue.js + Laravel8.4でLaravel側もバリデーションしてみる] [Vue.js + Laravel8.4でLaravel側のAPIの認証処理してみる] ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ Github でもソースを公開していますので参考にして下さい。 前提条件 参考にさせて頂いた記事との開発環境の相違箇所。 ・Laravel のバージョンが異なる Laravel 6.* App\Task; ↓ Laravel 8.4 App\Models\Task; 上記のようにLaravel 8.*ではコントローラーのパスが 変更されていますので適宜変更して下さい。 ・入力項目のCSSを若干変更しています。 ・DBはMySQLを使用 使い方 <script> 部分の修正。 resources/js/components/TaskCreateComponent.vue <script> ・・・ getTasks() { axios.get('/api/tasks') .then((res) => { this.tasks = res.data; }) // ほとんどのエラーをキャッチ Vue.config.errorHandler = function (err, vm, info) { // 2.2.0 以降で使用可 alert(err); } // 残りのエラーをキャッチ window.addEventListener("error", event => { alert(event.error); }); window.addEventListener("unhandledrejection", event => { alert(event.reason); }); ・・・ </script> Vue.config.errorHandler Vuejs内のエラーを一元的に処理したい場合に有効 例えば、全ての画面でのどんなエラーでも収集したい場合 addEventListener JavaScriptからさまざまなイベント処理を実行することができるメソッド。 例として、こんな感じです。 Webページが読み込まれたかどうか? マウスによるクリックがされたかどうか? フォームに何らかの操作が行われたかどうか? キーボードから入力が行われたかどうか? 別の処理方法もある そして上記のやり方とは別に 下記は古い?処理方法のやり方っぽいです。 resources/js/components/TaskCreateComponent.vue .catch(function (error){ if(error.reponse){ console.log(error.reponse.data); console.log(error.reponse.status); console.log(error.reponse.header); } else if (error.request) { // 要求がなされたが、応答が受信されなかった console.log(error.request); } else { // トリガーしたリクエストの設定に何かしらのエラーがある console.log('Error', error.message); } console.log(error.config); }); 更に余裕があれば vue-axios-interceptors を使ってみる。 (対象 : Laravel 5.5以上) vue-axios-interceptors ますはターミナルでインストール。 npm install vue-axios-interceptors --save そして下記の2ファイルを修正。 resources\js\app.js /* vue-axios-interceptors */ require('vue-axios-interceptors'); window.axios = require('axios'); resources\js\components\TaskListComponent.vue /* axios.interceptors */ mounted() { window.intercepted.$on('response', data => { console.log(data); // { status: 404, code: 'Not found', body: null } }); this.getTasks(); } vue-axios-interceptorsを使うことによって 例えばgoogle chrome developer toolsでF12で起動し Consoleのタブでレスポンスの内容を赤枠部分に表示出来ます。 commit:Axios エラーハンドリング 最後に Vue.jsは勉強して日が浅く、 間違い等があるかもしれません。 修正点等ありましたらコメント下さい。 参考 【Ajax】axiosを使って簡単にHTTP通信
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue.js + Laravel8.4でVeeValidateを使ってバリデーションしてみる

【開発環境】 Windows 10 HOME PHP 7.4.7 MySQL Laravel Framework 8.4 vue : 2.6.12 vue-router : 3.5.1 vee-validate : 3.4.9 【目次】   項目      - はじめに - 前提条件 - インストール - 使い方 - 解説 - 最後に - 参考 はじめに Vue.js + Laravel 8.4で VeeValidate を使ってバリデーションしてみました。 Vue.js側のバリデーションを調べてみたところ いくつかありかしたが今回はVeeValidateを使ってみました。 Vue.js + Laravelの勉強をしており、 下記の記事を見て動かしてみましたが CRUDの最小構成のシステムで もっと機能を追加したという事で、今回の記事にしたという経緯です。 (2021年6月11日現在) Vue.js + LaravelでシンプルなSPA構築チュートリアル:概要編 Vue.jsとLaravelによるSPA記事で下記の記事で構成しています。 (未リンクの記事はこれから作成予定) ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ Vue.js + Laravel8.4でVeeValidateを使ってバリデーションしてみる ↑↑↑↑↑↑今回はここです↑↑↑↑↑↑ Vue.js + Laravel8.4でAjaxのエラーハンドリングしてみる [Vue.js + Laravel8.4でLaravel側もバリデーションしてみる] [Vue.js + Laravel8.4でLaravel側のAPIの認証処理してみる] ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ Github でもソースを公開していますので参考にして下さい。 前提条件 参考にさせて頂いた記事との開発環境の相違箇所。 ・Laravel のバージョンが異なる Laravel 6.* App\Task; ↓ Laravel 8.4 App\Models\Task; 上記のようにLaravel 8.*ではコントローラーのパスが 変更されていますので適宜変更して下さい。 ・入力項目のCSSを若干変更しています。 ・DBはMySQLを使用 インストール ターミナルを開き 対象ディレクトリで下記のコマンド実行します。 npm install vee-validate 使い方 <script> 部分の修正。 resources/js/components/TaskCreateComponent.vue <script> import Vue from 'vue' import { ValidationObserver, ValidationProvider, localize, extend, } from 'vee-validate' // ルール設定 import * as rules from 'vee-validate/dist/rules' for (let rule in rules) { extend(rule, rules[rule]) } // 日本語化 import ja from 'vee-validate/dist/locale/ja' localize('ja', ja) // コンポーネント設定 Vue.component('ValidationProvider', ValidationProvider) Vue.component('ValidationObserver', ValidationObserver) export default { data: function () { return { task: {} } }, methods: { submit() { this.$refs.observer.validate().then((result) => { if(result) { // OK axios.post('/api/tasks', this.task) .then((res) => { this.$router.push({name: 'task.list'}); }); } }) } } } </script> <template>側の修正 今回は下記にバリデーションを設定します。 ・Title ・Content resources/js/components/TaskCreateComponent.vue <template> <div class="container"> <div class="row justify-content-center"> <div class="col-sm-6"> <ValidationObserver ref="observer" v-slot="{ invalid }"> <form v-on:submit.prevent="submit"> <div class="form-group row"> <label for="title" class="col-sm-3 col-form-label">Title</label> <ValidationProvider name="title" rules="required|min:5|max:30" v-slot="{ errors }" > <input type="text" name="title" class="col-sm-9 form-control" v-model="task.title"> <div class="text-danger">{{ errors[0] }}</div> </ValidationProvider> </div> <div class="form-group row"> <label for="content" class="col-sm-3 col-form-label">Content</label> <ValidationProvider name="content" rules="required|min:10|max:50" v-slot="{ errors }" > <input type="text" class="col-sm-9 form-control" name="content" v-model="task.content"> <div class="text-danger">{{ errors[0] }}</div> </ValidationProvider> </div> <div class="form-group row"> <label for="person-in-charge" class="col-sm-3 col-form-label">Person In Charge</label> <input type="text" id="person-in-charge" class="col-sm-9 form-control" v-model="task.person_in_charge"> </div> <div> <button type="submit" :disabled="invalid" class="btn btn-primary">Submit</button> </div> </form> </ValidationObserver> </div> </div> </div> </template> 新規登録画面で文字を入力してみると 赤文字でバリデーションが表示されています。 解説 ---- モードについて mode="モード"で設定することができます。 aggressive:入力値を変更するたび passive:フォームが送信時 lazy:フォーカスが外れた時 or 選択が変更時(blur or change) eager:aggressiveとlazyのミックス vee-validate公式 VeeValidate comes with 4 interaction modes out of the box: [aggressive]: this is the default behavior and it validates whenever an input event is emitted. [passive]: doesn't validate on any event, so you can only validate manually. [lazy]: validates on change or blur depending on the input type. [eager]: when the field is valid or has not yet been interacted with, it will validate on change. When the field becomes invalid due to the first validation, it will validate on input for as long the field is invalid. When the field is valid again, it will go back to validating on change. It is a mix between the aggressive and lazy modes. 何となくeagerが良いのかと。 resources/js/components/TaskCreateComponent.vue <ValidationProvider name="title" rules="required|min:5|max:30" // 5-30文字で入力必須 v-slot="{ errors }" //エラー判定のスイッチみたいなもの > 【ValidationProvider】 フォーム部品のエラーを監視。 【ValidationObserver】 フォーム全体を監視。 フォーム全体のバリデーションの通過の判定。 resources/js/components/TaskCreateComponent.vue submit() { this.$refs.observer.validate().then((result) => { if(result) { // OK axios.post('/api/tasks', this.task) .then((res) => { this.$router.push({name: 'task.list'}); }); } }) } この2つをタグに設定すると バリデーションを通過するまで送信ボタンを無効にする。 v-slot="{ invalid } :disabled="invalid" resources/js/components/TaskCreateComponent.vue <ValidationObserver ref="observer" v-slot="{ invalid }"> ・・・ <button type="submit" :disabled="invalid" class="btn btn-primary">Submit</button> </ValidationObserver> commit:VeeValidate 新規登録画面 最後に ご覧頂きありがとうございました。 Vue.jsは勉強して日が浅く、 間違い等があるかもしれません。 修正点等ありましたらコメント下さい。 参考 Vue.jsでVeeValidateを使ってバリデーション
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel7.系でマルチログイン機能を実装する。

【Laravel7.系でマルチログイン機能を実装する。】 プロジェクトにて、複数でのログイン機能が必要になり、色々悪戦苦闘しながらやっとこさで作成できました。もし皆様も必要になった場合はよければご参考にしてみてください。 ■追記しました。2021/06/11 新規登録のコントローラーファイル・パスワードリセットのコントローラーなど一部記述が足りていなかったので、追記しました。 ■マルチ認証システム(複数のユーザーレベルでのログイン機能) 〇参考サイト様 大変参考になりました。ありがとうございます。 Laravel 7.xでマルチ認証機能を作成してみた(ほぼ以下のサイトを基本に実装した) 〇手順 △0.本手順の前提 Lataravelでコマンドを使い、ログイン機能を作成済み。 本手順にて、複数のログイン機能を実装するが、例として、「Admin」という名称にて実装していく。(Masterとか別の名前で作成したい場合は適宜、ファイル名やテーブル、namespaceなど変更すること) △1.モデルファイルとマイグレーションファイルを作成 以下コマンドで、Modelとmigrationを作成する。 $ php artisan make:model Models/Admin -m ◇モデルファイル側 以下をコピー&ペーストする。 app/Models/Admin.php <?php namespace App\Models; use Illuminate\Contracts\Auth\MustVerifyEmail; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; use App\Notifications\Admin\PasswordResetNotification; class Admin extends Authenticatable { use 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', ]; /** * パスワードリセット通知の送信 * * @param string $token * @return void */ public function sendPasswordResetNotification($token) { $this->notify(new PasswordResetNotification($token)); } } ◇マイグレーションファイル側 以下をコピー&ペーストする。 database/migrations/「生成されたマイグレーションファイル」.php <?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateAdminsTable extends Migration { public function up() { Schema::create('admins', function (Blueprint $table) { $table->id(); $table->string('name'); $table->string('email',191)->unique(); $table->timestamp('email_verified_at')->nullable(); $table->string('password'); $table->rememberToken(); $table->timestamps(); }); } public function down() { Schema::dropIfExists('admins'); } } △2.マイグレーションファイルを実行する 作成したマイグレーションファイルを実行して、テーブルを作成する。 $ php artisan migrate △3.auth.phpにAdminの定義を追加 auth.phpにAdminの情報を追加する。 config/auth.php <?php return [ /* |-------------------------------------------------------------------------- | Authentication Defaults |-------------------------------------------------------------------------- | | This option controls the default authentication "guard" and password | reset options for your application. You may change these defaults | as required, but they're a perfect start for most applications. | */ 'defaults' => [ 'guard' => 'web', 'passwords' => 'users', ], /* |-------------------------------------------------------------------------- | Authentication Guards |-------------------------------------------------------------------------- | | Next, you may define every authentication guard for your application. | Of course, a great default configuration has been defined for you | here which uses session storage and the Eloquent user provider. | | All authentication drivers have a user provider. This defines how the | users are actually retrieved out of your database or other storage | mechanisms used by this application to persist your user's data. | | Supported: "session", "token" | */ 'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'admin' => [ // adminの定義を追加 'driver' => 'session', 'provider' => 'admins', ], 'api' => [ 'driver' => 'token', 'provider' => 'users', 'hash' => false, ], ], /* |-------------------------------------------------------------------------- | User Providers |-------------------------------------------------------------------------- | | All authentication drivers have a user provider. This defines how the | users are actually retrieved out of your database or other storage | mechanisms used by this application to persist your user's data. | | If you have multiple user tables or models you may configure multiple | sources which represent each model / table. These sources may then | be assigned to any extra authentication guards you have defined. | | Supported: "database", "eloquent" | */ 'providers' => [ 'users' => [ 'driver' => 'eloquent', 'model' => App\User::class, ], 'admins' => [ // adminの定義を追加 'driver' => 'eloquent', 'model' => App\Models\Admin::class, ], ], /* |-------------------------------------------------------------------------- | Resetting Passwords |-------------------------------------------------------------------------- | | You may specify multiple password reset configurations if you have more | than one user table or model in the application and you want to have | separate password reset settings based on the specific user types. | | The expire time is the number of minutes that the reset token should be | considered valid. This security feature keeps tokens short-lived so | they have less time to be guessed. You may change this as needed. | */ 'passwords' => [ 'users' => [ 'provider' => 'users', 'table' => 'password_resets', 'expire' => 60, 'throttle' => 60, ], 'admins' => [ // 追加 'provider' => 'admins', 'table' => 'admin_password_resets', // さきほど追加作成したテーブルを指定 'expire' => 60, 'throttle' => 60, ] ], /* |-------------------------------------------------------------------------- | Password Confirmation Timeout |-------------------------------------------------------------------------- | | Here you may define the amount of seconds before a password confirmation | times out and the user is prompted to re-enter their password via the | confirmation screen. By default, the timeout lasts for three hours. | */ 'password_timeout' => 10800, ]; △4.RouteServiceProviderにAdmin用のパスを追加 RouteServiceProvider.phpにAdminのパスとして、ADMIN_HOMEを追加を追加する。 app/Providers/RouteServiceProvider.php すでにコマンドにログイン機能が実装されているならば、以下の、 「public const HOME = '/home';」を目印に、以下のように一行追加をする。 public const HOME = '/home'; public const ADMIN_HOME = '/admin/home'; // 管理者用のパスを追加 △5.Authenticateに未ログイン時の挙動を書く Authenticate.phpに以下の内容をコピー&ペーストする。 app/Http/Middleware/Authenticate.php <?php namespace App\Http\Middleware; use Illuminate\Auth\Middleware\Authenticate as Middleware; use Illuminate\Support\Facades\Route; class Authenticate extends Middleware { /** * Get the path the user should be redirected to when they are not authenticated. * * @param \Illuminate\Http\Request $request * @return string|null */ protected function redirectTo($request) { if (! $request->expectsJson()) { if (Route::is('admin.*')) { // admin からのRouteの分岐を追加 return route('admin.login'); } else { return route('login'); } } } } △6.RedirectIfAuthenticatedにログイン時の挙動を書く RedirectIfAuthenticated.phpに以下の内容をコピー&ペーストする。 app/Http/Middleware/RedirectIfAuthenticated.php use App\Providers\RouteServiceProvider; use Closure; use Illuminate\Support\Facades\Auth; class RedirectIfAuthenticated { /** * Handle an incoming request. * * @param \Illuminate\Http\Request $request * @param \Closure $next * @param string|null $guard * @return mixed */ public function handle($request, Closure $next, $guard = null) { // if (Auth::guard($guard)->check()) { // return redirect(RouteServiceProvider::HOME); // } if (Auth::guard($guard)->check() && $guard === 'admin') { // guardがadminかそれ以外かで分岐させる return redirect(RouteServiceProvider::ADMIN_HOME); } elseif (Auth::guard($guard)->check()) { return redirect(RouteServiceProvider::HOME); } return $next($request); } } △7.Controllerを追加 まずは、コマンドにて、コントローラーファイルを作成する。 そして、HomeController.phpに以下の内容をコピー&ペーストする。 ◇HomeController app/Http/Controllers/Admin/HomeController.php <?php namespace App\Http\Controllers\Admin; use App\Http\Controllers\Controller; use Illuminate\Http\Request; class HomeController extends Controller { public function index() { return view('admin.home'); } } ◇LoginController app/Http/Controllers/Admin/LoginController.php <?php namespace App\Http\Controllers\Admin\Auth; use App\Http\Controllers\Controller; use App\Providers\RouteServiceProvider; use App\Models\Admin; use Illuminate\Foundation\Auth\AuthenticatesUsers; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; class LoginController extends Controller { use AuthenticatesUsers; protected $redirectTo = RouteServiceProvider::ADMIN_HOME; protected function guard() { return Auth::guard('admin'); } public function showLoginForm() { //dd(Auth::check()); if (!Auth::guard('admin')->check()) { return view('admin.auth.login'); //return redirect( route('admin.login') ); } else { //return view('admin.home'); return redirect( route('admin.home') ); } } public function logout(Request $request) { Auth::guard('admin')->logout(); return redirect(route('admin.login')); } } ◇RegisterController アカウント新規登録の際に使用されるクラスファイル。 Validatorクラスの 'email' => ['required', 'string', 'email', 'max:255', 'unique:admins'], の部分の 'unique:admins' をテーブル名に変更することを忘れてはいけない。 忘れてデフォルトのまま(users)にしてしまうと、常にusersテーブルの内容と重複しないかをチェックしてしまうことになるため注意。 app/Http/Controllers/Admin/RegisterController.php <?php namespace App\Http\Controllers\Admin\Auth; use App\Http\Controllers\Controller; use App\Providers\RouteServiceProvider; use App\Models\Admin; use Illuminate\Foundation\Auth\RegistersUsers; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Validator; use Illuminate\Support\Facades\Auth; class RegisterController extends Controller { use RegistersUsers; // 管理者用のホーム画面へのリダイレクト protected $redirectTo = RouteServiceProvider::ADMIN_HOME; // 管理者用のguardを指定 protected function guard() { return Auth::guard('admin'); } // 管理者ユーザ登録用のviewを指定 public function showRegistrationForm() { return view('admin.auth.register'); } // 登録時のバリデーション protected function validator(array $data) { return Validator::make($data, [ 'name' => ['required', 'string', 'max:255'], 'email' => ['required', 'string', 'email', 'max:255', 'unique:admins'], 'password' => ['required', 'string', 'min:8', 'confirmed'], ]); } // 登録 protected function create(array $data) { // 管理者ユーザ用のAdmin Modelを指定 return Admin::create([ 'name' => $data['name'], 'email' => $data['email'], 'password' => Hash::make($data['password']), ]); } } ◇ResetPasswordController パスワードリセットの際に使用されるクラスファイル。 app/Http/Controllers/Admin/ResetPasswordController.php <?php namespace App\Http\Controllers\Admin\Auth; use App\Http\Controllers\Controller; use App\Providers\RouteServiceProvider; use App\Models\Admin; use Illuminate\Http\Request; use Illuminate\Foundation\Auth\ResetsPasswords; use Illuminate\Support\Facades\Password; use Illuminate\Support\Facades\Auth; class ResetPasswordController extends Controller { use ResetsPasswords; protected $redirectTo = RouteServiceProvider::ADMIN_HOME; // 管理者用のguardを指定 protected function guard() { return Auth::guard('admin'); } public function showResetForm(Request $request, $token = null) { // 管理者ユーザ用のviewを指定 return view('admin.auth.passwords.reset')->with( ['token' => $token, 'email' => $request->email] ); } public function broker() { // 管理者ユーザ用のパスワードブローカーを指定 return Password::broker('admins'); } } △8.ルーティング設定 Admin用のルーティング設定をする。 routes/web.php Auth::routes(); //これはコマンドにて作成したログイン機能用なのでそのままにしておく。 // 管理者用のルーティング Route::namespace('Admin')->prefix('admin')->name('admin.')->group(function () { Auth::routes([ 'register' => true, 'reset' => true, 'verify' => false ]); // 管理者のhome(認証後は/admin/homeへ) Route::middleware('auth:admin')->group(function () { Route::get('home', 'HomeController@index')->name('home'); }); }); △9.Admin用のviewを追加する すでに作成済み(コマンドにてログイン機能を作成している場合は)の、home.blade.phpとauth/login.blade.phpをコピーしてadminにそれぞれ同じような構成で作る。 layoutsもAdmin用で作成。 ※基本的に丸ごとコピーで大丈夫だと思いますが、 CSSやJSファイルなど必要な場合は別途記述を追加してください。 また、HTMLタグの構造やifの判定なども皆様の実装したい機能に応じて変更してもらってOKです。 ◇resources/views/layouts/admin/app.blade.php resources/views/layouts/admin/app.blade.php <!doctype html> <html lang="{{ str_replace('_', '-', app()->getLocale()) }}"> <head> <meta charset="utf-8"> <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> <!-- Fonts --> <link rel="dns-prefetch" href="//fonts.gstatic.com"> <link href="https://fonts.googleapis.com/css?family=Nunito" rel="stylesheet"> </head> <body> <div id="app"> <header class="gheader"> <div class="logo"> <h1>LOGO</h1> </div> </header> @if (in_array(\Request::route()->getName(), ['admin.home'])) <nav class="navbar navbar-expand-md navbar-light bg-white shadow-sm"> <div class="container"> <a class="navbar-brand" href="{{ url('/') }}"> {{ config('app.name', 'Laravel') }} </a> <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="{{ __('Toggle navigation') }}"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarSupportedContent"> <!-- Left Side Of Navbar --> <ul class="navbar-nav mr-auto"> </ul> <!-- Right Side Of Navbar --> <ul class="navbar-nav ml-auto"> <!-- Authentication Links --> @unless (Auth::guard('admin')->check()) <li class="nav-item"> <a class="nav-link" href="{{ route('admin.login') }}">{{ __('Login') }}</a> </li> @if (Route::has('register')) <li class="nav-item"> <a class="nav-link" href="{{ route('admin.register') }}">{{ __('Register') }}</a> </li> @endif @else <li class="nav-item dropdown"> <a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre> <span class="caret"></span> </a> <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown"> <a class="dropdown-item" href="{{ route('admin.logout') }}" onclick="event.preventDefault(); document.getElementById('logout-form').submit();"> {{ __('Logout') }} </a> <form id="logout-form" action="{{ route('admin.logout') }}" method="POST" style="display: none;"> @csrf </form> </div> </li> @endunless </ul> </div> </div> </nav> @endif <main> @yield('content') </main> </div> </body> </html> ◇resources/views/admin/auth/login.blade.php resources/views/admin/auth/login.blade.php ※{{ __('E-Mail Address') }}などの部分も適宜文字列を変更してもらってOKです。 こういう設定は、resources/lang/ja.jsonというファイルで変更できるはずです。 @extends('layouts.admin.app') @section('content') <section class="c-section"> <div class="login-screen"> <form method="POST" action="{{ route('admin.login') }}"> @csrf <div class="form-group row"> <label for="email" class="form-label">{{ __('E-Mail Address') }}</label> <div class="col-md-6"> <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" placeholder="abc@〇〇〇〇.jp" autofocus> @error('email') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="form-group row"> <label for="password" class="form-label">{{ __('Password') }}</label> <div class="col-md-6"> <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password" placeholder="●●●●●●"> @error('password') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="form-group row" style="display: none;"> <div class="col-md-6 offset-md-4"> <div class="form-check"> <input class="form-check-input" type="checkbox" name="remember" id="remember" {{ old('remember') ? 'checked' : '' }}> <label class="form-check-label" for="remember"> {{ __('Remember Me') }} </label> </div> </div> </div> <div class="form-group submit-area"> <div class="btn-link"> <button type="submit" class="c-btn"> <span class="c-btn__body"> {{ __('AdminLogin') }}</span> </button> </div> @if (Route::has('admin.password.request')) <div class="password-request"> <a href="{{ route('admin.password.request') }}"> {{ __('Forgot Your Password?') }} </a> </div> @endif <div class="direction-link"> <a href="{{ route('admin.register') }}">{{ __('CreateAccount') }}</a> </div> </div> </form> </div> </section> @endsection ◇resources/views/admin/auth/register.blade.php @extends('layouts.admin.app') @section('content') <section class="c-section"> <div class="login-screen"> <form class="form" method="POST" action="{{ route('admin.register') }}"> @csrf @if ($errors->any()) <span class="c-invalid-feedback" role="alert"> @foreach ($errors->all() as $error) <strong>{{ $error }}</strong> @endforeach </span> @endif <div class="form-group"> <label for="name" class="form-label">{{ __('AccountName') }}</label> <div> <input id="name" type="text" placeholder="〇〇〇〇" class="form-control @error('name') is-invalid @enderror" name="name" value="{{ old('name') }}" required autocomplete="name" autofocus> @error('name') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="form-group"> <label for="email" class="form-label">{{ __('E-Mail Address') }}</label> <div> <input id="email" type="email" placeholder="abc@〇〇〇〇.jp" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email"> @error('email') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="form-group"> <label for="password" class="form-label">{{ __('Password') }}</label> <div> <input id="password" type="password" placeholder="●●●●●●" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="new-password"> @error('password') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="form-group"> <label for="password-confirm" class="form-label">{{ __('Confirm Password') }}</label> <div> <input id="password-confirm" type="password" placeholder="●●●●●●" class="form-control" name="password_confirmation" required autocomplete="new-password"> </div> </div> <div class="form-group submit-area"> <div class="btn-link"> <button type="submit" class="c-btn"> <span class="c-btn__body"> {{ __('Create') }}</span> </button> </div> </div> <div class="direction-link"> <a href="{{ route('login') }}">{{ __('AlreadyHaveAccount') }}</a> </div> </form> </div> </section> @endsection ◇resources/views/admin/auth/emails/password_reset.blade.php これはパスワードをリセット用のメールが送られてきた場合のメールの中身のコーディング内容です。 管理者がパスワードをリセットしなくても良いという場合は、作成しても変更はしなくてもいいかもしれません。 <!DOCTYPE html> <html lang="ja"> <style> body { background-color: #fffacd; } h1 { font-size: 16px; color: #ff6666; } #button { width: 200px; text-align: center; } #button a { padding: 10px 20px; display: block; border: 1px solid #2a88bd; background-color: #FFFFFF; color: #2a88bd; text-decoration: none; box-shadow: 2px 2px 3px #f5deb3; } #button a:hover { background-color: #2a88bd; color: #FFFFFF; } </style> <body> <h1> 管理者パスワードリセット </h1> <p> 以下のボタンを押下し、パスワードリセットの手続きを行ってください。 </p> <p id="button"> <a href="{{$reset_url}}">パスワードリセット</a> </p> </body> </html> ◇resources/views/admin/auth/passwords/email.blade.php これはパスワードをリセット用のメール送る画面のコーディング内容です。 管理者がパスワードをリセットしなくても良いという場合は、作成しても変更は作成しなくてもいいかもしれません。 @extends('layouts.admin.app') @section('content') <div class="c-section"> <div class="login-screen"> <h2 class="login-header">{{ __('Reset Password') }}</h2> @if (session('status')) <div class="alert alert-success" role="alert"> {{ session('status') }} </div> @endif <form method="POST" action="{{ route('admin.password.email') }}"> @csrf @if ($errors->any()) <span class="c-invalid-feedback" role="alert"> @foreach ($errors->all() as $error) <strong>{{ $error }}</strong> @endforeach </span> @endif <div class="form-group"> <label for="email" class="form-label">{{ __('E-Mail Address') }}</label> <div> <input id="email" type="email" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ old('email') }}" required autocomplete="email" placeholder="abc@〇〇〇〇.jp" autofocus> </div> </div> <div class="form-group submit-area"> <div class="btn-link --wider"> <button type="submit" class="c-btn"> <span class="c-btn__body"> {{ __('Send Password Reset Link') }}</span> </button> </div> </div> </form> </div> </div> @endsection ◇resources/views/admin/auth/passwords/reset.blade.php これはパスワードを実際にリセットする画面のコーディング内容です。 管理者がパスワードをリセットしなくても良いという場合は、作成しても変更は作成しなくてもいいかもしれません。 @extends('layouts.admin.app') @section('content') <div class="c-section"> <div class="login-screen"> <h2 class="login-header">{{ __('Reset Password') }}</h2> <form method="POST" action="{{ route('admin.password.update') }}"> @csrf @if ($errors->any()) <span class="c-invalid-feedback" role="alert"> @foreach ($errors->all() as $error) <strong>{{ $error }}</strong> @endforeach </span> @endif <input type="hidden" name="token" value="{{ $token }}"> <div class="form-group"> <label for="email" class="form-label">{{ __('メールアドレス') }}</label> <div> <input id="email" type="email" placeholder="abc@〇〇〇〇.jp" class="form-control @error('email') is-invalid @enderror" name="email" value="{{ $email ?? old('email') }}" required autocomplete="email" autofocus> </div> </div> <div class="form-group"> <label for="password" class="form-label">{{ __('Password') }}</label> <div> <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required placeholder="●●●●●●" autocomplete="new-password"> </div> </div> <div class="form-group"> <label for="password-confirm" class="form-label">{{ __('Confirm Password') }}</label> <div> <input id="password-confirm" type="password" class="form-control" name="password_confirmation" required placeholder="●●●●●●" autocomplete="new-password"> </div> </div> <div class="form-group submit-area"> <div class="btn-link"> <button type="submit" class="c-btn"> <span class="c-btn__body"> {{ __('Reset Password') }}</span> </button> </div> </div> </form> </div> </div> @endsection ◇resources/views/admin/auth/passwords/comfirm.blade.php こちらも必要ない場合は作成しても変更はしなくてもいいかもしれません。 @extends('layouts.admin.app') @section('content') <div class="c-section"> <div class="login-screen"> <div class="card-header">{{ __('Confirm Password') }}</div> <div class="card-body"> {{ __('Please confirm your password before continuing.') }} <form method="POST" action="{{ route('admin.password.confirm') }}"> @csrf <div class="form-group row"> <label for="password" class="col-md-4 col-form-label text-md-right">{{ __('Password') }}</label> <div class="col-md-6"> <input id="password" type="password" class="form-control @error('password') is-invalid @enderror" name="password" required autocomplete="current-password"> @error('password') <span class="invalid-feedback" role="alert"> <strong>{{ $message }}</strong> </span> @enderror </div> </div> <div class="form-group row mb-0"> <div class="col-md-8 offset-md-4"> <button type="submit" class="btn btn-primary"> {{ __('Confirm Password') }} </button> @if (Route::has('password.request')) <a class="btn btn-link" href="{{ route('admin.password.request') }}"> {{ __('Forgot Your Password?') }} </a> @endif </div> </div> </form> </div> </div> </div> @endsection △10.Admin用のNotifications追加する これは、パスワードをリセットする際のメール送信のクラスで、どのviewファイルをメールのテンプレートかを選択することができる。 このファイルを作成しておかないと、デフォルトの本文とテンプレートファイルが選択されてしまうことになるので注意。 Notifications/Admin/PasswordResetNotification.php <?php namespace App\Notifications\Admin; use Illuminate\Bus\Queueable; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Auth\Notifications\ResetPassword; class PasswordResetNotification extends ResetPassword { use Queueable; /** * Get the mail representation of the notification. * * @param mixed $notifiable * @return \Illuminate\Notifications\Messages\MailMessage */ public function toMail($notifiable) { if (static::$toMailCallback) { return call_user_func(static::$toMailCallback, $notifiable, $this->token); } return (new MailMessage) ->subject('パスワードリセット通知') ->view('admin.emails.password_reset', [ 'reset_url' => url(config('app.url').route('admin.password.reset', ['token' => $this->token, 'email' => $notifiable->getEmailForPasswordReset()], false)) ]); } } △11.Admin用のパスワードリセット用のテーブルを作成する。 以下を実行するとマイグレーションファイルが生成される。 $ php artisan make:migration add_admin_password_resets database/日付_add_admin_password_resets_table.php <?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class AddAdminPasswordResetsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('admin_password_resets', function (Blueprint $table) { $table->string('email')->index(); $table->string('token'); $table->timestamp('created_at')->nullable(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('admin_password_resets'); } } その後、以下を実行してテーブルを実際に作成する。 $ php artisan migrate
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

PHPのパフォーマンスチューニング(メモ化)

はじめに Laravelはよくパフォーマンスが悪いと言われますが、コードの書き方やLaravelの使い方次第で大幅にパフォーマンスは改善することが可能です。 本日はパフォーマンスチューニングの手法の一つであるメモ化について記事にしたいと思います。 動作確認環境 PHP 8.0 Laravel 8.0 ※下位環境でも動作する場合がございます パフォーマンスについて 一番遅い:データベースから値を取得 遅い:外部キャッシュ(Redis、Memcacheなど)から値を取得 速い:データキャッシュとしてのapcuから値を取得 一番速い:メモ化して値を取得 保存箇所へのアクセスが遠いほど、パフォーマンス劣化が発生します。 メモ化とは メモ化(英: Memoization)とは、プログラムの高速化のための最適化技法の一種であり、サブルーチン呼び出しの結果を後で再利用するために保持し、そのサブルーチン(関数)の呼び出し毎の再計算を防ぐ手法 Wikipediaには上記のようにあります。 噛み砕くと、処理の返り値を変数に保存しておき、それを再利用することで同じ計算を省略して処理速度を向上させる手法です。 Laravelでメモ化が使われている箇所 Laravelでは、Illuminate\Support\Strなどで実際に使われています。 以下は、引数で渡された値を先頭大文字の英字(通称アッパーキャメルケース)に変換してくれるメソッドです。 一度目の呼び出しと同じ引数が渡された場合は、すでにメモ化しているので、変換処理をはさまずに値が返ります。 /** * The cache of studly-cased words. * * @var array */ protected static $studlyCache = []; /** * Convert a value to studly caps case. * * @param string $value * @return string */ public static function studly($value) { $key = $value; if (isset(static::$studlyCache[$key])) { return static::$studlyCache[$key]; } $value = ucwords(str_replace(['-', '_'], ' ', $value)); return static::$studlyCache[$key] = str_replace(' ', '', $value); } クロージャーを使ったメモ化 Laravelの\Illuminate\Cache\Repository::rememberをメモ化用に改変 /** * @var array */ protected $localCache = []; /** * Memoization sample * * @param string $cacheKey * @return mixed */ public function localCache(string $cacheKey) { return $this->rememberLocal($cacheKey, function () { // メモ化したい処理 // 引数のcacheKeyが同じであれば一度目しか重い処理は通りません }); } /** * Get an item from the cache, or execute the given Closure and store the result. * * @param string $key * @param \Closure $callback * @return mixed */ private function rememberLocal(string $key, Closure $callback) { $value = $this->get($key); if ($value !== null) { return $value; } $this->put($key, $value = $callback()); return $value; } /** * Retrieve an item from the local cache by key. * * @param string $key * @return mixed */ private function get(string $key) { return $this->localCache[$key] ?? null; } /** * Store an item in the local cache. * * @param string $key * @param mixed $value */ private function put(string $key, $value): void { $this->localCache[$key] = $value; } 編集後記 パフォーマンスチューニングについて勉強した時に一番最初に学んだメモ化について記事にしました。 どこまでパフォーマンスチューニングするかはケースバイケースだとは思いますが、私の場合DBへのアクセスするのは最終手段という中で育ってきたので、メモ化は積極的に使うようにしています。 1回の通信で同じ重い処理を2度以上する可能性がある場合にはまずはメモ化の検討をオススメします。 Laravelのパフォーマンスチューニングに関する記事
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む