- 投稿日:2020-01-22T22:47:27+09:00
⛰【Vue.js】MariaDBのJSON型カラムから取得したデータの指定の値を変数名で取り出す為にパースする
環境
Vue.js 6.12.0
PHP 7.3.10
Laravel 6.5.0
MariaDBやりたいこと
MariaDBのに下記のようなデータが格納されていてそれをLaravelのAPIで取得する。
その内のcontentカラム(JSON型)のデータからdata_kindの値を判断基準として条件分岐を行い、
valueの内容である「テストの内容です?」や「赤文字で強調する!!」や円周率をVueので表示する為にJSONをパースする
id content del_flg 1 {"data_kind": "1", "value": "テストの内容です?"} 0 2 {"data_kind": "2", "value": "赤文字で強調する!!"} 0 3 {"data_kind": "3", "value": "円周率を表示"} 0 4 {"data_kind": "4", "value": "削除済み???"} 1 やったこと
下に全体で色々書いてますが一番重要なのは下記のパース部分です
これが無いとJSONではなくJSONの形をしたただの文字列としてデータが扱われるので値を取り出すことができません// 取得データをforEachで全てJSON.parseにかける res.data.recordList.forEach(value => { value["content"] = JSON.parse(value["content"]); });全体
Hoge.vue// テンプレ部分 <template> <section class="container"> <div v-for="data in tableData" :key="content.id"> <!-- 通常表示 --> <div v-if="data.content.data_kind === '1'">{{ data.content.value }}</div> <!-- クラスを付けて装飾を変更 --> <div v-else-if="data.content.data_kind === '2'" class="redBold">{{ data.content.value }}</div> <!-- 円周率を表示 --> <div v-else-if="data.content.data_kind === '3'">3.141592653589323846....</div> </div> </section> </template> // データ取得とパース部分 <script> export default { data() { return { tableData: [] }; }, methods: { getContent() { axios .get("/api/getContent", { params: { delFlg: 0 } }) .then(res => { // 取得データをforEachで全てJSON.parseにかける res.data.recordList.forEach(value => { value["content"] = JSON.parse(value["content"]); }); this.tableData = res.data.recordList; }); } }, mounted() { this.getContent(); } }; </script> // 装飾 <style lang="sass" scoped> .redBold color: red font-weight: bold </style>
- 投稿日:2020-01-22T19:20:03+09:00
AWSで作ったLaravelアプリケーションをXserverにデプロイする
はじめに
タイトル通りですが、LaravelアプリケーションのデプロイをXserverで試してみました。
AWSでアプリケーションは作成したのですが、
XAMPPで作成した方も以下のやり方でできると思います。まとめ
さっそくですがやり方を以下にまとめました。
※XAMPPでアプリケーションを作成した方は⑥以降を参考にしていただければと思います。
①AWSでLaravelアプリケーションを作成。
②作成したアプリケーションのフォルダをダウンロードしてXAMPP環境下(htdocs内)に保存する。
③XAMPPでphpMyAdminを起動。アプリケーション用のDB(DB名:laravel)を作成する。
④.envをXAMPP環境下でDBが使えるようにに編集。
.envDB_DATABASE=laravel DB_USERNAME=root DB_PASSWORD=⑤コマンドプロンプトで同フォルダに移動してテーブル作成
$ cd 作成したアプリのフォルダ $ php artisan migrate//このコマンドでテーブルが作成されます。⑥FTP(FileZillaを使用しました)で転送したいドメイン内のpublic_htmlへ作成したアプリのpublic内のファイルをすべて転送
⑦publicフォルダ以外のファイルをまとめて新しく作ったフォルダに転送(public_htmlと同じ階層下)
⑧public_htmlに転送したindex.phpのrequire先を新しく作ったフォルダ先に変更
index.phprequire __DIR__.'/../laraveltest/vendor/autoload.php';//28行目 ~~ $app = require_once __DIR__.'/../laraveltest/bootstrap/app.php';//38行目⑨XAMPP環境下のphpMyAdminで同アプリ用に作ったテーブルをエクスポート(.sqlファイル)
⑩先ほどエクスポートしたsqlファイルをXserver環境下のphpMyAdminにインポートしてテーブル作成
⑪転送した.envを以下のように編集して保存
.envAPP_URL=http://独自ドメイン/ DB_connection=mysql DB_HOST=自身のサーバ名.xserver.jp DB_PORT=3306 DB_DATABASE=自身のデータベース名 DB_USERNAME=自身のユーザー名 DB_PASSWORD=自身で設定したパスワード⑫対象のドメインへアクセスして問題ないか確認。
参考
参考にさせていただいた記事は以下です。
https://qiita.com/hitotch/items/5c8c2858c883e69cd507最後に
完全に個人的備忘録です。
もしかしたらかなり遠回りな方法でデプロイしているかもしれませんので、
他にも良い方法がありましたら教えていただけますと幸いです。
また、初投稿で書き方もあまりわからなかったのでかなり雑な記事になってしまいました。。。
- 投稿日:2020-01-22T17:32:35+09:00
Laravel:ブラウザ上の大量のinput要素に自動的に動作確認用のダミーデータを挿入する方法
はじめに
Laravelに限らず開発をする際において、ブラウザ上で動作確認のテストをすることは重要だと思います。(テストコードを書けという意見もありますが、初心者のうちはテストコードなんて書くのが難しかったりしますので、そこは多めにみてください…)
ブラウザ上で動作確認のテストをする際、大量のinput要素に一個一個手打ちでデータを入れていくのはとても手間がかかり、面倒な作業です。例えば、それが100個のinput要素だったりした時には面倒で面倒で仕方ないと思います。そこで、特定のURLにアクセスすると自動的に動作確認用のダミーデータを挿入する方法を書いていこうと思います。
サンプルコードと解説
今回はサンプルなので実際に使う際は自分なりに変えて使ってみてください。
例えば下記のように、こんなマイグレーションファイルがあったとします。<?php use Illuminate\Support\Facades\Schema; use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Migrations\Migration; class CreatePostsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('posts', function (Blueprint $table) { $table->bigIncrements('id'); $table->string('content1'); $table->string('content2'); $table->string('content3'); $table->string('content4'); $table->string('content5'); $table->string('content6'); $table->string('content7'); $table->string('content8'); $table->string('content9'); $table->string('content10'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('posts'); } }contentを今回は10個用意しました。
さて、次はbladeですが、このように記述します。
@extends('layouts.app') @section('content') <form action="{{ url('/posts/create') }}" method="post"> @csrf @method('POST') <label for="content1">content1</label><br> <input type="text" name="content1" id="content1" value="{{ old('content1', ($post) ? $post->content1 : '') }}"><br> <label for="content2">content2</label><br> <input type="text" name="content2" id="content2" value="{{ old('content2', ($post) ? $post->content2 : '') }}"><br> <label for="content3">content3</label><br> <input type="text" name="content3" id="content3" value="{{ old('content3', ($post) ? $post->content3 : '') }}"><br> <label for="content4">content4</label><br> <input type="text" name="content4" id="content4" value="{{ old('content4', ($post) ? $post->content4 : '') }}"><br> <label for="content5">content5</label><br> <input type="text" name="content5" id="content5" value="{{ old('content5', ($post) ? $post->content5 : '') }}"><br> <label for="content6">content6</label><br> <input type="text" name="content6" id="content6" value="{{ old('content6', ($post) ? $post->content6 : '') }}"><br> <label for="content7">content7</label><br> <input type="text" name="content7" id="content7" value="{{ old('content7', ($post) ? $post->content7 : '') }}"><br> <label for="content8">content8</label><br> <input type="text" name="content8" id="content8" value="{{ old('content8', ($post) ? $post->content8 : '') }}"><br> <label for="content9">content9</label><br> <input type="text" name="content9" id="content9" value="{{ old('content9', ($post) ? $post->content9 : '') }}"><br> <label for="content10">content10</label><br> <input type="text" name="content10" id="content10" value="{{ old('content10', ($post) ? $post->content10 : '') }}"><br> <input type="submit" value="送信"> </form> @endsection続いて、web.phpも書いていきます。
Route::get('/', 'PostsController@index'); Route::get('/posts/create/{type?}', 'PostsController@create'); Route::post('/posts/create', 'PostsController@store');2行目に書いてある
/posts/create/{type?}
が今回重要になってきます。{type?}
はLaravelの任意パラメータというものになります。詳しくはドキュメントを参考にしてください。このように記述し、
アプリ名/posts/create
にアクセスすると、下のようなフォームができていると思います。
フォームができていることを確認したら次はコントローラーを記述していきます。
今回はサンプルで作ったのでバリデーションなどは書いていません。適宜、自分のサービスに合わせてバリデーションを書いてください。<?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Post; class PostsController extends Controller { /** * Display a listing of the resource. * * @return \Illuminate\Http\Response */ public function index() { $posts = Post::all(); return view('posts.index',[ 'posts' => $posts, ]); } /** * Show the form for creating a new resource. * * @return \Illuminate\Http\Response */ public function create($type = null) { $post = []; if(!is_null($type)){ $post = new Post; $dummy_data = config('form_dummy_data'); if(array_key_exists($type, $dummy_data)){ foreach($dummy_data[$type] as $_key => $_val){ $post->$_key = $_val; } } } return view('posts.create',[ 'post' => $post, ]); } /** * Store a newly created resource in storage. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function store(Request $request) { $post = new Post; $post->content1 = $request->content1; $post->content2 = $request->content2; $post->content3 = $request->content3; $post->content4 = $request->content4; $post->content5 = $request->content5; $post->content6 = $request->content6; $post->content7 = $request->content7; $post->content8 = $request->content8; $post->content9 = $request->content9; $post->content10 = $request->content10; $post->save(); return redirect('/'); } }ここで重要になるのはcreateメソッドです。一部抜粋します。
public function create($type = null) { $post = []; if(!is_null($type)){ $post = new Post; $dummy_data = config('form_dummy_data'); if(array_key_exists($type, $dummy_data)){ foreach($dummy_data[$type] as $_key => $_val){ $post->$_key = $_val; } } } return view('posts.create',[ 'post' => $post, ]); }ダミーデータをconfigから呼んでいます。
では、そのダミーデータをconfigディレクトリにファイルを作成して、記述しましょう。
この時に使うファイル名は上で書いたform_dummy_data.php
とします。<?php return [ // バリデーションを通過する入力データ 'post_create_true' => [ 'content1' => 'content1', 'content2' => 'content2', 'content3' => 'content3', 'content4' => 'content4', 'content5' => 'content5', 'content6' => 'content6', 'content7' => 'content7', 'content8' => 'content8', 'content9' => 'content9', 'content10' => 'content10', ], // バリデーションを通過しない入力データ 'post_create_false' => [ 'content1' => 'content1', 'content2' => 'content2', 'content3' => 'content3', 'content4' => 'content4', 'content5' => 'content5', 'content6' => 'content6', 'content7' => 'content7', 'content8' => 'content8', 'content9' => 'content9', 'content10' => 'content10', ] ];trueとfalseで分けているのは、コメントの通り、バリデーションを通すデータと通さないデータに分けているからです。
これでいったん完成なのですが、それでは今度は、
アプリ名/posts/create/post_create_true
か、アプリ名/posts/create/post_create_false
にアクセスしてみてください。フォームに自動的にデータが挿入された状態になっていると思います。
最後に
ブラウザ上で大量のinput要素に自動的にダミーデータを挿入する方法を書きました。
本来ならテストコードを書くべきなのですが、冒頭にも述べた通り、初心者には難しかったり、テストを書く時間がない場合など様々な場面で使えると思います。
ダミーデータも自分のサービスに合わせて作り変えたりして活用してみてください。
- 投稿日:2020-01-22T16:53:14+09:00
LaravelのDockerでX-debugを有効にする
Enable X-debug on Docker for Laravel
始める前に
dockerを使用してx-debugを設定するさまざまな方法に関するチュートリアルがたくさんあります。一部は時代遅れで、一部は他のテクニックを使用します。
Before starting
There are a lot of tutorials on different ways to configure x-debug using docker, some are outdated, some use other techniques, this tutorial is on the configuration that works for me, but I don´t guarantee that it will work for you.
APIの作成方法の前のチュートリアルに従ってください
Follow previous tutorial on how to make an API
nginx.confを編集します
nginx.confファイルにはserver_nameが必要です
the nginx.conf file needs a server_name
server { listen 80; server_name localhost;Dockerfileを編集
Dockerfileを編集して、x-debugプラグインのインストールを追加し、docker-pluginを有効にします
Edit the Dockerfile to add x-debug plugin install and enable the docker-plugin
RUN pecl install xdebug && docker-php-ext-enable xdebug #COPY xdebug.ini /etc/php/7.3/mods-available/xdebug.ini RUN echo "xdebug.remote_enable=1" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \ && echo "xdebug.remote_autostart=1" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \ && echo "xdebug.remote_connect_back = 1" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \ && echo "xdebug.profiler_enable = 1" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini \ && echo "xdebug.remote_port=9000" >> /usr/local/etc/php/conf.d/docker-php-ext-xdebug.inidocker-compose.yml編集
app-serverコンテナ内のdocker-composeファイルでx-debugを有効にします
Enable x-debug on docker-compose file, in the app-server container
services: app-server: build: .cloud/docker image: laravel-api #name of the image environment: PHP_XDEBUG_ENABLED: 1 depends_on:PHP Stormを構成する
PHP設定内で、プロパティを次のように設定します。
Inside the PHP settings, set the properties as follow
サーバーをセットアップする
右上のメニューから新しい構成をセットアップします
Setup a new configuration, from the upper right menu
次のウィンドウで、セットアップ新しいPHPのウェブサイト、左上隅からの+記号をクリックして、
In the next window, setup a new PHP web site, by clickin the + sign from the upper left corner
サーバーリストから[...]ボタンをクリックして、新しいサーバーを作成します。
create a new server, by clicking the [...] button from the server list.
localhostアドレスを追加し、プロジェクトをdockerサーバー(この場合は/ applicationディレクトリ)にマップします。
Add the localhost address, and map the project to the docker server, in our case, the /application directory.
これにより、PHPStormで受信したリクエストをデバッグできます。
With this, you will be able to debug incoming request on PHPStorm.
- 投稿日:2020-01-22T16:09:09+09:00
Laravelの認証処理に独自の制約を追加する
ログインするときにパスワードによる認証だけでなく、独自の制約を追加したい場合があると思います。
例えばユーザが仮登録ではない場合(本登録)のみログインとか、ある権限を有する場合のみログインなどです。
よくある話なので実装も何回か経験していますが、毎回調べながらなので今回記事としてまとめてみました。
とはいってもLaravelのバージョンによって認証周りは結構違うんですよね。
今回試すのはLaravel 6.12.0です(もう12!!)。https://laravel.com/docs/6.x/authentication
https://readouble.com/laravel/6.x/ja/authentication.html準備
$ composer create-project --prefer-dist laravel/laravel auth-sample $ cd auth-sample $ composer require laravel/ui --dev $ php artisan ui vue --auth $ php artisan make:migration alter_table_users_add_column_disabled --table=users $ php artisan migrate $ yarn install $ yarn run dev $ php artisan servedatabase/migrations/alter_table_users_add_column_disabled.php<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class AlterTableUsersAddColumnDisabled extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::table('users', function (Blueprint $table) { $table->tinyInteger('disabled')->default(0)->after('remember_token'); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::table('users', function (Blueprint $table) { $table->dropColumn('disabled'); }); } }今回の認証の仕様
単純にデフォルトの認証ならば、メールアドレスとパスワードのみでログイン出来ます。
そこに今回はdisabled
フラグを追加して、disabled = 0
ならばログインという仕様とします。
どうすれば認証処理で新たな制約を加えることが出来るのか。認証のテスト
まずはテストを書いてみます。
$ php artisan make:test LoginTest
tests/Feature/LoginTest.php<?php namespace Tests\Feature; use Illuminate\Foundation\Testing\RefreshDatabase; use Illuminate\Foundation\Testing\WithFaker; use Tests\TestCase; class LoginTest extends TestCase { use RefreshDatabase; /** * 間違ったパスワードのログイン失敗のテスト * * @return void */ public function testInvalidPasswordLogin() { $user = factory(\App\User::class)->create(['email' => 'dummy@test.jp', 'disabled' => 0]); $response = $this->get('/login'); $response->assertStatus(200); $response = $this->post('/login', ['email' => 'dummy@test.jp', 'password' => 'dummy']); $response->assertStatus(302); $response->assertRedirect('/login'); // 認証不可なのでログイン画面にリダイレクト $this->assertFalse($this->isAuthenticated('web')); } /** * ログイン成功のテスト * * @return void */ public function testLogin() { $user = factory(\App\User::class)->create(['email' => 'enable@test.jp', 'disabled' => 0]); $response = $this->post('/login', ['email' => 'enable@test.jp', 'password' => 'password']); $response->assertStatus(302); $response->assertRedirect('/home'); $this->assertAuthenticatedAs($user, $guard = 'web'); } /** * disabled = 1 のログイン失敗のテスト * * @return void */ public function testDisableLogin() { $user = factory(\App\User::class)->create(['email' => 'disable@test.jp', 'disabled' => 1]); $response = $this->get('/login'); $response->assertStatus(200); $response = $this->post('/login', ['email' => 'disable@test.jp', 'password' => 'password']); $response->assertStatus(302); $response->assertRedirect('/login'); // 認証不可なのでログイン画面にリダイレクト $this->assertFalse($this->isAuthenticated('web')); } }$ vendor/bin/phpunit tests/Feature/LoginTest.php PHPUnit 8.5.2 by Sebastian Bergmann and contributors. ..F 3 / 3 (100%) Time: 326 ms, Memory: 24.00 MB There was 1 failure: 1) Tests\Feature\LoginTest::testDisableLogin Failed asserting that two strings are equal. --- Expected +++ Actual @@ @@ -'http://localhost/login' +'http://localhost/home' /Users/takahashikiyoshi/dev/valet/auth-sample/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestResponse.php:259 /Users/takahashikiyoshi/dev/valet/auth-sample/vendor/laravel/framework/src/Illuminate/Foundation/Testing/TestResponse.php:204 /Users/takahashikiyoshi/dev/valet/auth-sample/tests/Feature/LoginTest.php:65 FAILURES! Tests: 3, Assertions: 15, Failures: 1.
disabled = 1
のユーザが、認証不可で/login
にリダイレクトされなければいけないところを、正常にログインして/home
にリダイレクトされています。
このテストが成功するように修正していきます。認証処理のソースコードを追う
ログインのソースコードを追ってみましょう。
LoginController
app/Http/Controllers/Auth/LoginController.php<?php namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; use App\Providers\RouteServiceProvider; use Illuminate\Foundation\Auth\AuthenticatesUsers; class LoginController extends Controller { /* |-------------------------------------------------------------------------- | Login Controller |-------------------------------------------------------------------------- | | This controller handles authenticating users for the application and | redirecting them to your home screen. The controller uses a trait | to conveniently provide its functionality to your applications. | */ use AuthenticatesUsers; /** * Where to redirect users after login. * * @var string */ protected $redirectTo = RouteServiceProvider::HOME; /** * Create a new controller instance. * * @return void */ public function __construct() { $this->middleware('guest')->except('logout'); } }ログインのコントローラでは
AuthenticatesUsers
トレイトが使われていますね。AuthenticatesUsers
vendor/laravel/framework/src/Illuminate/Foundation/Auth/AuthenticatesUsers.php<?php namespace Illuminate\Foundation\Auth; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Validation\ValidationException; trait AuthenticatesUsers { use RedirectsUsers, ThrottlesLogins; /** * Show the application's login form. * * @return \Illuminate\Http\Response */ public function showLoginForm() { return view('auth.login'); } /** * Handle a login request to the application. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|\Illuminate\Http\JsonResponse * * @throws \Illuminate\Validation\ValidationException */ public function login(Request $request) { $this->validateLogin($request); // If the class is using the ThrottlesLogins trait, we can automatically throttle // the login attempts for this application. We'll key this by the username and // the IP address of the client making these requests into this application. if (method_exists($this, 'hasTooManyLoginAttempts') && $this->hasTooManyLoginAttempts($request)) { $this->fireLockoutEvent($request); return $this->sendLockoutResponse($request); } if ($this->attemptLogin($request)) { return $this->sendLoginResponse($request); } // If the login attempt was unsuccessful we will increment the number of attempts // to login and redirect the user back to the login form. Of course, when this // user surpasses their maximum number of attempts they will get locked out. $this->incrementLoginAttempts($request); return $this->sendFailedLoginResponse($request); } /** * Validate the user login request. * * @param \Illuminate\Http\Request $request * @return void * * @throws \Illuminate\Validation\ValidationException */ protected function validateLogin(Request $request) { $request->validate([ $this->username() => 'required|string', 'password' => 'required|string', ]); } /** * Attempt to log the user into the application. * * @param \Illuminate\Http\Request $request * @return bool */ protected function attemptLogin(Request $request) { return $this->guard()->attempt( $this->credentials($request), $request->filled('remember') ); } /** * Get the needed authorization credentials from the request. * * @param \Illuminate\Http\Request $request * @return array */ protected function credentials(Request $request) { return $request->only($this->username(), 'password'); } /** * Send the response after the user was authenticated. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ protected function sendLoginResponse(Request $request) { $request->session()->regenerate(); $this->clearLoginAttempts($request); return $this->authenticated($request, $this->guard()->user()) ?: redirect()->intended($this->redirectPath()); } /** * The user has been authenticated. * * @param \Illuminate\Http\Request $request * @param mixed $user * @return mixed */ protected function authenticated(Request $request, $user) { // } /** * Get the failed login response instance. * * @param \Illuminate\Http\Request $request * @return \Symfony\Component\HttpFoundation\Response * * @throws \Illuminate\Validation\ValidationException */ protected function sendFailedLoginResponse(Request $request) { throw ValidationException::withMessages([ $this->username() => [trans('auth.failed')], ]); } /** * Get the login username to be used by the controller. * * @return string */ public function username() { return 'email'; } /** * Log the user out of the application. * * @param \Illuminate\Http\Request $request * @return \Illuminate\Http\Response */ public function logout(Request $request) { $this->guard()->logout(); $request->session()->invalidate(); $request->session()->regenerateToken(); return $this->loggedOut($request) ?: redirect('/'); } /** * The user has logged out of the application. * * @param \Illuminate\Http\Request $request * @return mixed */ protected function loggedOut(Request $request) { // } /** * Get the guard to be used during authentication. * * @return \Illuminate\Contracts\Auth\StatefulGuard */ protected function guard() { return Auth::guard(); } }
login()
メソッドの中でattemptLogin()
メソッド、Auth::guard()
のattempt()
が呼ばれています。
attempt()
を調べたらvendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php
に行き着きました。
認証のガードやプロバイダは、config/auth.php
に記述されていますが、これがいまひとつ自分は理解出来ていませんSessionGuard
vendor/laravel/framework/src/Illuminate/Auth/SessionGuard.php<?php namespace Illuminate\Auth; class SessionGuard implements StatefulGuard, SupportsBasicAuth { // 省略 /** * Attempt to authenticate a user using the given credentials. * * @param array $credentials * @param bool $remember * @return bool */ public function attempt(array $credentials = [], $remember = false) { $this->fireAttemptEvent($credentials, $remember); $this->lastAttempted = $user = $this->provider->retrieveByCredentials($credentials); // If an implementation of UserInterface was returned, we'll ask the provider // to validate the user against the given credentials, and if they are in // fact valid we'll log the users into the application and return true. if ($this->hasValidCredentials($user, $credentials)) { $this->login($user, $remember); return true; } // If the authentication attempt fails we will fire an event so that the user // may be notified of any suspicious attempts to access their account from // an unrecognized user. A developer may listen to this event as needed. $this->fireFailedEvent($user, $credentials); return false; } // 省略 }
retrieveByCredentials()
で認証情報からユーザを取得しています。
次に出てくるのが、Eloquentを使っているならvendor/laravel/framework/src/Illuminate/Auth/EloquentUserProvider.php
です。EloquentUserProvider
vendor/laravel/framework/src/Illuminate/Auth/EloquentUserProvider.php<?php namespace Illuminate\Auth; class EloquentUserProvider implements UserProvider { // 省略 /** * Retrieve a user by the given credentials. * * @param array $credentials * @return \Illuminate\Contracts\Auth\Authenticatable|null */ public function retrieveByCredentials(array $credentials) { if (empty($credentials) || (count($credentials) === 1 && array_key_exists('password', $credentials))) { return; } // First we will add each credential element to the query as a where clause. // Then we can execute the query and, if we found a user, return it in a // Eloquent User "model" that will be utilized by the Guard instances. $query = $this->newModelQuery(); foreach ($credentials as $key => $value) { if (Str::contains($key, 'password')) { continue; } if (is_array($value) || $value instanceof Arrayable) { $query->whereIn($key, $value); } else { $query->where($key, $value); } } return $query->first(); } // 省略 }よく知られているように?、Laravelの認証では、まずパスワード以外の情報でユーザを取得してきてから、パスワードをチェックします。
ということは、retrieveByCredentials(array $credentials)
の$credentials
にdisabled = 0
の条件を渡せればいいわけです。
遡って$credentials
はどう取得されるかというと、AuthenticatesUsers
トレイトのcredentials()
メソッドで取得されています。
しかし、/** * Get the needed authorization credentials from the request. * * @param \Illuminate\Http\Request $request * @return array */ protected function credentials(Request $request) { return $request->only($this->username(), 'password'); }
username()
で定義された項目(デフォルトではメールアドレス)とパスワードしか返さない実装となっています。
ここをコントローラでオーバーライドすればいいのでは!?修正後LoginController
app/Http/Controllers/Auth/LoginController.php<?php namespace App\Http\Controllers\Auth; use App\Http\Controllers\Controller; use App\Providers\RouteServiceProvider; use Illuminate\Foundation\Auth\AuthenticatesUsers; use Illuminate\Http\Request; class LoginController extends Controller { /* |-------------------------------------------------------------------------- | Login Controller |-------------------------------------------------------------------------- | | This controller handles authenticating users for the application and | redirecting them to your home screen. The controller uses a trait | to conveniently provide its functionality to your applications. | */ use AuthenticatesUsers; /** * Where to redirect users after login. * * @var string */ protected $redirectTo = RouteServiceProvider::HOME; /** * Create a new controller instance. * * @return void */ public function __construct() { $this->middleware('guest')->except('logout'); } /** * Get the needed authorization credentials from the request. * * @param \Illuminate\Http\Request $request * @return array */ protected function credentials(Request $request) { $credentials = $request->only($this->username(), 'password'); $credentials['disabled'] = 0; return $credentials; } }はたして、、、
$ vendor/bin/phpunit tests/Feature/LoginTest.php PHPUnit 8.5.2 by Sebastian Bergmann and contributors. ... 3 / 3 (100%) Time: 283 ms, Memory: 24.00 MB OK (3 tests, 16 assertions)成功しました
まとめ
今回は
app/Http/Controllers/Auth/LoginController.php
の修正だけで出来ました。
公式ドキュメントにはガードとプロバイダを使った独自の認証について書かれていますが、難しい。。。
なので、つい今回みたいな小手先の修正となってしまいます。
シンプルな仕様ならLaravelは超簡単ですが、複雑なシステムを作ろうとすると奥が深いですね。。(それはどれも同じでは^^;)参考URL
- 投稿日:2020-01-22T15:50:18+09:00
Laravel カスタムバリデーション導入
はじめに
- Laravel ではバリデーションの機能がある。
- デフォルトでもそれなりに使えるけど、カスタムした方が使える時もままある
- ちょっと分からなくて調べたりしたのでそれを記述する
前提
laravel のバージョン
$ php artisan -V Laravel Framework 5.8.16使いたい
①カスタムバリデーションの作成をする
- Validation のクラスを作成する。
- 今回は、場所は
app/Services/
に作成する- メソッド名は
validate
の後ろにオリジナルの名前。この名前がバリデーションの名前になる。- 下記のメソッドは、半角英数字とスペースを許可するってこと。多分。
app/Services/CustomValidator.php<?php namespace App\Services; class CustomValidator extends \Illuminate\Validation\Validator { public function validateAlphanumeric($attribute, $value) { return (preg_match("/^[a-z0-9 ]+$/i", $value)); } }② 呼び出せるようにサービスプロパイダーで有効にする
- クラスを作ったままだと呼べないので、サービスプロパイダーという場所に登録する
AppServiceProvider
に登録を行うapp/ServiceProviders/AppServiceProvider.php<?php namespace App\ServiceProviders; use Illuminate\Support\ServiceProvider; use App\Services\CustomValidator; // 自分で作成したValidationのクラス class AppServiceProvider extends ServiceProvider { /** * Bootstrap any application services. * * @return void */ public function boot() { // 自分で作成した Validation のクラス \Validator::resolver(function($translator, $data, $rules, $messages) { return new CustomValidator($translator, $data, $rules, $messages); }); }③フォーム単位?で使いたい。
- コントローラで呼び出せるように
Request
クラスを生成して、割り当てていく。- 下記のコマンドで
app/Http/Requests/TestRequest.php
を作成する$ php artisan make:request TestRequest
- 作成した
Request
クラスを編集して、バリデーションをはめてく- バリデーションの名前は
validate
を抜いた名前でスネークケースにした名前にするapp/Http/Requests/TestRequest.php<?php namespace App\Http\Requests; use App\Http\Requests\Request; class TestRequest extends Request { public function authorize() { return true; } public function rules() { return [ // 自分で作成したカスタムバリデーションの名前を name = "test_box" の // テキストボックスに当てはめる 'test_box' => 'alpha_numeric', ]; } }
- コントローラ側でも使えるようにしていく
app/Http/Controllers/TestControllers.php<?php namespace App\Http\Controllers\Test; use App\Http\Controllers\Controller; use Illuminate\Http\Request; use App\Http\Request\TestRequest; // 作成した Request のクラスを使う class TestController extends Controller { // 作成した Request クラスを使う public function showForm(TestRequest $request) { 〜処理を色々省略 return view('test.form'); } }④バリデーションエラーの時のメッセージを多言語化
- バリデーションのエラーメッセージを多言語化できる。
/resources/lang/ja
の下にvalidation.php
を作成で日本語ファイル/resources/lang/en
の下にvalidation.php
を作成で英語ファイル/resources/lang/ja/validation.php<?php return [ 'alpha_numeric' => '半角英数字または半角スペース以外は登録できません', ];
- 投稿日:2020-01-22T13:03:59+09:00
【Laravel】SendGridのWeb APIを利用してメール送信を実装
実装手順
SendGridでの設定は省略します。
- .envにトークンを設定
- ライブラリをインストール
- githubのサンプルコードで仮実装
- 処理を分ける
- テンプレートを用意する。
.envにトークンを設定
.envSENDGRID_API_KEY=<SndGridで取得したトークン>ライブラリをインストール
composer require sendgrid/sendgridgithubのサンプルコードで仮実装
とりあえず、サンプルコードを動かしてみます。
必要なものをuseして、適当なメソッドに貼り付けます。
ルーティングの設定を忘れずに。TestMailController.php(省略) use SendGrid; use SendGrid\Mail\Mail; (省略) public function send(Mail $email) { $email->setFrom("test@<登録したドメイン>", "Example User"); // 送信元 $email->setSubject("Sending with Twilio SendGrid is Fun"); // タイトル $email->addTo("<受け取れるメールアドレス>", "Example User"); // 送信先 $email->addContent("text/plain", "and easy to do anywhere, even with PHP"); // プレーンテキストメール $email->addContent( "text/html", "<strong>and easy to do anywhere, even with PHP</strong>" // HTMLメール ); $sendgrid = new SendGrid(env('SENDGRID_API_KEY')); try { $response = $sendgrid->send($email); print $response->statusCode() . "\n"; print_r($response->headers()); print $response->body() . "\n"; } catch (Exception $e) { echo 'Caught exception: '. $e->getMessage() ."\n"; } }送信が完了したら、202から始まるテキストが表示されます。
処理を分ける
SendGrid以外のサービスを利用することになった場合でも、少ない修正で済むようにする!
ということまではしていません。あしからず。まずは、
$sendgrid = new SendGrid(env('SENDGRID_API_KEY'));
の部分をDIできるように、バインディングします。
以下のように登録することで、引数にトークンが渡された状態でDIされます。AppServiceProvider.phpuse SendGrid; (省略) public function register() { $this->app->bind('SendGrid', function ($app) { return new SendGrid(env('SENDGRID_API_KEY')); }); }というわけで、TestMailControllerを以下の通り修正
TestMailController.phppublic function send(Mail $email, SendGrid $sendgrid) // <-追記 { // $sendgrid = new SendGrid(env('SENDGRID_API_KEY')); <-この記述がいらなくなる。 }次に、メール構築部分の処理を分離していきます。
app/Mail/TestMail.phpを作成し、そのコンストラクターでメールを構築するようにします。app/Mail/TestMail.php<?php namespace App\Mail; use SendGrid\Mail\Mail; class TestMail extends Mail // SendGrid\Mail\Mailを継承 { /** * Create a new message instance. * * @return void */ public function __construct($args) { $this->setFrom("test@<登録したドメイン>", "Example User"); $this->setSubject($args->message); $this->addTo($args->to, "Example User"); $this->addContent("text/plain", "and easy to do anywhere, even with PHP"); $this->addContent( "text/html", "<strong>and easy to do anywhere, even with PHP</strong>" ); return $this; } }TestMailControllerを以下の通り修正
TestMailController.php(省略) use SendGrid; use SendGrid\Mail\Mail; use App\Mail\TestMail; // 追記 (省略) public function send(Mail $email, SendGrid $sendgrid) { try { $response = $sendgrid->send(new TestMail($this->args)); $message = 'メールを送信しました。'); // ついでに修正 } catch (Exception $e) { report($e); // ついでに修正 $message = 'メール送信に失敗しました。'); // ついでに修正 } return redirect()->back()->with('flash_message', $message); // ついでに修正 }テンプレートを用意する。
次にBladeテンプレートを用意して、それを利用するようにします。
(以下は、プレーンテキスト用のテンプレートです。)views/emails/test_mail.blade.phpテストメールテンプレート 変数{{ $hoge }}{{ $fuga }}も渡せます。テンプレートを利用するには、viewインスタンスを文字列にキャストしたものを、
addContentに渡せばOKです。app/Mail/TestMail.php<?php namespace App\Mail; use SendGrid\Mail\Mail; class TestMail extends Mail // SendGrid\Mail\Mailを継承 { /** * Create a new message instance. * * @return void */ public function __construct($args) { $hoge = 'hoge'; $fuga = 'fuga'; $this->setFrom("test@<登録したドメイン>", "Example User"); $this->setSubject($args->message); $this->addTo($args->to, "Example User"); $this->addContent( "text/plain", strval(view('emails.test_mail', compact('hoge', 'fuga'))) // viewインスタンスを文字列にキャスト ); // $this->addContent( // "text/html", "<strong>and easy to do anywhere, even with PHP</strong>" // ); return $this; } }複数のメールアドレスに対して送信する場合は、
addTos
メソッドを利用すればいいようです。
Send an Email to Multiple Recipients$tos = [ "test+test1@example.com" => "Example User1", "test+test2@example.com" => "Example User2", "test+test3@example.com" => "Example User3" ]; $email->addTos($tos);この場合、toに複数のアドレスを書き込んでいる状態なので、受信者は全てのメールアドレスを見れてしまいますので、気をつけてください。
マーケティングメールなどを送るときは、メールアドレス分ループさせるのが良さそうです。以上です。
参考
- 投稿日:2020-01-22T13:03:59+09:00
【Laravel】SendGrid Web APIを利用してメールを送信する
実装手順
SendGridでの設定は省略します。
- .envにトークンを設定
- ライブラリをインストール
- githubのサンプルコードで仮実装
- 処理を分ける
- テンプレートを用意する。
.envにトークンを設定
.envSENDGRID_API_KEY=<SndGridで取得したトークン>ライブラリをインストール
composer require sendgrid/sendgridgithubのサンプルコードで仮実装
とりあえず、サンプルコードを動かしてみます。
必要なものをuseして、適当なメソッドに貼り付けます。
ルーティングの設定を忘れずに。TestMailController.php(省略) use SendGrid; use SendGrid\Mail\Mail; (省略) public function send(Mail $email) { $email->setFrom("test@<登録したドメイン>", "Example User"); // 送信元 $email->setSubject("Sending with Twilio SendGrid is Fun"); // タイトル $email->addTo("<受け取れるメールアドレス>", "Example User"); // 送信先 $email->addContent("text/plain", "and easy to do anywhere, even with PHP"); // プレーンテキストメール $email->addContent( "text/html", "<strong>and easy to do anywhere, even with PHP</strong>" // HTMLメール ); $sendgrid = new SendGrid(env('SENDGRID_API_KEY')); try { $response = $sendgrid->send($email); print $response->statusCode() . "\n"; print_r($response->headers()); print $response->body() . "\n"; } catch (Exception $e) { echo 'Caught exception: '. $e->getMessage() ."\n"; } }送信が完了したら、202から始まるテキストが表示されます。
処理を分ける
SendGrid以外のサービスを利用することになった場合でも、少ない修正で済むようにする!
ということまではしていません。あしからず。まずは、
$sendgrid = new SendGrid(env('SENDGRID_API_KEY'));
の部分をDIできるように、バインディングします。
以下のように登録することで、引数にトークンが渡された状態でDIされます。AppServiceProvider.phpuse SendGrid; (省略) public function register() { $this->app->bind('SendGrid', function ($app) { return new SendGrid(env('SENDGRID_API_KEY')); }); }というわけで、TestMailControllerを以下の通り修正
TestMailController.phppublic function send(Mail $email, SendGrid $sendgrid) // <-追記 { // $sendgrid = new SendGrid(env('SENDGRID_API_KEY')); <-この記述がいらなくなる。 }次に、メール構築部分の処理を分離していきます。
app/Mail/TestMail.phpを作成し、そのコンストラクターでメールを構築するようにします。app/Mail/TestMail.php<?php namespace App\Mail; use SendGrid\Mail\Mail; class TestMail extends Mail // SendGrid\Mail\Mailを継承 { /** * Create a new message instance. * * @return void */ public function __construct($args) { $this->setFrom("test@<登録したドメイン>", "Example User"); $this->setSubject($args->message); $this->addTo($args->to, "Example User"); $this->addContent("text/plain", "and easy to do anywhere, even with PHP"); $this->addContent( "text/html", "<strong>and easy to do anywhere, even with PHP</strong>" ); return $this; } }TestMailControllerを以下の通り修正
TestMailController.php(省略) use SendGrid; use SendGrid\Mail\Mail; use App\Mail\TestMail; // 追記 (省略) public function send(Mail $email, SendGrid $sendgrid) { try { $response = $sendgrid->send(new TestMail($this->args)); $message = 'メールを送信しました。'); // ついでに修正 } catch (Exception $e) { report($e); // ついでに修正 $message = 'メール送信に失敗しました。'); // ついでに修正 } return redirect()->back()->with('flash_message', $message); // ついでに修正 }テンプレートを用意する。
次にBladeテンプレートを用意して、それを利用するようにします。
(以下は、プレーンテキスト用のテンプレートです。)views/emails/test_mail.blade.phpテストメールテンプレート 変数{{ $hoge }}{{ $fuga }}も渡せます。テンプレートを利用するには、viewインスタンスを文字列にキャストしたものを、
addContentに渡せばOKです。app/Mail/TestMail.php<?php namespace App\Mail; use SendGrid\Mail\Mail; class TestMail extends Mail // SendGrid\Mail\Mailを継承 { /** * Create a new message instance. * * @return void */ public function __construct($args) { $hoge = 'hoge'; $fuga = 'fuga'; $this->setFrom("test@<登録したドメイン>", "Example User"); $this->setSubject($args->message); $this->addTo($args->to, "Example User"); $this->addContent( "text/plain", strval(view('emails.test_mail', compact('hoge', 'fuga'))) // viewインスタンスを文字列にキャスト ); // $this->addContent( // "text/html", "<strong>and easy to do anywhere, even with PHP</strong>" // ); return $this; } }複数のメールアドレスに対して送信する場合は、
addTos
メソッドを利用すればいいようで。
Send an Email to Multiple Recipients$tos = [ "test+test1@example.com" => "Example User1", "test+test2@example.com" => "Example User2", "test+test3@example.com" => "Example User3" ]; $email->addTos($tos);以上です。
参考
- 投稿日:2020-01-22T01:37:16+09:00
LaravelでCSVダウンロード機能を実装する。
CSVダウンロード機能を実装するにあたり、少しはまった部分があったので備忘録です。
流れ
- 出力データを用意
- 出力データをカンマ区切りで配列にセット
- 改行区切りの文字列に変換
- 一時的にCSVファイルを作成
- CSVファイルに書き込み
- ダウンロードレスポンスを返す。
実装
はまったポイント
- 金額など、値にカンマが含まれる文字の扱い -> 値をクオートで囲むことで1つの値として評価される。
- 出力結果が改行されnai -> 改行コードをダブルコートで囲むことで改行コードとして評価される。
- Excelで開いたときに文字化けする -> SJISにエンコーディングする。public function downloadCsv() { // 1. 出力データをを用意 $csvHeader = ["No,", 師匠名, ふりがな]; $csvData = [ [0001, 春風亭助羊, しゅんぷうてい すけよう], [0002, 桂左志郎, かつら さしろう], [0003, 三遊亭遊智, さんゆうてい ゆうち], [0004, 月亭がた枝, つきてい がたし], [0005, 春雨や雷扇, はるさめや らいおう], [0006, 桂喜八宝, かつら きゃぽう], [0007, 翁家八ゑ千代, おきなや やえちよ], [0008, 春雨や雷海, はるさめや らいかい], ]; // 2. 出力データをカンマ区切りで配列にセット $downloadData = []; $downloadData[] = implode(',', $csvHeader); foreach ($csvData as $record) { // 念の為ダブルクオートで囲む foreach ($record as $i => $v) { $record[$i] = '"' . $v . '"'; } $downloadData[] = implode(',', $record); } // 3. 改行区切りの文字列に変換 $downloadData = implode("\r", $downloadData); // 改行コードはダブルクオートで囲む // Excel対応 $downloadData = mb_convert_encoding($downloadData, "SJIS", "UTF-8"); // 4. 一時的にcsvファイルを作成 if (! file_exists(storage_path('csv'))) { $bool = mkdir(storage_path('csv')); if (! $bool) { throw new Exception("ディレクトリを作成できませんでした。", 500); } } $name = 'test.csv'; $pathToFile = storage_path('csv/' . $name); // 5. CSVファイルを作成 if (! file_put_contents($pathToFile, $csvData)) { throw new Exception("ファイルの書き込みに失敗しました。", 500); } // 6. ダウンロードレスポンスを返却 return response()->download($pathToFile, $name)->deleteFileAfterSend(true); }参考
https://gray-code.com/php/download-for-csv-file/
https://teratail.com/questions/2199
https://bayashita.com/p/entry/show/49
https://qiita.com/ikemonn/items/f2bc4f9f834c989084ff
https://www.megasoft.co.jp/support/mifes/faq/miw8faq/faq017.html
- 投稿日:2020-01-22T00:57:36+09:00
システムからメール送信するときのメール配信サービスを列挙
システムからメール送信をするため、メール配信サービスを調査したので列挙します。
当初は、Amazon SESの利用を検討していましたが、
バウンスメールの処理を独自実装しないといけないため、
他のサービスを検討することとなりました。AWSのSESでバウンスメール(bouncemail)対策。3つの方法とメリット・デメリット
AWS SESでバウンスメールをハンドリングしてみたちなみにLaravelドキュメントで触れられているサービスは、
バージョンによって地味に変わっています。5.7
MailgunとSparkPostなどAPIベースドライバはシンプルでSMTPサーバよりも高速です。
5.8
MailgunとSparkPost、Postmarkなど、APIベースドライバはシンプルでSMTPサーバよりも高速です。
6.x
MailgunとPostmarkなど、APIベースドライバはシンプルでSMTPサーバよりも高速です。
Mailgun
以下の理由で却下
- RFC準拠していないメドに容赦ない
- 単純にAmazon SESの4倍くらい高価
Postmark
このサービスは、トランザクションメールにのみ利用できるというポリシーのようで、今回のメール配信内容としてはマーケティングメールに当たるため、却下
SendGrid
今回はSendGridで要望を満たせたため、こちらを採用しました。
- マーケティングメールでも利用可能
- バウンス処理をしてくれる
- 無料枠が大きい(12,000通/月)