Help us understand the problem. What is going on with this article?

EC2のLaravel6.0環境でVue+Rest APIを連携する AWS/Laravel連載(11)

はじめに

前回の記事でLaravel+Vueを使い、API経由で投稿一覧表示部分を実装しました。

EC2のLaravel6.0環境でVue+Rest APIを連携する AWS/Laravel連載(10)

今回はAPIで新規作成(Create)部分を実装します。

err.png

controllerの実装

app/Http/Controllers/PostController.php
...
    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        return Post::create($request->all());
    }
...

これでリクエストパラメータを元にレコード生成するロジック部分は完成です。
バリデーションはあとで実装します。

jsの実装

resources/js/app.js
const app = new Vue({
  el: '#app',
  data: {
    posts: [],
    title: '',
    content: '',
  },
  methods: {
    fetchPosts: function(){
      axios.get('/api/posts').then((res)=>{
        this.posts = res.data
      })
    },
    onSubmit: function(){
      const params = {
        title: this.title,
        content: this.content,
      };
      axios.post('/api/posts', params).then(res =>{
        this.title = '';
        this.content = '';
        this.fetchPosts();
      })
    }
  },
  created() {
    this.fetchPosts()
  },
});

修正したら、忘れずビルドしましょう。

$ npm run dev

viewの実装

resources/views/home.blade.php
...
                    <form>
                        <div class="form-group">
                            <label for="title">タイトル</label>
                            <input type="text" id="title" v-model="title" class="form-control">
                        </div>
                        <div class="form-group">
                            <label for="content">本文</label>
                            <textarea v-model="content" id="content" class="form-control"></textarea>
                        </div>
                        <input type="button" class="btn btn-primary center" @click="onSubmit()" value="投稿">
                    </form>
...

ボタンをクリックするとVueのonSubmitメソッドが呼び出されます。
するとv-modelで指定していたタイトル、本文の値がpostされます。

ここまで実装すると、

  1. フォームに値を入れる
  2. 送信ボタンを押す
  3. フォームの値がLaravel側のstoreに飛び、create処理が走ってレコード生成される
  4. fetchPostsメソッドが走り、今投稿されたデータも含め一覧再取得
  5. 投稿一覧情報が更新される

となり、レコード生成部分ができあがりました。

input.png

バリデーションの実装

しかしバリデーションが存在しません。
タイトル・本文が空欄でもレコードが生成されてしまいます。

空欄の場合に「タイトルを入力してください」といったエラー文言を出したいと思います。

まずはサーバサイドでバリデーションを実装しましょう。

controller側でバリデーション実装

app/Http/Controllers/PostController.php
...
    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        $validatedData = $request->validate([
            'title' => 'required|max:255',
            'content' => 'required',
        ]);
        return Post::create($request->all());
    }
...

requiredと入れることで必須要素となります。
$request->validateメソッドを通すことで、エラーがあった場合にその場で例外が返され、Post::createが走らなくなります。
また、max:255は最大255文字までの入力を受け付けるバリデーションです。
複数条件は|で繋いで指定します。

js側でバリデーションエラー時の処理を実装

resources/js/app.js
const app = new Vue({
  el: '#app',
  data: {
    posts: [],
    title: '',
    content: '',
    errors: {},
  },
  methods: {
    fetchPosts: function(){
      axios.get('/api/posts').then((res)=>{
        this.posts = res.data
      })
    },
    onSubmit: function(){
      const params = {
        title: this.title,
        content: this.content,
      };
      this.errors = {};
      axios.post('/api/posts', params).then(res =>{
        this.title = '';
        this.content = '';
        this.fetchPosts();
      }).catch(err =>{
        for(var key in err.response.data.errors) {
            this.$set(this.errors, key, err.response.data.errors[key].join('<br>'));
        }
      });
    }
  },
  created() {
    this.fetchPosts()
  },
});

コツは2点。

1点目は、axiosでcatchをしていること。
これにより、エラーが発生した時の処理を記述しています。

2点目は、

this.$set(this.errors, key, err.response.data.errors[key].join('<br>'));

Vueの配列・オブジェクトは要素を代入してもリアクティブに変更検知ができません。
つまり

this.errors[key] = err.response.data.errors[key].join('<br>')

のように書いても、Vue側でthis.errorsの変更を検知できず、view側でリアルタイムに反映されません。
this.$setを使うことで、オブジェクトの変更を検知し反映されるようにしています。

修正したら、忘れずビルドしましょう。

$ npm run dev

view側でバリデーションエラー時の処理を実装

resources/views/home.blade.php
...
                    <form>
                        <div class="form-group">
                            <label for="title">タイトル</label>
                            <input type="text" id="title" v-model="title" class="form-control" v-bind:class="{ 'is-invalid': errors.title }">
                            <p class="alert alert-danger" v-if="errors.title" v-html="errors.title"></p>
                        </div>
                        <div class="form-group">
                            <label for="content">本文</label>
                            <textarea v-model="content" id="content" class="form-control" v-bind:class="{ 'is-invalid': errors.content }"></textarea>
                            <p class="alert alert-danger" v-if="errors.content" v-html="errors.content"></p>
                        </div>
                        <input type="button" class="btn btn-primary center" @click="onSubmit()" value="投稿">
                    </form>
...

コツは2点。
v-bind:classとv-ifです。

1点目はv-bindですが、errors.titleが存在した場合にis-invalidというクラスが振られるようになっています。
axiosで例外が投げられた場合にerrors.titleに値が入ってくることで、タイトルにエラーがある時だけis-invalidが振られてフォームパーツが赤色になります。

2点目はv-ifです。
これも似たような実装ですが、errors.titleに値がある時のみ、pタグ要素が表示されます。
そしてエラー文言がpタグの中身に入ります(v-html="errors.title")。

エラーメッセージを日本語化する

基本的には連載6回目の記事でほぼすべて日本語化されているはずです。
ただし唯一項目名のみ英語のはずです。

err_e.png

下記ファイルにこの設定で解決します。

resources/lang/ja/validation.php
...
    'attributes' => [
...
        'title' => '題名',
        'content' => '本文',
    ],
...

err.png

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away