+2

Laravel 5.8 + Nuxt.js: Quick start

  • Ở bài viết này ta sẽ không đi sâu vào việc giới thiệu, giải thích Nuxt.js là gì? hay tại sao nên dùng Nuxt.js? Ưu nhược điểm của nó so với những thằng khác là như thế nào?
  • Bài này ta sẽ bắt tay nhanh vào việc thực hiện 1 simple project quản lý users sự dụng Laravel 5.8 làm server-side viết api và Nuxt.js làm client-side render ra view.
  • Tất nhiên cần build một project laravel sau đó thêm Nuxt.js vào để xử lý phía client, ở đây ta có thể hiểu là server laravel và server Nuxt.js là hoàn toàn độc lập với nhau.

I. Tạo project Laravel

  • Ta sử dụng phiên bản laravel 5.8 mới nhất tại thời điểm viết bài.
  • Bước 1: Tạo project laravel bằng 1 trong 2 lệnh sau: laravel new hoặc composer create-project
  • Bước 2: Kết nối database và chạy migrate php artisan migrate
  • Bước 3: Chạy lệnh php artisan make:auth để tạo mẫu signup, login, logout.

II. Tạo API thêm sửa xóa users

  • Đây là phần sử lý CRUD chính bên phía server laravel

  • Tạo controller app/Http/Controllers/UsersController.php với nội dung như sau:

    <?php
    namespace App\Http\Controllers;
    
    use Illuminate\Http\Request;
    use App\User;
    
    class UserController extends Controller
    {
        public function index()
        {
            $users = User::all();
    
            return response()->json($users);
        }
    
        public function show($id)
        {
            $user = User::find($id);
    
            return response()->json($user);
        }
    
        public function update(Request $request, $id)
        {
            $data = $request->only([
                'name',
                'email',
            ]);
    
            try {
                $user = User::find($id);
                $user->update($data);
                $result = [
                    'message' => true,
                ];
            } catch (Exception $e) {
                $result = [
                    'message' => false,
                ];
            }
    
            return response()->json($result);
        }
    
        public function store(Request $request)
        {
            $data = $request->only([
                'name',
                'email',
                'password',
            ]);
    
            $data['password'] = bcrypt($data['password']);
    
            try {
                User::create($data);
                $result = [
                    'message' => true,
                ];
            } catch (Exception $e) {
                $result = [
                    'message' => false,
                ];
            }
    
            return response()->json($result);
        }
    
        public function destroy($id)
        {
            try {
                $user = User::find($id);
                $user->delete();
                $result = [
                    'status' => true,
                ];
            } catch (Extention $e) {
                $result = [
                    'status' => false,
                ];
            }
    
            return response()->json($result);
        }
    }
    
  • Sau đó ta cần sửa file routes/api.php để chỉ định url dẫn đến UsersController.php

    Route::get('users', 'UserController@index');
    Route::get('users/{user}', 'UserController@show');
    Route::put('users/{user}', 'UserController@update');
    Route::post('users', 'UserController@store');
    Route::delete('users/{user}', 'UserController@destroy');
    
  • Đến đây khi ta start server với câu lệnh php artisan serve thì mặc định server được khởi tạo ở cổng 8000 với url http://localhost:8000

  • Ví dụ api lấy ra danh sách users sẽ được gọi như sau:

  • OK, phần API đến đây là ngon lành, với api get ta có thể test như trên, còn với các api create, update, delete thì ta có thể dùng Postman.

  • Lưu ý: đây là project demo đơn giản để ta hiểu sự kết hợp giữa laravel và Nuxt.js nên tạm thời ta không để authenticate, cũng như validate cho phần API này

III. Bắt đầu với Nuxt.js

  • Nuxt.js được phát triển dựa trên trên Vue.js và Node.js nên nếu bạn đã có kiến thức sẵn về Vue.js và Node.js thì thật là tuyệt vời, tiếp cận Nuxt.js sẽ rất dễ dàng với bạn. Nhưng nếu bạn mới chỉ tiếp cận thì cũng không sao, ta sẽ bắt đầu từ trang chủ Nuxt.js từng bước một:
  • Cài đặt Nuxt.js framework
    $ sudo npx create-nuxt-app <project-name>
    or
    $ sudo yarn create nuxt-app <project-name>
    
  • Lưu ý bạn phải cài đặt npx trước, từ phiên bản npm >= 5.2.0 thì mặc định npx đã được cài đặt sẵn rồi.
  • Sau khi chạy lệnh cài đặt trên, hệ thống sẽ hỏi ta 1 vài thông tin cơ bản như, project name, description, server,.. nếu muốn thay đổi thì chỉ việc nhập vào, còn không thì cứ enter nhé:
    > Generating Nuxt.js project in /home/framgia/projects/vangiang/nuxt-client
    ? Project name nuxt-client
    ? Project description My laudable Nuxt.js project
    ? Use a custom server framework none
    ? Choose features to install (Press <space> to select, <a> to toggle all, <i> to invert selection)
    ? Use a custom UI framework bootstrap
    ? Use a custom test framework none
    ? Choose rendering mode Universal
    ? Author name giangnv-0040
    ? Choose a package manager yarn
    
      To get started:
    
        cd nuxt-client
        yarn run dev
    
      To build & start for production:
    
        cd nuxt-client
        yarn run build
        yarn start
    
  • Sau khi cài đặt xong ta sẽ có cấu trúc thư mục như sau:
    .
    ├── assets
    │   └── README.md
    ├── components
    │   ├── Logo.vue
    │   └── README.md
    ├── layouts
    │   ├── default.vue
    │   └── README.md
    ├── middleware
    │   └── README.md
    ├── nuxt.config.js
    ├── package.json
    ├── pages
    │   ├── index.vue
    │   └── README.md
    ├── plugins
    │   └── README.md
    ├── README.md
    ├── static
    │   ├── favicon.ico
    │   └── README.md
    ├── store
    │   └── README.md
    └── yarn.lock
    
    
  • Để khởi chạy ứng dụng ta chạy lệnh yarn run dev. Và kết quả thu được:
  • Tiếp đến ta sẽ quan tâm đến thư mục pages, đây là thư mục chứa các file view, đồng thời việc đặt tên, đường dẫn cho file trong này cũng sẽ quy định route, ta sẽ giải thích bằng ví dụ bên dưới, ta sẽ tạo cái file view với đuôi .vue bên trong thư mục pages như sau:
    pages/
    ├── index.vue
    ├── README.md
    └── users
        ├── create.vue
        ├── _id
        │   └── edit.vue
        └── index.vue
    
  • Với việc bố trí đường dẫn, và đặt tên như trên ta sẽ có 3 route:
  • Index: http://localhost:3000/users
  • Edit: http://localhost:3000/users/1/edit/
  • Create: http://localhost:3000/users/create
  • Cụ thể ta có thể xem route được render ra tại .nuxt/route.js, thư mục .nuxt này sẽ được tự động sinh ra khi ta start server
    export function createRouter() {
      return new Router({
        mode: 'history',
        base: decodeURI('/'),
        linkActiveClass: 'nuxt-link-active',
        linkExactActiveClass: 'nuxt-link-exact-active',
        scrollBehavior,
    
        routes: [{
          path: "/users",
          component: _7038f74a,
          name: "users"
        }, {
          path: "/users/create",
          component: _82cb8698,
          name: "users-create"
        }, {
          path: "/users/:id/edit",
          component: _55dfcb77,
          name: "users-id-edit"
        }, {
          path: "/",
          component: _8b87105e,
          name: "index"
        }],
    
        fallback: false
      })
    }
    

- List Users:

  • Ta sẽ update file pages/users/index.vue như sau:
    <template>
      <div class="container">
        <table class="table table-striped table-bordered">
          <thead class="thead-dark">
            <tr>
            <th scope="col" width='5%'>#</th>
            <th scope="col">Name</th>
            <th scope="col">Email</th>
            <th scope="col">Action</th>
            </tr>
          </thead>
          <tbody>
            <tr v-for="user in users" :key="user.id">
              <td width="5%">{{ user.id }}</td>
              <td>{{ user.name }}</td>
              <td>{{ user.email }}</td>
              <td>
                <nuxt-link :to="'users/' + user.id + '/edit/'" class="btn btn-primary">Edit</nuxt-link>
                <a href="#" class="btn btn-danger"  @click="del(user.id)">Del</a>
              </td>
            </tr>
          </tbody>
        </table>
        <nuxt-link to="/users/create" class="btn btn-primary">Create User</nuxt-link>
      </div>
    </template>
    
    <script>
    export default {
      async fetch ({ store, params }) {
        await store.dispatch('user/getUsers');
      },
      computed: {
        users () {
          return this.$store.state.user.users
        }
      },
      methods: {
        async del(id) {
          await this.$store.dispatch('user/deleteUser', id);
          this.$store.dispatch('user/getUsers');
        },
      }
    }
    </script>
    
  • File index.vue trên có gọi đến hàm getUser trong file user.js trong thư mục store, cụ thể:
    import {
        callApiFetch,
        callApiAdd,
        callApiEdit,
        callApiDelete,
        callApiShow
    } from '../api/user.js';
    
    const FETCH = 'fetch';
    const SHOW = 'show';
    const EDIT = 'edit'
    
    export const state = () => ({
        users: [],
        user: {},
    })
    
    export const actions = {
        async getUsers({ commit }, data = {}) {
            let response = await callApiFetch(data);
    
            return commit(FETCH, { users: response.data });
        },
        async addUser({ commit }, data = {}) {
            let response = await callApiAdd(data);
    
            return response;
        },
        async getUser({ commit }, data = {}) {
            let response = await callApiShow(data);
    
            return commit(SHOW, { user: response.data });
        },
        async editUser({ commit }, data) {
            let response = await callApiEdit(data.id, data);
    
            return response;
        },
        async deleteUser({ commit }, data) {
            let response = await callApiDelete(data)
    
            return response;
        },
    
    }
    
    export const mutations = {
        [FETCH](state, { users }) {
            return state.users = users;
        },
        [SHOW](state, { user }) {
            return state.user = user;
        },
        [EDIT](state, { user }) {
            return state.user = user;
        },
    }
    
  • Trong file user.js này có import file api/user.js (thư mục api là mình tự tạo ra, mặc định nuxt.js không có):
    import axios from "./config";
    
    export function callApiFetch(params = {}) {
        return axios.get('/users', { params });
    }
    
    export function callApiAdd(params) {
        return axios.post('/users', params)
            .then(response => response)
            .catch(error => error)
    }
    
    export function callApiEdit(id, params) {
        return axios.put(`/users/${id}`, params)
            .then(response => response)
            .catch(error => error);
    
    }
    
    export function callApiDelete(id) {
        return axios.delete(`/users/${id}`)
            .then(response => response)
            .catch(error => error);
    }
    
    export function callApiShow(id) {
        return axios.get(`/users/${id}`)
            .then(response => response)
            .catch(error => error);
    }
    
    • và file config.js
    // api/config.js
    import axios from "axios"
    
    const apiUrl = 'http://localhost:8000/api'
    
    axios.defaults.baseURL = apiUrl;
    
    export default axios;
    
  • Ở đây khi ta truy cập vào trang user list, 1 apt fetch được gửi đi với cú pháp: await store.dispatch('user/getUsers');, lệnh này sẽ gọi đến function getUsers trong file user.js trong thư mục store, sau đó sẽ gán response trả về vào mảng users trong state.
  • Tiếp đến đoạn code sau sẽ thực hiện việc lấy data từ state để trả lại biến users sử dụng trên view
      computed: {
        users () {
          return this.$store.state.user.users
        }
      },
    
  • Phần xóa user thì được xử lý ở đoạn code này:
      methods: {
        async del(id) {
          await this.$store.dispatch('user/deleteUser', id);
          this.$store.dispatch('user/getUsers');
        },
      }
    
  • Khi click vào nút @click="del(user.id)" method del sẽ được gọi đến, sau đó gọi sang method deleteUser trong store/user.js, rồi gọi sang api callApiDelete bên api/user.js. Sau khi xóa xong thì 1 api fetch tương tự như trên lại được gọi lại để cập nhật trạng thái user list this.$store.dispatch('user/getUsers');
  • Phần create user và update user ta cũng có xử lý tương tự như bên dưới:
    // page/users/create.vue
    <template>
        <section class="container">
            <div class="card">
                <div class="card-header">
                    Create User
                </div>
                <div class="card-body">
                    <form @submit="createUser($event)">
                        <div class="form-group">
                            <label for="name">Name</label>
                            <input type="text" class="form-control" id="name" placeholder="Enter Name" v-model="user.name">
                        </div>
                        <div class="form-group">
                            <label for="email">Email address</label>
                            <input type="email" class="form-control" id="email" aria-describedby="emailHelp" placeholder="Enter email" v-model="user.email">
                        </div>
                        <div class="form-group">
                            <label for="password">Password</label>
                            <input type="password" class="form-control" id="password" placeholder="Enter password" v-model="user.password">
                        </div>
                        <button type="submit" class="btn btn-primary">Submit</button>
                        <nuxt-link to="/users" class="btn btn-primary">Cancel</nuxt-link>
                    </form>
                </div>
            </div>
        </section>
    </template>
    
    <script>
    export default {
        data() {
            return {
                user: {email: null, name: null, password: null},
            }
        },
        methods: {
            async createUser(event) {
                event.preventDefault();
                let response = await this.$store.dispatch('user/addUser', this.user);
    
                if (response && response.data && response.data.message) {
                    this.$router.push({name: 'users'});
                } else {
                    alert('Create error');
                }
            },
        }
    }
    </script>
    
        // page/users/_id/edit.vue
        <template>
        <section class="container">
            <div class="card">
                <div class="card-header">
                    Edit User
                </div>
                <div class="card-body">
                    <form @submit="updateUser($event)">
                        <div class="form-group">
                            <label for="name">Name</label>
                            <input type="text" class="form-control" id="name" placeholder="Enter Name" v-model="userData.name">
                        </div>
                        <div class="form-group">
                            <label for="email">Email address</label>
                            <input type="email" class="form-control" id="email" aria-describedby="emailHelp" placeholder="Enter email" v-model="userData.email">
                        </div>
                        <button type="submit" class="btn btn-primary">Submit</button>
                        <nuxt-link to="/users" class="btn btn-primary">Cancel</nuxt-link>
                    </form>
                </div>
            </div>
        </section>
    </template>
    
    <script>
    export default {
        async fetch ({ store, route }) { 
           await store.dispatch('user/getUser', route.params.id);
        },
        data() {
            return {
                userData: {...this.$store.state.user.user},
            }
        },
        methods: {
            async updateUser(event) {
                event.preventDefault();
                let response = await this.$store.dispatch('user/editUser', this.userData);
    
                if (response && response.data && response.data.message) {
                    this.$router.push({name: 'users'});
                } else {
                    alert('Update error');
                }
            },
        }
    }
    </script>
    

Tài liệu tham khảo

https://nuxtjs.org/guide

https://vuejs.org/v2/guide/


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí