20200317のlaravelに関する記事は12件です。

Laravel7.XでVue.jsを実装してみる

npmを起動してみる

npm run watchを叩くと以下のエラーメッセージが出力

terminal
'cross-env' is not recognized as an internal or external command,
operable program or batch file.

こちらのリンクを参考にして解決
https://stackoverflow.com/questions/45034581/laravel-5-4-cross-env-is-not-recognized-as-an-internal-or-external-command

Bootstrap-Vueの実装

こちらのドキュメントを参考に実施(https://bootstrap-vue.js.org/docs)

1) npmでbootstrap-vueをインストール

terminal
# With npm
npm install vue bootstrap-vue bootstrap

2) bootstrap-vueをインポート

resources\js\bootstrap.js
import Vue from 'vue'
import { BootstrapVue, IconsPlugin } from 'bootstrap-vue'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'

3) BladeとVueファイルをVueに合わせて構成
(作成中…)
Laravel5.xではVue環境が構築されていた気がするが(Example-component等)
7.xはない…

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel DBクラスを利用(SQL文を使ってDB操作)

使い方

use Illuminate\Support\Facades\DB;
# 例  DB::select('select * from テーブル名')
$items = DB::select('select * from people'); #この例ではpeopleテーブルを全て呼び出し

パラメータ結合

# 例  id検索
$param = ['id' => $request->id];
$items = DB::select('select * from people where id = :id', $param);

インサート

DB::insert(クエリ文, パラメータ配列);

viewにidを渡す

下記のようにするとurlにidを含めることができる

HogeController.php
  public function edit(Request $request){
    $params = ['id' => $request->id];
    # select文でurlで指定したidデータを取得(今回はエラー処理していないのでidがなければコケる)
    $item = DB::select('select * from people where id = :id', $params);
    return view('hello.edit', ['form' => $item[0]]);
  }
web.php
Route::get('/hoge/edit/{id}', 'HogeController@edit');

コントローラで['form' => $item[0]をviewに渡しているので$formが使える

view.blade.php
  <table>
    <form action="/hoge/edit" method="post">
      {{ csrf_field() }}
      <input type="hidden" name="id" value="{{ $form->id }}">
      <tr>
        <th>name: </th>
        <td><input type="text" name="name" value="{{ $form->name }}"></td>
      </tr>
      <tr>
        <th>email: </th>
        <td><input type="text" name="email" value="{{ $form->email }}"></td>
      </tr>
      <tr>
        <th>age: </th>
        <td><input type="text" name="age" value="{{ $form->age }}"></td>
      </tr>
      <tr>
        <th></th>
        <td><input type="submit" value="送信"></td>
      </tr>
    </form>
  </table>

update

HogeController.php
  public function update(Request $request){
    $params = [
      'id' => $request->id,
      'name' => $request->name,
      'email' => $request->email,
      'age' => $request->age
    ];
    DB::update('update people set name = :name, email = :email, age = :age where id = :id', $params);
    return redirect('/hoge');
  }
web.php
Route::post('/hoge/edit', 'HelloController@update');
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelでよく使うコマンド集

概要

ここでは、Laravelの初心者向けによく使うコマンドの説明をする。
基本的にはphp artisan なんとかーが多い。

Laravelで使用するコマンド集(よく使うものだけ)

composer dump-autoload

composer dump-autoload

自身で作成したクラスのファイルをフレームワークに認識させる。
これを行うことでrequire('ファイル名')require_once('ファイル名')が不要になる。
詳しくは、ここをチェック。

php artisan migrate

php artisan migrate

このコマンド1発で、Laravelで使用する全テーブルのCREATE文が実行される感じ。
対象のテーブル定義は、php artisan make:migrationで作成する。

php artisan db:seed

php artisan db:seed

このコマンド1発で、空っぽのテーブルに初期データを入れられる。
初期データの定義は、php artisan make:seederで作成する。

php artisan make:migration

php artisan make:migration [テーブル名]

テーブル作成用のクラスを作成する。
DB:insertなどを使用してCREATE文となる基を記述する。
Laravelは基本的にSQLを書くことはない。
詳しくは、https://readouble.com/laravel/6.x/ja/migrations.htmlが参考になる。

php artisan make:seeder

php artisan make:seeder [テーブル名(アッパーキャメル記法)]Seeder
↓例(テーブル名「m_user_types」の場合)↓
php artisan make:seeder MUserTypesSeeder

初期データのINSERTとなるレコードを記述する。
詳しくは、https://readouble.com/laravel/6.x/ja/seeding.htmlが参考になる。
これをやると、マスターデータなどコマンド1発で全て入る。
環境セットアップに便利!

shutdown

shutdown -s -t [秒数]

「お疲れ様でした。お先に失礼します。」って言う直前によく打つコマンド
指定した秒数が経過すると、自動でシャットダウンされます。


あとは、気が向いたらまた続きを書きます。
コントローラや他のことについて。。。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelでネストが深いバリデーションエラーメッセージを返す

今回はLaravelのバリデーションのエラーメッセージの話です。
Laravel :seven:で確認していますが:six:でも同じだと思います。

https://laravel.com/docs/7.x/validation
https://readouble.com/laravel/7.x/ja/validation.html

通常のバリデーション

Laravelでは、普通に作ると以下のような構造で$errorsにエラーメッセージが保存され、それをビューで使用します。

// 例えば、

$request->validate([
    'title' => 'required|string|max:100',
    'body'  => 'required|string|max:255',
]);

// というバリデーションだと、$errorsには、

$errors = [
    'title' => ['The title field is required.'],
    'body' => ['The body field is required.']
]

// それぞれのフィールドに対してメッセージの配列が作られます。

通常は、ビューでこれをそのまま使えば問題ありません。
上記は$request->validate()を書くだけで、エラーの場合は自動でエラーメッセージを含むレスポンスを作成してくれますが(フォームリクエストも然り)、何かしら複雑な事情があって手動でレスポンスを作りたい場合も、ありますよね:wink:
その場合は、

use Illuminate\Validation\ValidationException;
use Illuminate\Support\Facades\Validator;

...

$validator = Validator::make([], []);
$validator->errors()->add('title', 'タイトルのエラーです。');
throw new ValidationException($validator);

// もしくは

throw ValidationException::withMessages(['title' => 'タイトルのエラーです。']);

...

こうやって$validator->errors()->add($key, $message)することで、自由にメッセージを追加することができます。

ネストが深いバリデーションエラーメッセージ

ここからが本題です:bangbang:
上で見たように、エラーメッセージは基本的に2次元配列です。
しかし、エラーメッセージの構造をもっと階層的にしたい場合もあります。

// 例えば、

[
    'この' => [
        'エラーメッセージは' => [
            'とても' => [
                '深いです' => [
                    'とてもとても深いエラーメッセージ'
                ]
            ]
        ]
    ]
]

これを上記のadd()またはwithMessages()でやってもうまくいきません。
ダンプして確認してみます。

$errors = [
    'この' => [
        'エラーメッセージは' => [
            'とても' => [
                '深いです' => [
                    'とてもとても深いエラーメッセージ'
                ]
            ]
        ]
    ]
];
$validator = Validator::make([], []);
$validator->errors()->add('この', $errors['この']);
dd($validator->errors());

/**
Illuminate\Support\MessageBag {#263 ▼
  #messages: array:1 [▼
    "この" => array:1 [▼
      0 => array:1 [▼
        "エラーメッセージは" => array:1 [▼
          "とても" => array:1 [▼
            "深いです" => array:1 [▼
              0 => "とてもとても深いエラーメッセージ"
            ]
          ]
        ]
      ]
    ]
  ]
  #format: ":message"
}
*/

何故か、「この」と「エラーメッセージは」の間に配列が挟まっていますね:thinking:
withMessages()でも同様になります。
どういうことでしょうか。
$validator->errors()->add()add()MessageBagadd()です。
それを調べてみます。

vendor/laravel/framework/src/Illuminate/Support/MessageBag.php
    /**
     * Add a message to the message bag.
     *
     * @param  string  $key
     * @param  string  $message
     * @return $this
     */
    public function add($key, $message)
    {
        if ($this->isUnique($key, $message)) {
            $this->messages[$key][] = $message;
        }

        return $this;
    }

$this->messages[$key] = $message;ではなくて、$keyで配列を作って、その中に$messageを入れています。
だから配列が挟まっていたのです。
ValidationException::withMessages()では、

vendor/laravel/framework/src/Illuminate/Validation/ValidationException.php
    /**
     * Create a new validation exception from a plain array of messages.
     *
     * @param  array  $messages
     * @return static
     */
    public static function withMessages(array $messages)
    {
        return new static(tap(ValidatorFacade::make([], []), function ($validator) use ($messages) {
            foreach ($messages as $key => $value) {
                foreach (Arr::wrap($value) as $message) {
                    $validator->errors()->add($key, $message);
                }
            }
        }));
    }

こちらでも、MessageBagadd()を使っています。
従って同様に配列が差し込まれます。
どうしたらいいのでしょうか。

解決策はmerge()

merge()を使いましょう。
これなら余計な配列は作られません。

$errors = [
    'この' => [
        'エラーメッセージは' => [
            'とても' => [
                '深いです' => [
                    'とてもとても深いエラーメッセージ'
                ]
            ]
        ]
    ]
];
$validator = Validator::make([], []);
$validator->errors()->merge($errors);
dd($validator->errors());
throw new ValidationException($validator);

/**
Illuminate\Support\MessageBag {#263 ▼
  #messages: array:1 [▼
    "この" => array:1 [▼
      "エラーメッセージは" => array:1 [▼
        "とても" => array:1 [▼
          "深いです" => array:1 [▼
            0 => "とてもとても深いエラーメッセージ"
          ]
        ]
      ]
    ]
  ]
  #format: ":message"
}
*/

merge()を使うことでエラーメッセージの構造を崩すことなく、深い階層構造を持つエラーメッセージを返すことができました:beer:

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LaravelのQueryBuilderのfirst()で結果が無かった時の処理と空判定

はじめに: LaravelのQuerybuilderのfirst()がどんな動きをしているか確認

書きの記事を参考にしてLaravelのQuerybuilderのfirst()がどんな動きをしているか確認します。
QueryBuilderのfirst()の動きを調べてみた

結果が無い場合の返り値

結果が無かったら
5.1ではnull
5.3から collect([])が返ってくるようです

バージョンによって少し異なるようですがLaraevl6.4でlogger()で吐かせたところ
Objectの{}が返却されていました。

isEmpty()とかempty()で判定したい

colectionでもarrayでも無いのでできません。
さえ、どうしましょう。。。

if (!$res->count()) {
  // coentが0の場合の処理
}

今回はこんな感じにしました。
時間が無いので検証などできませんでしたが、今度時間がある時にもう少し理解を深めたいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part4

前回のパート

前回は、全ページで使用するヘッダーの作成と会員登録の処理の実装を行いました
前回のパートはこちら→Vue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part3
今回は、前回実装し登録をした内容を利用してログイン機能の実装をしていこうと思います

ログインフォームの実装(Vue側)

まずはログインフォームのview側の実装をしていきます
client/componentsLogin.vueを作成して下記を追加してください

Login.vue
<template>
    <div>
        <Header />
        <div class="main-container">
            <v-form v-model="valid">
                <v-text-field v-model="form.email" :rules="emailRules" label="E-mail" required></v-text-field>
                <v-text-field v-model="form.password" :rules="passwordRules" label="Password" required type="password"></v-text-field>
                <v-btn small>submit</v-btn>
            </v-form>
        </div>
    </div>
</template>

<script>
    import Header from './Header';

    export default {
        components: {
            Header
        },
        metaInfo: {
            title: 'ログイン',
            htmlAttrs: {
                lang: 'ja'
            }
        },
        created () {
            const user = this.$store.getters['auth/user'];
            if (user !== null) {
                this.$router.push('/todo');
            }
        },
        data () {
            return {
                form: {
                    email: '',
                    password: '',
                },
                emailRules: [
                    v => !!v || 'E-mailを入力してください',
                    v => /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/.test(v) || 'E-mailを正しく入力してください'
                ],
                passwordRules: [
                    v => !!v || 'パスワードを入力してください'
                ]
            }
        }
    }
</script>

<style>
    .main-container {
        width: 500px;
        margin: auto;
    }
</style>

前回のRegister.vueと内容はほとんど同じなので
特に説明は必要ないかと思いますので、割愛します
説明が必要な方は、前回のパートに戻って確認してみてください

ではここにmethodを追加していきます

Register.vue
<template>
    <div>
        <Header />
        <div class="main-container">
            <v-form v-model="valid">
                <v-text-field v-model="form.email" :rules="emailRules" label="E-mail" required></v-text-field>
                <v-text-field v-model="form.password" :rules="passwordRules" label="Password" required type="password"></v-text-field>
                <v-btn small @click="login">submit</v-btn>    //追加
            </v-form>
        </div>
    </div>
</template>

<script>
    import Header from './Header';

    export default {
        components: {
            Header
        },
        metaInfo: {
            title: 'ログイン',
            htmlAttrs: {
                lang: 'ja'
            }
        },
        created () {
            const user = this.$store.getters['auth/user'];
            if (user !== null) {
                this.$router.push('/todo');
            }
        },
        data () {
            return {
                form: {
                    email: '',
                    password: '',
                },
                emailRules: [
                    v => !!v || 'E-mailを入力してください',
                    v => /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/.test(v) || 'E-mailを正しく入力してください'
                ],
                passwordRules: [
                    v => !!v || 'パスワードを入力してください'
                ]
            }
        },
        methods: {    //追加
            async login() {
                await this.$store.dispatch('auth/login', this.form);
                this.$router.push('/todo');
            }
        }
    }
</script>

<style>
    .main-container {
        width: 500px;
        margin: auto;
    }
</style>

ログイン時のmethodを追加しました
内容は、auth.jsloginアクションにフォームに入力された内容を渡しdipatchしています
その処理が終われば、/todoのTodoページに遷移する様に設定してます

では次に、auth.jsloginアクションを定義していきます
'auth.js`に下記を追加してください

auth.js
import axios from 'axios';

const state = {
    user: null
}

const getters = {
    user: state => state.user
}

const mutations = {
    setUser (state, user) {
        state.user = user;
    }
}

const actions = {
    async register({ commit }, data) {
        const response = await axios.post('/api/register', data)
        commit('setUser', response.data);
    },

    async searchUser({ commit }) {
        const response = await axios.get('/api/user');
        const user = response.data ? response.data : null;
        commit('setUser', user);
    },

    async login({ commit }, data) {    //追加
        const response = await axios.post('/api/login', data);
        commit('setUser', response.data);
    }
}

export default {
    namespaced: true,
    state,
    getters,
    mutations,
    actions
}

今回追加したアクションはLaravelで定義した、/loginのルートに対して入力されたデータをPOSTし
その、情報を変数responseに格納しています
そして、その値をsetUsermutationに対してcommitしstateのuserの値をセットしています
registerの時と同じ流れになります

次に、Laravel側の実装をしていきます

Laravel側の実装

Vue側でのauth.jsでPOSTしていた/loginをルートに定義していきます
api.phpに下記を追加してください

api.php
Route::post('/register', 'Auth\RegisterController@register')->name('register');
Route::post('/login', 'Auth\LoginController@login')->name('login');    //追加
Route::get('/user', function(){
    return Auth::user();
});

これでauth.jsloginアクションからaxiosでリクエストを投げられる様になりました

そして、part3で下記の記述をLoginController.phpに追加してますのでこれでLaravel側での実装は完了です

LoginController.php
    protected function authenticated(Request $request, $user)    //追加
    {
        return $user;
    }

上記までの記述で一通りログイン機能の実装が終わりました
ただ遷移する画面の/todoをまだ用意していないので正常に画面が遷移することができないので
ここで、Todo.vueコンポーネントを追加しておきましょう
client/componentsTodo.vueファイルを新規で作成してください
中身はこの様にしておいてください

Todo.vue
<template></template>

<script></script>

<style></style>

次にrouter.jsにコンポーネントを登録しておきます
router.jsに下記を追加してください

router.js
import Vue from 'vue';
import Router from 'vue-router';
import Login from '../components/Login';
import Register from '../components/Register';
import Todo from '../components/Todo';    //追加

Vue.use(Router);

export default new Router({
    mode: 'history',
    routes: [
        {
            path: '/login',
            component: Login
        },
        {
            path: '/signup',
            component: Register
        },
        {
            path: '/todo',    //追加
            component: Todo
        }
    ]
})

ここまでできたらログインを試してみてください
正常であれば、ログイン後/todoに遷移し真っ白な画面が表示されるかと思います

終わりに

今回はログイン機能の実装を行いました
それに加えて、/todoページの用意も行いました

次回は、ログアウト機能を実装したいと思います
次回のログアウト機能が終われば認証周りの実装は終わりですので、Todoを作っていく実装に入っていきます

次回のパートはこちら→Vue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part5

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part3

前回のパート

前回は、Authの導入とマイグレーションファイルを作成し
今回のTodoアプリで使用するテーブルの作成まで行いました
前回のパートはこちら→Vue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part2
今回は、全てのページで使うヘッダーの作成と認証周りの実装をやっていきます

ヘッダーコンポーネントの作成

全てのページで共通で使うヘッダーを作成していきます
Vuetifyで実装しますので、Vuetifyを使用しない方は自作での実装をしてください
まず、client/componentsHeader.vueを作成し下記を追加してください

Header.vue
<template>
    <v-card color="grey lighten-4" flat height="200px" tile>
        <v-toolbar dense>
            <v-app-bar-nav-icon></v-app-bar-nav-icon>

            <v-toolbar-title>Todo-List</v-toolbar-title>

            <v-spacer></v-spacer>

            <router-link to="/signup">
                <v-btn icon>
                    <v-icon>mdi-account</v-icon>
                </v-btn>
            </router-link>

            <router-link to="/todo">
                <v-btn icon>
                    <v-icon>mdi-align-horizontal-left</v-icon>
                </v-btn>
            </router-link>

            <router-link to="/login">
                <v-btn icon>
                    <v-icon>mdi-login</v-icon>
                </v-btn>
            </router-link>

            <v-btn icon>
                <v-icon>mdi-logout</v-icon>
            </v-btn>
        </v-toolbar>
    </v-card>
</template>

templateのみのシンプルな物です
<v-btn><router-link>で囲み、会員登録やログインやログアウト、Todoページのリンクを設定しています
全てVuetifyになります
詳しく知りたい方はこちらを参照ください→こちら
これでヘッダーの用意は終わりです

会員登録の実装

まずはVue側でフォームの実装をしていきます
client/componentsRegister.vueを作成してください
Register.vueにを下記の様に下記を追加してください

Register.vue
<template>
    <div>
        <Header />
        <div class="main-container">
            <v-form v-model="valid">    //・・・①
                <v-text-field v-model="form.name" :rules="nameRules" label="Name" required></v-text-field>    //・・・②
                <v-text-field v-model="form.email" :rules="emailRules" label="E-mail" required></v-text-field>
                <v-text-field v-model="form.password" :rules="passwordRules" label="Password" required type="password"></v-text-field>
                <v-text-field v-model="form.password_confirmation" :rules="repasswordRules" label="Confirm Password" required type="password"></v-text-field>
                <v-btn small>submit</v-btn>
            </v-form>
        </div>
    </div>
</template>

<script>
    import Header from './Header';

    export default {
        components: {
            Header
        },
        metaInfo: {    //・・・③
            title: '会員登録',
            htmlAttrs: {
                lang: 'ja'
            }
        },
        data () {
            return {
                form : {
                    name: '',
                    email: '',
                    password: '',
                    password_confirmation: '',
                },
                nameRules: [
                    v => !!v || '名前を入力してください',    //・・・④
                ],
                emailRules: [
                    v => !!v || 'E-mailを入力してください',
                    v => /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/.test(v) || 'E-mailを正しく入力してください'    //・・・⑤
                ],
                passwordRules: [
                    v => !!v || 'パスワードを入力してください',
                    v => v.length >= 8 || 'パスワードは8文字以上で入力してください'    //・・・⑥
                ],
                repasswordRules: [
                    v => !!v || 'パスワードの再入力を行ってください',
                    v => v == this.form.password || 'パスワードが一致しません'    //・・・⑦
                ]
            }
        }
    }
</script>

<style>
    .main-container {
        width: 500px;
        margin: auto;
    }
</style>

①ここは全てVuetifyになります
ここでは簡単な説明はしますが、もっと詳しく知りたい方は、こちらをご覧ください→こちら

②Vuetifyのフォームになります
:rulesこの部分で下の④⑤⑥⑦のルールとバインディングしており、フロント側でのバリデーションを行っています
requireは入力必須を意味します
labelでフォームのラベル名に設定できます
②で設定しているものはどれも同じ要領です
次に設定しているバリデーションのルールについて軽く触れておきます
④入力の値が存在しているかどうか(未入力でないか)をチェックしています
⑤正規表現でEmailの形式かどうかをチェックしています
⑥パスワードが8文字以上で入力されているかをチェックしています
⑦passwordの入力値を一致しているかをチェックしています

③part1でインストールしたVue Metaです
ここでは、titleを【会員登録】としてlangをjaに設定しています
Vue Metaをこの様な設定を行うために使用します

これで、Vuetifyを使って簡単にフォームを用意できバリデーションまで行えました
次に会員登録機能の実装を行っていきます

会員登録機能の実装(Laravel側)

まずは、routes/api.phpを開きルートを定義していきます
/registerは会員登録用のルート
/userはログインをしているのかを常時チェックするために用意したルートになります(この後のVue側の実装で再度説明します)

api.php
<?php

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;

Route::post('/register', 'Auth\RegisterController@register')->name('register');    //追加
Route::get('/user', function(){    //追加
    return Auth::user();
});

次にapp/Http/Controllers/Auth/RegisterCOntroller.phpを開いて下記を追加します

RegisterController.php
    protected function registered(Request $request, $user)    //追加
    {
        return $user;
    }

registerd()メソッドをオーバーライドすることで
登録処理をした際にUser情報を返却できる様に設定できます
この値を使って今後認証を行うので、上記を追加しました

このついでに
ログイン時にとログアウト時にも必要になる処理を先にやっておきます
app/Http/Controllers/Auth/LoginController.phpを開いて下記を追加してください

LoginController.php
    protected function authenticated(Request $request, $user)    //追加
    {
        return $user;
    }

こちらの追加の内容も、registerd()メソッドのオーバーライドをした際と同じ理由になり
ログイン時にUserの情報を返却する様に設定しています

次にLoginController.phpでさらにloggedOut()メソッドを追加してください

LoginController.php
    protected function loggedOut(Request $request)
    {
        $request->session()->regenerate();
        return response('', 200);
    }

こちらは、ログアウト時に呼ばれるメソッドになり
ログアウト時にセッションを再生成を行い、レスポンスを返す様にしてあります

上記3つの追加点については、本記事作成でも参考にさせて頂いたこちらの記事を参照してください→こちら

以上で会員登録に必要なLaravel側の処理の実装は終わりです(ログイン時とログアウト時の設定も行いました)

会員登録処理(Vue側)

まずRegister.vueに下記を追加してください

Register.vue
<template>
    <div>
        <Header />
        <div class="main-container">
            <v-form v-model="valid">
                <v-text-field v-model="form.name" :rules="nameRules" :counter="10" label="Name" required></v-text-field>
                <v-text-field v-model="form.email" :rules="emailRules" label="E-mail" required></v-text-field>
                <v-text-field v-model="form.password" :rules="passwordRules" label="Password" required type="password"></v-text-field>
                <v-text-field v-model="form.password_confirmation" :rules="repasswordRules" label="Confirm Password" required type="password"></v-text-field>
                <v-btn small @click="register">submit</v-btn>    //追加
            </v-form>
        </div>
    </div>
</template>

<script>
    import Header from './Header';

    export default {
        components: {
            Header
        },
        metaInfo: {
            title: '会員登録',
            htmlAttrs: {
                lang: 'ja'
            }
        },
        created () {
            const user = this.$store.getters['auth/user'];
            if (user !== null) {
                this.$router.push('/todo');
            }
        },
        data () {
            return {
                form : {
                    name: '',
                    email: '',
                    password: '',
                    password_confirmation: '',
                },
                nameRules: [
                    v => !!v || '名前を入力してください',
                ],
                emailRules: [
                    v => !!v || 'E-mailを入力してください',
                    v => /^\w+([.-]?\w+)*@\w+([.-]?\w+)*(\.\w{2,3})+$/.test(v) || 'E-mailを正しく入力してください'
                ],
                passwordRules: [
                    v => !!v || 'パスワードを入力してください',
                    v => v.length >= 8 || 'パスワードは8文字以上で入力してください'
                ],
                repasswordRules: [
                    v => !!v || 'パスワードの再入力を行ってください',
                v => v == this.form.password || 'パスワードが一致しません'
                ]
            }
        },
        methods: {    //追加
            async register() {
                await this.$store.dispatch('auth/register', this.form);
                this.$router.push('/todo');
            }
        }
    }
</script>

<style>
    .main-container {
        width: 500px;
        margin: auto;
    }
</style>

①クリックイベントを追加しています
クリックすることでregisterメソッドを呼び出します

②このメソッドによって登録処理を実行します
厳密に言うと、登録処理をするアクションを呼び出しています(auth.jsについてはこの後実装します)
会員登録の処理後に、Todoページに遷移する様にしています

次に、register()でdispatchしているauth.jsregisterの実装をしていきます

auth.js
import axios from 'axios';

const state = {
    user: null    //追加
}

const getters = {
    user: state => state.user    //追加
}

const mutations = {
    setUser (state, user) {    //追加
        state.user = user;
    }
}

const actions = {
    async register({ commit }, data) {    //追加
        const response = await axios.post('/api/register', data)
        commit('setUser', response.data);
    },

    async searchUser({ commit }) {    //追加
        const response = await axios.get('/api/user');
        const user = response.data ? response.data : null
        commit('setUser', user);
    },

}

export default {
    namespaced: true,
    state,
    getters,
    mutations,
    actions
}
main.js
import Vue from 'vue';
import App from './App.vue';
import router from './router/router';
import store from './store/store';
import vuetify from './plugins/vuetify';
import axios from 'axios';
import VueMeta from 'vue-meta';

Vue.config.productionTip = false

Vue.use(axios);
Vue.use(VueMeta, { refreshOnceOnNavigation: true });

const createApp = async () => {    //追加
    await store.dispatch('auth/searchUser');

    new Vue({
        router,
        vuetify,
        store,
        render: h => h(App)
    }).$mount('#app')
}

createApp();    //追加

一気にauth.jsの追加とmain.jsの一部追加を行いました
一つづ説明していきます

auth.js
const state = {
    user: null  
}

stateでuserの情報を管理します
このアプリケーション内では、全てこのuserの値の有無を見てログイン認証やユーザー情報の所得を行います

auth.js
const getters = {
    user: state => state.user
}

stateのuserの情報を所得するgetterを定義しています

auth.js
const mutations = {
    setUser (state, user) {
        state.user = user;
    }
}

受け取ったデータをstateのuserに値をセットするミューテーションを定義しています

auth.js
    async register({ commit }, data) {    //追加
        const response = await axios.post('/api/register', data)
        commit('setUser', response.data);
    }

Laravel側のapi.phpで定義したregisterに対してpostしています
第二引数のdataはフォームに入力された値がv-modelでバインドされたdataの中身が渡されています
ここでの返り値として、先ほどオーバーライドしたregistered()メソッドでの$userの値がresponseにセットされます
その値をsetUserに対してcommitしてstateのuserに値を設定しています

auth.js
    async searchtUser({ commit }) {    //追加
        const response = await axios.get('/api/user');
        const user = response.data ? response.data : null;
        commit('setUser', user);
    }

Laravel側の実装の際にapi.phpにて定義した/userのルートに対してアクセスをしています
/userのルートはAuth::user()として認証しているユーザーの情報を返却する様に作りました
Auth::user()は認証していればユーザー情報が、認証されていなければnullが返ってきます
それを利用して、上記アクションでは/api/userに対してリクエストをしてその戻り値(認証されていればユーザー情報、認証されていなければnull)を
変数responseに入れています
const user = response.data ? response.data : null;で認証されていてresponseに値が入っていればその値を
認証されておらずnullが入っていればnullを変数userに入れています
そのuserをsetUserでcommitしてstateのuserに値を入れています
なのでstateのuserはログインしていればユーザー情報、ログインしていなければnullが常に入っている様にしています
あとはこれを常時実行するためにどうしているかと言うと、先ほど追加したmain.jsの追加部分になります

main.js
const createApp = async () => {
    await store.dispatch('auth/searchUser');

    new Vue({
        router,
        vuetify,
        store,
        render: h => h(App)
    }).$mount('#app')
}

createApp();

ここでVueインスタンスが作られる際に毎回、createApp()を実行することにより
auth.jssearchUserアクションを実行しています
これにより、auth.jssearchUserが毎回実行されることで
Laravelで定義した/userにリクエストを投げ現在の認証情報を所得しauth.jssetUserがcommitされ
stateのuserに値がセットされると言うことになります

Register.vue
created () {
    const user = this.$store.getters['auth/user'];
    if (user !== null) {
        this.$router.push('/todo');
    }
}

この部分でそのページにアクセスする度にcreated()が実行され
auth.jsのgettersを利用してstateの現在のuser情報を所得し
ログインしていれば/todoのページへ遷移する様に設定しています(ログインしていない場合はログインページが表示される)

ここまでの実装で
会員登録が行える様になりますので、フォームから入力して登録を実行してみてください
正常であれば、入力内容がDBに保存され登録処理ができます

終わりに

今回はヘッダーのコンポーネントの作成と、会員登録機能の実装を一通り行いました
次回は、今回登録した内容を利用してログインができる様に、ログイン機能の実装をやっていきます

次のパートはこちら→Vue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part4

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part2

前回のパート

前回はVue CLIとLaravelの環境構築を含め、VuexやVue RouterのセットアップとVue MetaとVuetifyの
インストールを行いました
前回の記事はこちら→Vue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part1
今回は、本記事で作成するTodoリストに使うテーブル作成から行います

Authの導入

まずはターミナルから$ php artisan serve$ npm run serveを実行して
LaravelとVue CLIを動かしておいてください

また事前に、.envファイルのDBの設定とDBの作成を行ってください
Laravelの認証を使うためにAuthを導入していきます
コマンドにて下記を実行してください
$ composer require laravel/ui
$ php artisan ui vue --auth
$ php artisan migrate
$ npm install
$ npm run dev
上記を順に実行することで、LaravelでAuthが導入できLaravel側でのデフォルトの認証画面にアクセスできる状態になります(今回はview側はVueを使用するため使いません)

マイグレーションの作成

まず今回使うテーブルは2種類になります
ユーザーの情報を扱う、UsersテーブルとTodoの情報を管理するTodosテーブルになります
Usersテーブルは上記のAuthの導入をした際に、作成されているUsersテーブルをそのまま使うので
ここで作るテーブルはTodosテーブルのみになります
早速Todosテーブルのマイグレーションを作成していきます
コマンドにて下記を実行
$ php artisan make:migration create_todos_table --create=todos

作成されたファイルdatabase/migrations/XXXX_XX_XX_XXXXXXX_create_todos_table.phpを開き下記と同じ様に書き換えてください

XXXX_XX_XX_XXXXXXXX_create_todos_table.php
<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class TodosCreateTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('todos', function (Blueprint $table) {
            $table->id();
            $table->integer('user_id');
            $table->text('text');    
            $table->softDeletes();
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('todos');
    }
}

上記が完了したら、マイグレーションを実行しましょう
$ php artisan migrate
実行したら作成したマイグレーションのテーブルが作成できているか、確認してください

以上で今回使用する、テーブルの準備が整いました

終わりに

今回は、Authの導入とマイグレーションファイルの作成を行いました
次回から会員登録やログインなどの認証まわりの実装に入っていきます

次のパートはこちら→Vue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part3

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Vue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part1

はじめに

本記事は、Vue CLIとLaravelに加えてVuetifyを使ってTodoリストを作るチュートリアルです
Vuetifyは各自採用するかは決めていただければと思います。
本記事にはVuetifyの記載が多く登場しますが、Vuetifyを使わずにこのチュートリアルを進められる方はVuetifyの部分は無視してもらい
自身でマークアップを行ってください。
また、本チュートリアルはこちらの記事を参考にさせて頂き制作しました

このチュートリアルで学べること

  • Vue CLIとLaravelを利用した開発の流れ
  • Vue(SPA)での開発
  • LaravelとVueでの認証周りのあれこれ
  • Vuexの活用方法
  • Vue Routerの活用方法

このチュートリアルの対象者

  • Laravelの基本を学んだ方
  • Vueの基本を学んだ方
  • Vue CLIとLaravelで何か作ってみたい方

バージョン

Vue CLI - 4.0.5
laravel - 7.0.4

Vue CLI × Laravelの環境構築

まずはLaravelでアプリケーションを作成していきます
コマンドにて下記を実行
$ laravel new VueTodo
作ったアプリケーションに移動します
$ cd VueTodo
次にVue CLIをLaravelに作っていきます
$ vue create client
Manualyを選択
スクリーンショット 2020-03-15 16.28.26.png
RouterとVuexを選択
スクリーンショット 2020-03-15 16.28.44.png
historyモードを使用するのでyを押してEnter
スクリーンショット 2020-03-15 16.28.56.png
今回はエラー防止のみのESLintを使用するのでそのままEnter
スクリーンショット 2020-03-15 16.29.06.png
保存時にLintを実行するため、そのままEnter
スクリーンショット 2020-03-15 16.29.12.png
package.jsonで管理するため、In package.jsonを選択してEnter
スクリーンショット 2020-03-15 16.29.22.png
今回の設定を今回は特に保存しないので、nを押してEnter
スクリーンショット 2020-03-15 16.43.52.png
これでLaravelにclientと言う名前でVue CLIが構築されます
ここまでで、LaravelとVue CLIの準備は完了

proxyの設定を行う

Vue CLIとLaravel間ではドメインが違うため、お互いに通信を行うための設定が必要です
今回はproxyを使います
Vue CLIの機能を使いproxyの設定をすることができます。

clientディレクトリにvue.config.jsファイルを作成
下記を追加
これによりVue CLIからの通信は http://127.0.0.1:8000 に変換され同一ドメインでの通信が可能になります
(ここのパスは環境によって変わるため、$ php artisan serveをした際にアクセスできるLaravel側の自身の環境に書き換えてください)

vue.config.js
module.exports = {
    devServer: {
        proxy: {
            '/api': {
                target: 'http://127.0.0.1:8000'
            }
        }
    }
}

RouteServiceProviderの変更

app/Http/Providers/RouteServiceProvidermapApiRoutes()メソッドの変更を行います
デフォルトでは下記のようになっています

RouteServiceProvider.php
    protected function mapApiRoutes()
    {
        Route::prefix('api')
             ->middleware('api')  //・・・①
             ->namespace($this->namespace)
             ->group(base_path('routes/api.php'));
    }

①の部分がデフォルトではapiとなっています
このmiddlewareの設定を下記のように書き換えます

RouteServiceProider.php
    protected function mapApiRoutes()
    {
        Route::prefix('api')
             ->middleware('web')
             ->namespace($this->namespace)
             ->group(base_path('routes/api.php'));
    }

何故ここを書き換えるのかと言うと
apiとwebでは適応されるmiddlewareが違います
apiのmiddlewareはCSFRトークンやセッションなどのmiddlewareが含まれていません
ただ今回の場合は、Laravelのセッションの認証をするためにセッションや、クッキーを必要とするため
webに変更しています
従来のAPIとしてLaravelを利用する場合は、apiのデフォルトのままで大丈夫です

axiosのインストール

コマンドにて下記を実行
$ npm install axios

clietn/src/main.jsにてaxiosをuseする

main.js
import Vue from 'vue';
import App from './App.vue';
import axios from 'axios';

Vue.config.productionTip = false

Vue.use(axios);

new Vue({ 
    render: h => h(App)
}).$mount('#app')


Vue Routeのセットアップ

Vue Routerセットアップを行います
すでにVue CLIの作成時にRouterを導入してるのでインストールは不要です
client/src/routerと言うディレクトリがRouterのファイルになります

client/src/index.jsのファイル名をわかりやすい様にリネームし、router.jsと変更し
router.jsに下記に書き換えてください

router.js
import Vue from 'vue';
import Router from 'vue-router';

Vue.use(Router);

export default new Router({
    mode: 'history',
    routes: [
        //今は空にしておく
    ]
})

router.jsの作成が完了したら、main.jsにて下記を追加

main.js
import Vue from 'vue';
import App from './App.vue';
import router from './router/router';    //追加
import axios from 'axios';

Vue.use(axios);

new Vue({
    router,    //追加
    render: h => h(App)
}).$mount('#app')

これでVue Routerのセットアップは完了です

Vuexのセットアップ

こちらも、Vue CLIの作成時にインストールしているのでインストールは不要です

client/src/storeがVuexのファイルになります
こちらも、わかりやすい様にリネームしてstore.jsとして、新規ファイルでauth.jsを作成してください
作成したらstore.jsを下記に書き換えてください

store.js
import Vue from 'vue';
import Vuex from 'vuex';
import auth from './auth';

Vue.use(Vuex);

export default new Vuex.Store({
    modules: {
        auth
    }
})

auth.jsにも下記を追加

auth.js
import axios from 'axios';    //axiosを今後使うので読み込んでます

const state = {
}

const getters = {
}

const mutations = {
}

const actions = {
}

export default {
    namespaced: true,
    state,
    getters,
    mutations,
    actions
}

main.jsに下記を追加する

main.js
import Vue from 'vue';
import App from './App.vue';
import router from './router/router';
import store from './store/store';    //追加
import axios from 'axios';

Vue.use(axios);

new Vue({
    router,
    store,    //追加
    render: h => h(App)
}).$mount('#app')

以上でVuexのセットアップは終了です

Vue Metaのインストール

Vue Metaを導入することでVueのscript内でheadのlang設定やtitleなどの設定を行えるようになります
今後使っていくので使い方等は追々説明しますので、ここではインストールとセットアップのみ手順にしたがって行ってください

コマンドにて下記を実行
$ npm install vue-meta

main.jsにて下記を追加する

main.js
import Vue from 'vue';
import App from './App.vue';
import router from './router/router';
import store from './store/store';
import axios from 'axios';
import VueMeta from 'vue-meta';    //追加

Vue.use(axios);
Vue.use(VueMeta, { refreshOnceOnNavigation: true });    //追加

new Vue({
    router,
    store,
    render: h => h(App)
}).$mount('#app')

Vuetifyの導入

※Vuetifyを使わない方はここを飛ばしてもらってOKです

$ cd clientに移動して下記コマンドを実行
$ vue add vuetify
コマンドを実行することでclient/pluginsvuetify.jsが作成されていることを確認してください
また、main.jsに自動的に追加されていることも確認してください(追加されていない場合は追加してください)

main.js
import Vue from 'vue';
import App from './App.vue';
import router from './router/router';
import store from './store/store';
import vuetify from './plugins/vuetify';    //追加されている
import axios from 'axios';
import VueMeta from 'vue-meta';

Vue.use(axios);
Vue.use(VueMeta, { refreshOnceOnNavigation: true });    //追加

new Vue({
    router,
    vuetify,    //追加されている
    store,
    render: h => h(App)
}).$mount('#app')

終わりに

以上でVue CLIとLaravelで今回必要な物のインストールやセットアップなどの環境の構築が終わりました
次回は、今回のTodoリストで使うUserのテーブルとTodoを管理する2つのテーブルのマイグレーションの作成から始めようと思います

次のパートはこちら→Vue CLI × Laravel × VuetifyでCSSいらずの爆速開発でTodoリストを作る part2

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

laravel-dompdf の使い方

1) プロジェクトの作成

laravel new pdf01

2) ライブラリーのインストール

cd pdf01
composer require barryvdh/laravel-dompdf
php artisan vendor:publish --provider="Barryvdh\DomPDF\ServiceProvider"

3) コントローラーの作成

php artisan make:controller HomeController
app/Http/Controllers/HomeController.php
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class HomeController extends Controller
{
    //
public function index() {
        $pdf = \PDF::loadView('generate_pdf');
        return $pdf->download('test01.pdf');
        }
}

4) View の作成

resources/views/generate_pdf.blade.php
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>My First Page</title>
</head>
<body>
<h1>Hello World!!</h1>
<blockquote>
        Good Afternoon<p />
</blockquote>
Mar/17/2020 PM 15:48<p />
</body>
</html>

5) routes の作成

routes/web.php
<?php

use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    return view('welcome');
});

Route::get('pdf', 'HomeController@index')->name('generate_pdf.index');

6) サーバーの起動

php artisan serve --host 0.0.0.0

7) クライアントからアクセス

http://hostname:8000/pdf/

dompdf_mar17.png

test01.pdf という PDF がダウンロードされます。
dompdf_mar1702.png

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravel6.x バリデーション 他テーブル特定カラムに存在しなければいけない

他テーブルの特定のカラムに入力値が存在するしなければいけない。。
そんなバリデーションを書くには、、
そう、existです。

exists:テーブル,カラム
参考・引用元

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Laravelでjs読み込む時に「$ is not defined javascript」エラー

めちゃめちゃ基本的なところでつまづいたので備忘録。

laravelを使ってjs読み込みたいのに何故か最初のおまじない部分でずっとコンソールエラー。
$ is not defined javascript

hoge.js
$(function(){ //←処理入る前のここで既に怒られる
 //ここに処理
});



なんでえ???と思ってたらjsの呼び出し方間違えてました。
↓こう書いてたのを

hoge.blade.php
 <script src="/js/hoge.js"></script>

こうしないといけなかったみたい。

hoge.blade.php
<script src="{{ asset('/js/hoge.js') }}"></script>

参考にさせていただいた記事
https://qiita.com/sakuraya/items/411dbc2e1e633928340e

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む