20210510のlaravelに関する記事は6件です。

初心者向け、LaravelとNuxt.jsで作るログイン機能をゼロからいっしょに作ろう!

はじめに LaravelとNuxt.jsを使って開発したいと思っても最初に何を作ったらいいか思いつきませんよね。 どんなサービスを作るにしても基本となる、ログイン機能を作ることで基礎を定着させましょう。 開発環境 基本はDockerで構築しています。 フロントエンド側では、composition-apiやTypeScriptなどを導入しています。 Docker ・php:7.4-fpm-buster ・composer:1.10 ・nginx:1.18-alpine ・mysql:5.7 ・node:14.15.3-alpine Laravel ・Laravel Passport Nuxt.js ・TypeScript ・@nuxtjs/axios ・@nuxtjs/composition-api/module ・vee-validate 完成コード それでは一緒にやってみましょう! Githubからクローン Dockerから解説となると長くなるので省略します。 GitHubにやり方は書いていますが、以下を実行することで環境構築ができます。 ターミナル $ git clone git@github.com:ssk9597/Docker-Laravel-Nuxt-Nginx-MySQL.git $ cd Docker-Laravel-Nuxt-Nginx-MySQL $ make nuxt $ make backend $ make typescript $ make composition-api make typescriptとmake composition-apiの2つを実行時にはファイルに追記が必要となります。 こちらは以下の記事を参考にしてください。 まずはHTMLとCSS部分を作成 frontend/pages/register/index.vue <template> <div class="register-container"> <form class="register-wrapper"> <h2 class="register-title">新規登録</h2> <!-- 姓 --> <label class="register-label"> <span class="register-label-check">必須</span> <span class="register-label-name">姓</span> </label> <input class="register-input-area" type="text" placeholder="例)田中" /> <!-- 名 --> <label class="register-label"> <span class="register-label-check">必須</span> <span class="register-label-name">名</span> </label> <input class="register-input-area" type="text" placeholder="例)太郎" /> <!-- メールアドレス --> <label class="register-label"> <span class="register-label-check">必須</span> <span class="register-label-name">メールアドレス</span> </label> <input class="register-input-area" type="email" placeholder="例)taro@example.com" /> <!-- パスワード --> <label class="register-label"> <span class="register-label-check">必須</span> <span class="register-label-name">パスワード</span> </label> <input class="register-input-area" type="password" placeholder="例)taroTanaka" /> <!-- 新規追加 --> <button class="register-button" type="submit">新規登録</button> </form> </div> </template> <style lang="scss" scoped> .register { &-container { position: relative; height: 100vh; width: 100%; background-color: #eeeeee; } &-wrapper { position: absolute; top: 50%; right: 50%; transform: translateX(50%) translateY(-50%); max-width: 400px; width: 100%; background-color: #fff; border-radius: 10px; padding: 50px 50px; } &-title { text-align: center; padding-bottom: 20px; } &-alert-red { padding: 5px 10px; background: #ffebee; margin-bottom: 10px; font-size: 12px; } &-label { &-check { background-color: #ee4056; color: #fff; padding: 3px 10px; border-radius: 10%; font-size: 0.75rem; font-weight: 600; } &-name { font-size: 0.8rem; padding: 5px; } } &-input { padding-top: 10px; padding-bottom: 15px; display: block; &-area { box-sizing: border-box; width: 100%; padding: 10px; font-size: 14px; color: #303030; border: solid 1px #eee; border-radius: 6px; outline: 0; transition: 0.3s; } &-error { padding-top: 5px; font-size: 12px; color: #ee4056; font-weight: 600; } } &-button { margin-top: 20px; width: 100%; height: 40px; color: #fff; background: #40c7c1; text-decoration: none; border: none; outline: none; border-radius: 8px; cursor: pointer; appearance: none; transition: 0.2s; &:hover { background: #78cec8; } } } </style> フロントエンド側にバリデーションを追加 フロントエンドとバックエンドの両方にバリデーションをつけることが必須です。 フロントエンドにバリデーションがない場合、HTTP通信が走ってしまうのでエラーページへ遷移してしまいます。 また万が一、APIを直接叩かれたときにバックエンド側にバリデーションがないとDBに直接値が入ってしまう可能性があります。 以上の理由からフロントエンドにバリデーションを設定しましょう。 Nuxt.jsには、VeeValidateがあるのでこちらを使って実装していきましょう。 インストール方法 ①VeeValidateをインストールする ターミナル $ docker-compose exec front npm install vee-validate --save ②プラグインファイルを作成する frontend/plugins/vee-validate.js import Vue from 'vue'; // 使用する機能 import { ValidationProvider, ValidationObserver, localize, extend } from 'vee-validate'; // エラーメッセージの日本語 import ja from 'vee-validate/dist/locale/ja.json'; // 使用するルール import { required, email, min, alpha_dash } from 'vee-validate/dist/rules'; extend('required', required); extend('email', email); extend('min', min); extend('alpha_dash', alpha_dash); Vue.component('ValidationProvider', ValidationProvider); Vue.component('ValidationObserver', ValidationObserver); localize('ja', ja); ③nuxt.config.jsに追記 frontend/nuxt.config.js { plugins: [{ src: '@/plugins/vee-validate' }], build: { transpile: ['vee-validate/dist/rules'], }, } これで使えるようになります。 使い方 index.vue // まずは全体をValidationObserverで囲もう。 // :disabledにinvalidが付くことでバリデーションすべてクリアしないとボタンがクリックできません。 <ValidationObserver v-slot="{ invalid }"> <button type="submit" :disabled="invalid">新規登録</button> </ValidationObserver> index.vue // バリデーション対象をValidationProviderで囲もう。 // nameはバリデーションエラー時のメッセージで使われます。 // rulesにどんどんルールを入れていきます。 <ValidationProvider name="パスワード" rules="required|min:8|alpha_dash" v-slot="{ errors }"> <input type="password" placeholder="例)taroTanaka" v-model="password" /> <p>{{ errors[0] }}</p> </ValidationProvider> ルールに関しては以下の公式サイトが網羅されているのでこちらをご確認ください。 それでは実装していきましょう frontend/pages/register/index.vue <template> <div class="register-container"> <ValidationObserver v-slot="{ invalid }"> <form class="register-wrapper"> <h2 class="register-title">新規登録</h2> <!-- 姓 --> <label class="register-label"> <span class="register-label-check">必須</span> <span class="register-label-name">姓</span> </label> <ValidationProvider class="register-input" name="姓" rules="required" v-slot="{ errors }"> <input class="register-input-area" type="text" placeholder="例)田中" /> <p class="register-input-error">{{ errors[0] }}</p> </ValidationProvider> <!-- 名 --> <label class="register-label"> <span class="register-label-check">必須</span> <span class="register-label-name">名</span> </label> <ValidationProvider class="register-input" name="名" rules="required" v-slot="{ errors }"> <input class="register-input-area" type="text" placeholder="例)太郎" /> <p class="register-input-error">{{ errors[0] }}</p> </ValidationProvider> <!-- メールアドレス --> <label class="register-label"> <span class="register-label-check">必須</span> <span class="register-label-name">メールアドレス</span> </label> <ValidationProvider class="register-input" name="メールアドレス" rules="required|email" v-slot="{ errors }" > <input class="register-input-area" type="email" placeholder="例)taro@example.com" /> <p class="register-input-error">{{ errors[0] }}</p> </ValidationProvider> <!-- パスワード --> <label class="register-label"> <span class="register-label-check">必須</span> <span class="register-label-name">パスワード</span> </label> <ValidationProvider class="register-input" name="パスワード" rules="required|min:8|alpha_dash" v-slot="{ errors }" > <input class="register-input-area" type="password" placeholder="例)taroTanaka" /> <p class="register-input-error">{{ errors[0] }}</p> </ValidationProvider> <!-- 新規追加 --> <button class="register-button" type="submit" :disabled="invalid">新規登録</button> </form> </ValidationObserver> </div> </template> スクリプトを追加 それでは次に動作を担うスクリプトの部分を追加していきましょう。 分けて考えた方が分かりやすいと思いますので今回は、data, computed, methodsの3つのパートに分けて説明していきます。 data 特に難しいところはないですね。 lastName、firstName、email、passwordの4つは、DBへ登録するために使う値です。 errorsはエラー文を表示する際に使用します。 frontend/pages/register/index.vue <template> <div class="register-container"> <ValidationObserver v-slot="{ invalid }"> <form class="register-wrapper"> <h2 class="register-title">新規登録</h2> <!-- 姓 --> <label class="register-label"> <span class="register-label-check">必須</span> <span class="register-label-name">姓</span> </label> <ValidationProvider class="register-input" name="姓" rules="required" v-slot="{ errors }"> <input class="register-input-area" type="text" placeholder="例)田中" v-model="lastName" /> <p class="register-input-error">{{ errors[0] }}</p> </ValidationProvider> <!-- 名 --> <label class="register-label"> <span class="register-label-check">必須</span> <span class="register-label-name">名</span> </label> <ValidationProvider class="register-input" name="名" rules="required" v-slot="{ errors }"> <input class="register-input-area" type="text" placeholder="例)太郎" v-model="firstName" /> <p class="register-input-error">{{ errors[0] }}</p> </ValidationProvider> <!-- メールアドレス --> <label class="register-label"> <span class="register-label-check">必須</span> <span class="register-label-name">メールアドレス</span> </label> <ValidationProvider class="register-input" name="メールアドレス" rules="required|email" v-slot="{ errors }" > <input class="register-input-area" type="email" placeholder="例)taro@example.com" v-model="email" /> <p class="register-input-error">{{ errors[0] }}</p> </ValidationProvider> <!-- パスワード --> <label class="register-label"> <span class="register-label-check">必須</span> <span class="register-label-name">パスワード</span> </label> <ValidationProvider class="register-input" name="パスワード" rules="required|min:8|alpha_dash" v-slot="{ errors }" > <input class="register-input-area" type="password" placeholder="例)taroTanaka" v-model="password" /> <p class="register-input-error">{{ errors[0] }}</p> </ValidationProvider> <!-- 新規追加 --> <button class="register-button" type="submit" :disabled="invalid">新規登録</button> </form> </ValidationObserver> </div> </template> <script lang="ts"> import { defineComponent, ref } from '@nuxtjs/composition-api'; export default defineComponent({ setup() { // data const lastName = ref<string>(''); const firstName = ref<string>(''); const email = ref<string>(''); const password = ref<string>(''); const errors = ref<string>(''); return { // data lastName, firstName, email, password, errors, }; }, }); </script> computed こちらはHTMLの部分が何も変わらないのでスクリプト部分のみ記載します。 ポイントは2つです。 computedは、@nuxtjs/composition-api内で読み込みが必要 refはvalueで値を取得するため、lastName.value, firstName.valueとなる frontend/pages/register/index.vue <script lang="ts"> import { defineComponent, ref, computed } from '@nuxtjs/composition-api'; export default defineComponent({ setup(props, context) { // data const lastName = ref<string>(''); const firstName = ref<string>(''); // computed const name = computed(() => { return `${lastName.value} ${firstName.value}`; }); return { // data lastName, firstName, // computed name, }; }, }); </script> methods こちらは重要なポイントがオンパレードです。 なので順に説明していきます。 frontend/pages/register/index.vue <template> <div class="register-container"> <ValidationObserver v-slot="{ invalid }"> <form class="register-wrapper" @submit.prevent="registerUser()"> <h2 class="register-title">新規登録</h2> <div v-if="errors" class="register-alert-red"> <p v-if="errors.name">{{ errors.name[0] }}</p> <p v-if="errors.email">{{ errors.email[0] }}</p> <p v-if="errors.password">{{ errors.password[0] }}</p> </div> <!-- 姓 --> <label class="register-label"> <span class="register-label-check">必須</span> <span class="register-label-name">姓</span> </label> <ValidationProvider class="register-input" name="姓" rules="required" v-slot="{ errors }"> <input class="register-input-area" type="text" placeholder="例)田中" v-model="lastName" /> <p class="register-input-error">{{ errors[0] }}</p> </ValidationProvider> <!-- 名 --> <label class="register-label"> <span class="register-label-check">必須</span> <span class="register-label-name">名</span> </label> <ValidationProvider class="register-input" name="名" rules="required" v-slot="{ errors }"> <input class="register-input-area" type="text" placeholder="例)太郎" v-model="firstName" /> <p class="register-input-error">{{ errors[0] }}</p> </ValidationProvider> <!-- メールアドレス --> <label class="register-label"> <span class="register-label-check">必須</span> <span class="register-label-name">メールアドレス</span> </label> <ValidationProvider class="register-input" name="メールアドレス" rules="required|email" v-slot="{ errors }" > <input class="register-input-area" type="email" placeholder="例)taro@example.com" v-model="email" /> <p class="register-input-error">{{ errors[0] }}</p> </ValidationProvider> <!-- パスワード --> <label class="register-label"> <span class="register-label-check">必須</span> <span class="register-label-name">パスワード</span> </label> <ValidationProvider class="register-input" name="パスワード" rules="required|min:8|alpha_dash" v-slot="{ errors }" > <input class="register-input-area" type="password" placeholder="例)taroTanaka" v-model="password" /> <p class="register-input-error">{{ errors[0] }}</p> </ValidationProvider> <!-- 新規追加 --> <button class="register-button" type="submit" :disabled="invalid">新規登録</button> </form> </ValidationObserver> </div> </template> <script lang="ts"> import { defineComponent, ref, computed, useContext } from '@nuxtjs/composition-api'; export default defineComponent({ setup(props, context) { // axios const { $axios } = useContext(); // router const router = context.root.$router; // data const lastName = ref<string>(''); const firstName = ref<string>(''); const email = ref<string>(''); const password = ref<string>(''); const errors = ref<string>(''); // computed const name = computed(() => { return `${lastName.value} ${firstName.value}`; }); // methods const registerUser = async () => { try { // 新規登録 await $axios.post('/users/register', { name: name.value, email: email.value, password: password.value, }); // ログイン const res = await $axios.post('/users/login', { email: email.value, password: password.value, }); // token await $axios.setToken(res.data.token, 'Bearer'); router.push('/'); } catch (err) { errors.value = err.response.data.errors; } }; return { // data lastName, firstName, email, password, errors, // computed name, // methods registerUser, }; }, }); </script> ①axiosの使い方 frontend/pages/register/index.vue // axios const { $axios } = useContext(); ②routerの使い方 frontend/pages/register/index.vue // router const router = context.root.$router; ③バックエンドからのエラーメッセージの取得方法 err.response.data.errorsで取得できます。 frontend/pages/register/index.vue <script lang="ts"> export default defineComponent({ setup(props, context) { const registerUser = async () => { try {} catch (err) { errors.value = err.response.data.errors; } }; }, }); </script> では次にバックエンド側を作っていきましょう! まずはLaravel Passportを導入しよう LaravelにはデフォルトでトークンベースのシンプルなAPI認証の機能が備わっています。 しかし公式マニュアルにある通り、Laravel Passportを使用することを強く推奨されています。 なので今回も準拠した形で作っていきましょう。 インストール方法 ①パッケージをインストール ターミナル $ docker-compose exec app composer require laravel/passport ②DBをマイグレーションしてキーを発行する ターミナル $ docker-compose exec app php artisan migrate $ docker-compose exec app php artisan passport:install ③Models/User.phpを編集 api/app/Models/User.php <?php namespace App\Models; use Laravel\Passport\HasApiTokens; // 追記 class User extends Authenticatable { use HasApiTokens, Notifiable; // 追記 } ④Providers/AuthServiceProvider.phpを編集 api/app/Providers/AuthServiceProvider.php <?php namespace App\Providers; use Laravel\Passport\Passport; // 追記 class AuthServiceProvider extends ServiceProvider { public function boot() { $this->registerPolicies(); Passport::routes(); // 追記 } } ⑤config/auth.phpを編集 api/config/auth.php <?php return [ 'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'users', ], 'api' => [ 'driver' => 'passport', 'provider' => 'users', 'hash' => false, ], ], } これで使えるようになりました。 Authを使うのでCORSを修正しよう header情報にtokenを設置するのでCORSの修正が必要になります。 api/app/Http/Middleware/Cors.php <?php namespace App\Http\Middleware; use Closure; class Cors { public function handle($request, Closure $next) { return $next($request) ->header('Access-Control-Allow-Origin', 'http://localhost:3000') ->header('Access-Control-Allow-Methods', 'GET, POST') ->header('Access-Control-Allow-Headers', 'Content-Type, X-XSRF-TOKEN, Authorization') ->header('Access-Control-Allow-Credentials', 'true'); } } バリデーションを作成する ターミナル $ docker-compose exec app php artisan make:request StoreRegister api/app/Http/Requests/StoreBlogPost.php // ①trueに変更(これをしないとうまく作用しない) public function authorize() { return true; } public function rules() { return [ "name" => "required|string", "email" => "required|email|unique:users|max:255", "password" => "required|min:8" ]; } コントローラーを作成する ターミナル $ docker-compose exec app php artisan make:controller LoginController api/app/Http/Controllers/LoginController.php <?php namespace App\Http\Controllers; use Illuminate\Http\Request; // Models use App\Models\User; // Hash use Illuminate\Support\Facades\Hash; // Auth use Illuminate\Support\Facades\Auth; // Validation use App\Http\Requests\StoreRegister; class LoginController extends Controller { // 新規登録 public function register(StoreRegister $request) { // instance $user = new User; // value_save $user->fill(array_merge($request->all(), ["password" => Hash::make($request->password)]))->save(); } // ログイン public function login(Request $request) { // validation $login = $request->validate([ "email" => "required|email", "password" => "required|string|min:8" ]); // validationエラー if (!Auth::attempt($login)) { return response(["message" => "メールアドレスもしくはパスワードが間違っています"]); }; // token発行 $token = Auth::user()->createToken('authToken')->accessToken; // tokenとユーザー情報を受け取る return response(["user" => Auth::user(), "token" => $token]); } } ルーティング api/routes/web.php Route::middleware(['cors'])->group(function () { Route::options('accounts', function () { return response()->json(); }); Route::post("/users/register", "LoginController@register"); Route::post("/users/login", "LoginController@login"); }); これで新規登録画面が作成できました。 それでは次にログイン画面を作っていきましょう。 バックエンドの処理に関してはすべて記載したので、フロントエンド部分のみ作ります。 ログイン画面 frontend/pages/login/index.vue <template> <div class="login-container"> <ValidationObserver v-slot="{ invalid }"> <form class="login-wrapper" @submit.prevent="loginUser()"> <h2 class="login-title">ログイン</h2> <div v-if="errors" class="login-alert-red"> <p>{{ errors }}</p> </div> <!-- メールアドレス --> <label class="login-label"> <span class="login-label-name">メールアドレス</span> </label> <ValidationProvider class="login-input" name="メールアドレス" rules="required|email" v-slot="{ errors }" > <input class="login-input-area" type="email" placeholder="例)taro@example.com" v-model="email" /> <p class="login-input-error">{{ errors[0] }}</p> </ValidationProvider> <!-- パスワード --> <label class="login-label"> <span class="login-label-name">パスワード</span> </label> <ValidationProvider class="login-input" name="パスワード" rules="required|min:8|alpha_dash" v-slot="{ errors }" > <input class="login-input-area" type="password" placeholder="例)taroTanaka" v-model="password" /> <p class="login-input-error">{{ errors[0] }}</p> </ValidationProvider> <!-- 新規追加 --> <button class="login-button" type="submit" :disabled="invalid">ログイン</button> </form> </ValidationObserver> </div> </template> <script lang="ts"> import { defineComponent, ref, useContext } from '@nuxtjs/composition-api'; export default defineComponent({ setup(props, context) { // axios const { $axios } = useContext(); // router const router = context.root.$router; // data const email = ref<string>(''); const password = ref<string>(''); const errors = ref<string>(''); // methods const loginUser = async () => { try { const res = await $axios.post('/users/login', { email: email.value, password: password.value, }); // error_message errors.value = res.data.message; // errorがない時TOPページへ移動する if (!errors.value) { router.push('/'); } // token await $axios.setToken(res.data.token, 'Bearer'); } catch (err) { errors.value = err.response.data.errors; } }; return { // data email, password, errors, // methods loginUser, }; }, }); </script> <style lang="scss" scoped> .login { &-container { position: relative; height: 100vh; width: 100%; background-color: #eeeeee; } &-wrapper { position: absolute; top: 50%; right: 50%; transform: translateX(50%) translateY(-50%); max-width: 400px; width: 100%; background-color: #fff; border-radius: 10px; padding: 50px 50px; } &-title { text-align: center; padding-bottom: 20px; } &-alert-red { padding: 5px 10px; background: #ffebee; margin-bottom: 10px; font-size: 12px; } &-label { &-name { font-size: 0.8rem; } } &-input { padding-top: 10px; padding-bottom: 15px; display: block; &-area { box-sizing: border-box; width: 100%; padding: 10px; font-size: 14px; color: #303030; border: solid 1px #eee; border-radius: 6px; outline: 0; transition: 0.3s; } &-error { padding-top: 5px; font-size: 12px; color: #ee4056; font-weight: 600; } } &-button { margin-top: 20px; width: 100%; height: 40px; color: #fff; background: #40c7c1; text-decoration: none; border: none; outline: none; border-radius: 8px; cursor: pointer; appearance: none; transition: 0.2s; &:hover { background: #78cec8; } } } </style> 終わりに 特に難しいと思うポイントも少なく実装できるかと思います。 この後は掲示板サイトを作ってみても、何かしらのアプリケーションを作ってみてもいいと思います。 こちらをスタートに良質なポートフォリオをたくさん作っていってくださいね。 何かわからないところや間違っているところがありましたらご指摘よろしくお願いいたします。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Laravel】faviconが表示されないときに試したこと

今回は簡単にですがfaviconの設定方法について解説します。 laravelでfaviconを設定する情報がそもそも少ないんですが、他の記事通りに設定してみても表示されなくて、結構ハマってしまいました。 開発環境 PHP 7.3.9 MAMP 5.7 Mysql 5.7.26 Laravel 6.20.7 Composer 2.0.8 favicon(ファビコン)とは faviconとはタブの上部に表示されるアイコンのことです。 faviconの作成方法 自分が使いたい画像を用意して以下のサイトでfavicon用に加工できます。 Favicon & App Icon Generator faviconの設定方法 生成したfavicon.ico(ファイル名は任意で拡張子.icoのもの)をpublicディレクトリ配下に置きます。 こんな感じですね。 あとはresources/views/layouts/app.blade.phpのheadタグ内で以下の記述をすれば表示されます。 app.blade.php <link rel="shortcut icon" type="image/x-icon" href="{{ asset('/favicon.ico') }}"> 基本的には以上の設定で大丈夫だと思われますが、 どのブラウザで表示することを想定するかによって設定が異なるで注意してください。 faviconが表示されないとき 「favicon laravel」等で検索して出てくる記事は上記の設定で表示できると書かれていることが多いのですが、なぜか僕の環境では表示できなかったので、もし同じ状態の方は参考にしてください。 僕が修正したのは単純にfavicon.icoをpublicディレクトリ直下に置くのではなくpublic配下にimagesディレクトリを作成してその中にfavicon.icoを設置しました。 こんな感じです。 その後、resources/views/layouts/app.blade.phpのheadタグ内の記述を以下に変更します。 app.blade.php <link rel="shortcut icon" type="image/x-icon" href="{{ asset('images/favicon.ico') }}"> こうするとしっかり表示されるようになりました。 パスの書き方が間違っているのかと思い、いろいろ試しましたがなぜかpublic直下では全く読み込みできなかったです。 再現性は低いかもしれませんがどなたかの役に立てれば幸いです。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

パスワード変更機能の実装

前回実装した退会機能に引き続き今回はパスワード変更機能を実装していこうと思います。 (退会機能の実装についてはこちらから) 【Laravel】論理削除を利用して退会機能を実装する 使用環境 AWS Cloud9 Laravel 5.5.50 実装する機能の内容 今回のパスワード変更機能はメール認証をしない仕様で実装します。 コントローラーの作成 $ php artisan make:controller ChangePasswordController ルートの定義 web.php //省略 Route::group(['middleware'=>'auth'],function(){ //中略 Route::get('/password/change','ChangePasswordController@edit')->name('password.form'); Route::put('/password/change','ChangePasswordController@update')->name('password.change'); パスワードの変更についてはログイン認証をしたユーザーのみがアクセスできるようにする。 /password/changeのアドレスでパスワードの変更画面を表示させる。 コントローラーの編集 ChangePasswordController.php use App\User; use Illuminate\Http\Request; use Illuminate\Support\Facades\Validator; class ChangePasswordController extends Controller { public function edit() { return view('password.form'); } protected function validator(array $data) { return Validator::make($data,[ 'new_password' => 'required|string|min:6|confirmed', ]); } public function update(Request $request) $user = \Auth::user(); if(!password_verify($request->current_password,$user->password)) { return redirect('/password/change') ->with('warning','パスワードが違います'); } //新規パスワードの確認 $this->validator($request->all())->validate(); $user->password = bcrypt($request->new_password); $user->save(); return redirect ('/') ->with('status','パスワードの変更が終了しました'); } } editメソッドでパスワード変更画面(password.form)に飛ばします。 validatorファザードを使用してバリデーションをした値をupdateメソッドに返します。 updateメソッドではまず現在のパスワードをpassword_verifyを用いて確認、一致しないのであればエラーのフラッシュメッセージを返すよう記述。 その後新しく入力されたパスワードが要件を満たしているかを確認後保存されるように記述、成功したのであれば完了文をTOPページに表示させます。 今回はフラッシュメッセージを用いてエラーメッセージ等を出せるようにしていきます。 リダイレクト先でフラッシュメッセージを表示させるにはredirectメソッドにアロー演算子を用いてwith()メソッドでつなぎます。 第1引数にキー、第2引数に値を入れます。 フラッシュメッセージの表示 form.php @if(session('warning')) <div class="alert alert-danger"> {{ session('warning') }} </div> @endif データはセッションを用いて表示されるのでsession()を使う。 statusを表示させるためにtop画面にも同様の追記。 変更に成功した場合 以前のパスワードが一致しない場合 参考:Laravel でフラッシュメッセージを表示する方法 パスワード編集画面の実装 resources >> views >> password >> form form.php @if(session('warning'))//エラー文を表示させる <div class="alert alert-danger"> {{ session('warning') }} </div> @endif <div class="row mt-5 mb-5"> <div class="col-sm-6 offset-sm-3"> {!! Form::open(['route'=>'password.change','method'=>'put']) !!} <div class="form-group"> {!! Form::label('current_password','以前のパスワード') !!} {!! Form::password('current_password',['class'=>'form-control']) !!} </div> <div class="form-group"> {!! Form::label('new_password','新しいパスワード') !!} {!! Form::password('new_password',['class'=>'form-control']) !!} </div> <div class="form-group"> {!! Form::label('new_password_confirmation','パスワードの確認') !!} {!! Form::password('new_password_confirmation',['class'=>'form-control']) !!} </div> {!! Form::submit('パスワードを変更する',['class'=>'btn btn btn-primary mt-2']) !!} {!! Form::close() !!} </div> </div> @endsection 感想 ・フラッシュメッセージを新しく知れてよかった。 ・自分でバリデーションを実行することでバリデーションの流れについて学べた。 ・難しいかと思ったが意外とシンプルだった。 ・次回はメール認証でのパスワード変更にチャレンジする。 参考 Laravel入門 Laravel公式 バリデーション
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel 8 の Model を使って複数のキーでリレーションをする方法

こんにちは、最近またLaravelをゴリゴリ書いています。 以前Laravelを使ってからもう1.5年ほど経って、色々とお作法が変わっていたり忘れていたりしたので結構戸惑いました。 とくに、データベースのリレーションには苦労しましたです、はい… ということで、タイトルの通り「複数のカラムの値を使って、別のテーブルのデータを絞り込んだ上で取り込みたい!」という願いを叶える方法を書き残しておきます。 上級サブクエリというなんかすごそうな名前の機能を使う Laravelのドキュメントでいうと、下記のページにやり方が記載されています。 この中の上級サブクエリを使って求める結果を得るのです。 まず、下記のようなテーブルを用意します。 companies id name 1 有限会社倉敷 2 株式会社足軽 3 株式会社 branch_offices id name company_id 1 東京本社 1 2 愛知本社 2 3 神奈川本社 3 4 長野支社 1 5 沖縄支社 1 6 東京支社 2 7 北海道支社 3 emproyees id name company_id branch_office_id 1 徳川 1 1 2 織田 1 4 3 上杉 1 5 4 源 2 2 5 坂本 2 6 6 西郷 3 7 これらをもとに、社名と勤務先を含んだ従業員 (emproyee) 一覧を作りたいとします。 そうなるとですよ、会社名はいいとして、勤め先の本社や支社は company_id, branch_office_id を使って絞り込む必要があるわけです。 最終的には下記のようなデータが欲しいわけですよ。 ID 名前 会社名 支店名 1 徳川 有限会社幕末 東京本社 2 西郷 有限会社幕末 長野支社 3 中岡 有限会社幕末 沖縄支社 4 上杉 株式会社戦国 愛知本社 5 織田 株式会社戦国 東京支社 6 スサノオ 株式会社ヤマト 北海道支社 Modeでサブクエリを書く これを実現するために書くコードは下記の通り。 Emproyee.php <?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Customer extends Model { use HasFactory; public function company() { return $this->belongsTo(Company::class); } } HomeController <?php namespace App\Http\Controllers; use Illuminate\Http\Request; use App\Models\Employee; use App\Models\BranchOffice; class HomeController extends Controller { public function home(Request $request) { $query = Employee::addSelect(['branch_office' => BranchOffice::select('name') ->whereColumn('company_id', 'branch_offices.company_id') ->whereColumn('branch_office_id', 'branch_offices.id')]); $employees = $query->get(); foreach ($employees as $employee) { static $employeesArray; $employeesArray[] = [ 'id' => $employee->id, 'name' => $employee->name, 'company' => $employee->company->name, 'branch_office' => $employee->branch_office, ]; }; return view('welcome', ['employees' => $employeesArray]); } } ということで、addSelectのサブクエリでwhereColumnを2つ繋げばいいというだけでした。 わかってみれば非常に簡単に組み立てられますね。 この手の道具は普段から触っておかないとだめだな…
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Laravel]Implicit BindingとgetRouteKeyName() メソッドについて

LaravelのImplicit BindingとgetRouteKeyName() メソッドについて、備忘録でまとめます! 1. ☆彡Implicit Bindingとは☆彡 URLからid を受け取って、Controllerでそのidを元にデータを引っ張ってくるという流れはよく行われるので、特に記述しなくても、自動的にモデルをデータに結びつけられるImplicit Bindingという仕組みが用意されているのだそう。 例えば、ブログサイトとかで個別記事のURLを『 http://127.0.0.1:8000/articles/1 』のようにしたかった場合。(環境はローカル環境を想定。) ルートファイル(web.phpね。)の記述は、以下のようになる。 Route::get('/articles/{article}', 'ArticlesController@show'); これにヒットする、ArticlesControllerのshow()メソッドを以下のように書いたとすると。。。 public function show(Article $article) { return view('articles.show', ['article' => $article]); } show(Article $article)の、仮引数の箇所の 『(Article \$article)』の記述がImplicit Bindingとなる。 これは、Articleの部分がArticleモデルってことで、\$articleの部分が、Articleモデルのインスタンスってことになる。だから、このようなImplicit Bindingの書き方で書くと、『必ずそのモデルのインスタンスが渡ってくる』ってことになる。 で、ここで一つ知っておくべきことは、Implicit Bindingの場合、Laravelはデフォルトで、『該当するモデルのインスタンスのidに対応するデータを取ってくる』ということ。 だから、『 http://127.0.0.1:8000/articles/1 』とか、『 http://127.0.0.1:8000/articles/2 』のようなURLにしたい場合(idを普通の連番にした場合と想定。)は特に問題ないのだけれども、 『 http://127.0.0.1:8000/articles/my-profile 』とか、idではなくtitleとかの違うカラムのデータを引っ張ってきてURLにしたい場合はエラーになってしまう。 (※ ここで、idではないカラムのデータを引っ張ってきたい場合は、該当するモデルファイルにgetRouteKeyName() メソッドを追記することで解決できます。これに関しては後述しています。) つまり、以下二つのコードは全く同じ結果を返すことになる。 public function show($id) { $article = Article::findOrFail($id); return view('articles.show', ['article' => $article]); } public function show(Article $article) { return view('articles.show', ['article' => $article]); } 1つ目はImplicit Bindingではない書き方で、2つ目はImplicit Bindingで書いたもの。 Implicit Bindingは、『(Article \$article)』の部分で、既にArticleモデルのidを引っ張ってきているから、1つ目のコードように、わざわざ『Article::findOrFail(\$id);』とかを書かなくてもいいというわけ。 因みに、findOrFail()というメソッドですが、例えは『Article::find(\$id);』と書いたとすると、もしデータベースに登録のないidでアクセスされるとエラー表示されてしまうけれど、findOrFail()であればエラー表示ではなく、『404|Not Found』が表示されるようになるそうです。 !!!注意点!!! ルートファイルで、ワイルドカード(URLの{}のところ)を変更したら、コントローラーの仮引数の部分も併せて変更しなくてはなりません!↓↓の例のように、赤い文字部分は全部合わせて! 『Route::get('/articles/{foobar}', 'ArticlesController@show');』 『public function show(Article \$foobar) { return view('articles.show', ['article' => \$foobar]); } 』 2. ☆彡getRouteKeyName() メソッドについて☆彡 さて、前述した通り、Implicit Bindingはデフォルトだと、該当するモデルのインスタンスのidを引っ張ってきてしまうわけすが、これをidではないものにしたい場合は、該当するモデルファイルにgetRouteKeyName() メソッドを以下のように追記することで解決できます。 ※ 『return 'slug';』のslugとは、URLの末尾の部分を任意の文字列に指定できる機能のことらしい。 Article.php <?php namespace App; use Illuminate\Database\Eloquent\Model; class Article extends Model { public function getRouteKeyName() { return 'slug'; //Article::where('slug', $article)->first() という意味。 } } 以上です!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

laravel newInstance() メソッドの定義場所 個人メモ

目的 各モデルクラスでインスタンスを作るときに使用するnewInstance()メソッドの記載場所をメモ的にまとめておく 場所 アプリ名ディレクトリ/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.phpの中に記載されている。 下記に定義部分を抜粋して記載する。 アプリ名ディレクトリ/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php /** * Create a new instance of the given model. * * @param array $attributes * @param bool $exists * @return static */ public function newInstance($attributes = [], $exists = false) { // This method just provides a convenient way for us to generate fresh model // instances of this current model. It is particularly useful during the // hydration of new objects via the Eloquent query builder instances. $model = new static((array) $attributes); $model->exists = $exists; $model->setConnection( $this->getConnectionName() ); $model->setTable($this->getTable()); return $model; } 各モデルクラスはアプリ名ディレクトリ/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.phpに記載されているModelクラスを継承して作成されている。そのため各モデルクラスでもnewInstance()メソッドを使うことができる。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む