0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

DockerをつかったVueとRailsの環境にAPIを実装する

Last updated at Posted at 2020-05-10

この記事はDockerをつかってVueとRailsの開発環境にユーザー認証機能を実装するの続編です。

一連の記事

  1. DockerをつかってVueとRailsの開発環境をつくる
  2. DockerをつかってVueとRailsの開発環境にユーザー認証機能を実装する
  3. DockerをつかったVueとRailsの環境にAPIを実装するこの記事!!

この記事の目指すところ

  • 前回まで作ってきた環境に新しいAPIを追加する
    • 今回はユーザーが出会ったことのある人を登録する機能を作ってみます

サーバー側

テーブルの作成

今回は前回までに作ったuserテーブルのIDに対し外部キーを設定します。
テーブルの構造はこんな感じ
テーブル名:acquaintance

カラム名 オプション 説明
user_id not null, foreign_key UserテーブルのIDを参照する外部キー
id not null ID
name 名前
nickname あだ名

早速コマンドを実行してテーブルを作成しましょう

bash
rails g model acquaintance user:references name:string nickname:string
rails db:migrate

コントローラーの作成

テーブルができたので、コントローラーを作っていきましょう

bash
rails g controller acquaintances

コントローラーが生成されたので、動作を書いていきましょう。
今回書く動作は以下のとおりです。

  • create
    • 知り合い情報を登録するAPI
  • search
    • 知り合いを探すAPI
server/app/controllers/acquaintances_controller.rb
class AcquaintancesController < ApplicationController

  def create
    @acquaintance = Acquaintance.new(user_id: params[:user_id], name: params[:name], nickname: params[:nickname])

    if @acquaintance.save
      render json: @acquaintance
    else
      render json: { errors: @acquaintance.errors.full_messages }, status: 400
    end
  end

  def search
    @acquaintance = Acquaintance.where(user_id: params[:user_id]).where(name: params[:name]).or(Acquaintance.where(user_id: params[:user_id]).where(nickname: params[:nickname]))
    if @acquaintance != []
      render json: @acquaintance
    else
      render json: { errors: ['ユーザーが見つかりません'] }, status: 404
    end
  end
end

ルーティング情報の設定変更

コントローラーで動作をかいたので、これを使えるようにルーティング情報を記入しましょう。

server/config/routers.rb
Rails.application.routes.draw do
  resources :users, only: [ :create ] do
    collection do
      post 'sign_in'
      get 'me'
    end
  end

  post 'login/login'

  get 'acquaintance/search' => 'acquaintances#search'
  resources :acquaintances do
    collection do
      post 'create'
      get 'index'
    end
  end
end

フロント側

検索画面を作る

Vueでログイン後の検索画面を作ります。

front/src/views/search.vue
<template>
  <div class="search">
    <table>
      <tr>
        <td>
          <el-input
            placeholder="名前か、あだ名で検索"
            v-model="searchtext"
            clearable>
          </el-input>
        </td>
        <td>
          <el-button type="primary" icon="el-icon-search" @click="send">検索</el-button>
        </td>
        <td>
          <router-link to="/new_acquaintance">
            <el-button type="warning"><i class="el-icon-plus">登録</i></el-button>
          </router-link>
        </td>
      </tr>
    </table>
    <el-table
       :data="acqdata"
       stripe
       @row-click="user"
       style="width: 100%">
       <el-table-column
         prop="name"
         label="名前"
         width="180">
       </el-table-column>
       <el-table-column
         prop="nickname"
         label="あだ名"
         width="180">
       </el-table-column>
     </el-table>
  </div>
</template>

<script>

export default {
  name: 'search',
  data () {
    return {
      searchtext: '',
      acqdata: {
        name: '',
        nickname: ''
      }
    }
  },
  components: {
  },
  mounted () {
    this.axios.get('http://0.0.0.0:3000/acquaintances/', {
      params: {
        user_id: this.$store.state.userinfo.id
      },
      headers: {
        'Content-Type': 'application/json',
        Authorization: 'Bearer ' + this.$store.state.userinfo.token
      }
    })
      .then((response) => {
        this.acqdata = response.data
      })
      .catch((e) => {
        console.log(e)
        this.$message.error('情報取得に失敗しました!' + e)
      })
  },
  methods: {
    user (val) {
      this.$store.dispatch('doRegistrationAcqUser', val)
      this.$router.push('/acquaintanceInfo')
    },
    send () {
      this.axios.get('http://0.0.0.0:3000/acquaintance/search', {
        params: {
          user_id: this.$store.state.userinfo.id,
          name: this.searchtext,
          nickname: this.searchtext
        },
        headers: {
          'Content-Type': 'application/json',
          Authorization: 'Bearer ' + this.$store.state.userinfo.token
        }
      })
        .then((response) => {
          this.acqdata = response.data
        })
        .catch((e) => {
          console.log(e)
          this.$confirm('新しく登録しますか?', 'ユーザーが見つかりませんでした。', {
            confirmButtonText: 'OK',
            cancelButtonText: 'Cancel',
            type: 'warning'
          }).then(() => {
            this.$router.push('new_acquaintance')
          }).catch(() => {
          })
        })
    }
  }
}
</script>
<style scoped>
  .search{
    width: 70%;
    margin: 3vh auto;
  }
  table{
    width: 100%;
  }
</style>

前回からStateの情報も少し変えているので、その修正点も変更していきます。

front/src/store/index.js
import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    userinfo: ''
  },
  mutations: {
    RegistrationToken (state, payload) {
      state.userinfo = payload.userinfo
    },
  },
  actions: {
    doRegistrationToken ({ commit }, userinfo) {
      commit('RegistrationToken', { userinfo })
    }
  },
  modules: {
  }
})

知り合いの登録画面の作成

知り合いの登録画面を作成します。

front/src/views/new_acquaintance.vue
<template>
  <div class="new_acquaintance">
    <h1>知り合い登録</h1>
    <el-form :model="ruleForm" :rules="rules" ref="ruleForm" label-width="120px" class="demo-ruleForm">
      <el-form-item label="氏名" prop="name">
        <el-input v-model="ruleForm.name"></el-input>
      </el-form-item>
      <el-form-item label="あだ名" prop="nickname">
        <el-input v-model="ruleForm.nickname"></el-input>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="submitForm('ruleForm')">Create</el-button>
        <el-button @click="resetForm('ruleForm')">Reset</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>

export default {
  name: 'new_acquaintance',
  data () {
    return {
      ruleForm: {
        user_id: this.$store.state.userinfo.id,
        name: '',
        nickname: ''
      },
      rules: {
        name: [
          { required: true, message: '名前を入力してください', trigger: 'blur' },
          { min: 1, max: 20, message: '1文字から20文字で入力してください', trigger: 'blur' }
        ]
      }
    }
  },
  components: {
  },
  methods: {
    submitForm (formName) {
      this.$refs[formName].validate((valid) => {
        if (valid) {
          this.axios.post('http://0.0.0.0:3000/acquaintances/', {
            user_id: this.ruleForm.user_id,
            name: this.ruleForm.name,
            nickname: this.ruleForm.nickname
          },
          {
            headers: {
              'Content-Type': 'application/json',
              Authorization: 'Bearer ' + this.$store.state.userinfo.token
            }
          })
            .then((response) => {
              this.$notify({
                title: 'Success',
                message: '登録にに成功しました!',
                type: 'success'
              })
            })
            .catch((e) => {
              this.$message.error('登録に失敗しました!' + e)
            })
        } else {
          console.log('error submit!!')
          return false
        }
      })
    },
    resetForm (formName) {
      this.$refs[formName].resetFields()
    }
  }
}
</script>
<style scoped>
</style>

ルーティング情報の修正

ここまで作ったviewのルーティング情報を追記していきます。
また、ログインしていない場合、Homeにリダイレクトされる処理も書き加えています。

front/src/router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Store from '@/store/index.js'
import Home from '../views/Home.vue'
import Login from '../views/login.vue'
import Signup from '../views/signup.vue'
import Search from '../views/search.vue'
import Newacquaintance from '../views/new_acquaintance.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Home',
    meta: {
      isPublic: true
    },
    component: Home
  },
  {
    path: '/login',
    name: 'login',
    meta: {
      isPublic: true
    },
    component: Login
  },
  {
    path: '/signup',
    name: 'signup',
    meta: {
      isPublic: true
    },
    component: Signup
  },
  {
    path: '/search',
    name: 'search',
    component: Search
  },
  {
    path: '/new_acquaintance',
    name: 'new_acquaintance',
    component: Newacquaintance
  }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})
router.beforeEach((to, from, next) => {
  if (to.matched.some(page => page.meta.isPublic) || Store.state.userinfo.token) {
    next()
  } else {
    next('/')
  }
})
export default router

確認

ログイン後のSearch.vueの画面はこんな感じになると思います。
あだ名か、名前を入力するとどちらかが一致するものが帰ってくる感じです。
FireShot Capture 009 - mybutler - localhost.png
新規追加画面の様子です。
FireShot Capture 010 - mybutler - localhost.png

おわりに

今回はログイン後にAPIをつかいデータの登録と取得の操作をしました。
次回は、これに紐づけたテーブルをもう一つ作り知り合いとの経歴を保存できるようにしていきます。

参考にさせていただいた記事

【Rails5】外部キーの設定メモ

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?