12/08/2018, 14:53

Laravel and Vuejs CRUD with pagination

Tản mạn Vuejs Ngày nay, có rất nhiều Javascript Framework ra đời kể đến như là React js , Angular js, Vuejs... Nếu các bạn đã từng tiếp cận với React và Angular nó cung cấp API cực kì lớn, rất khó cho người mới bất đầu, đặc biệt nếu chúng không nắm vững kiến thức về javascript thì e rằng sẽ khó ...

Tản mạn Vuejs

Ngày nay, có rất nhiều Javascript Framework ra đời kể đến như là React js , Angular js, Vuejs... Nếu các bạn đã từng tiếp cận với React và Angular nó cung cấp API cực kì lớn, rất khó cho người mới bất đầu, đặc biệt nếu chúng không nắm vững kiến thức về javascript thì e rằng sẽ khó mà tiếp cận đến chúng. Vuejs được nhắc đến khá nhiều trong những năm gần đây. Với tính năng "Virtual DOM" làm tăng performace lên đáng kể và đặc biệt đó là "Components" là kiến thức kì cực quan trọng cần phải nắm bắt đầu tiên khi học đến Vuejs. Đứng về khía cạnh khách quan thì Vuejs sẽ thua thế hơn là Angular và React. Vuejs được xem như là bản mini của Angular có rất nhiều điểm tương đồng với nhau. Và điều mình cực kì thích ở Vuejs đó là API của nó rất clear, dễ dùng, vô cùng ngắn gọn và xúc tích.

Hôm nay, mình sẽ hướng dẫn các bạn làm CRUD sử dụng laravel + vuejs đầy đủ những tính năng cần thiết:

  1. Item Listing (pagination)
  2. Item Create
  3. Item Edit
  4. Item Delete

Les't go, chúng ta hãy bất đầu nào

Step 1: Laravel Installation

Bước đầu tiên, các bạn phải install laravel, ở đây mình dùng phiên bản 5.3

composer create-project --prefer-dist laravel/laravel blog

Step 2: Create items table and model

Bước tiếp theo chúng ta cần tạo migration table 'items', hãy chạy lệnh này trong command line

php artisan make:migration create_items_table

Khi chạy xong dòng này, chúng ta check out đến thư mục database/migrations tìm đến file vừa tạo và thêm thuộc tính cho table items.

use IlluminateDatabaseSchemaBlueprint;
use IlluminateDatabaseMigrationsMigration;

class CreateItemsTable extends Migration
{

    public function up()
    {
        Schema::create('items', function (Blueprint $table) {
            $table->increments('id');
            $table->string('title');
            $table->text('description');
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::drop("items");
    }
}

Bước tiếp theo chúng ta tạo model Item trong thư mục app/Models

namespace AppModels;

use IlluminateDatabaseEloquentModel;

class Item extends Model
{

    public $fillable = [
        'title',
        'description',
    ];

}

Step 3: Add Route and Controller

Thêm route để xử lí các tác vụ của CRUD ở đây mình sử dụng resource route mà laravel đã xây dựng sắn cho chúng ta: index, create, edit and delete route app/Http/routes.php

Route::get('manage-vue', 'VueItemController@manageVue');
Route::resource('vueitems','VueItemController');

Next, chúng ta chạy lệnh artisan để sinh ra controller 1 cách tự động

php artisan make:controller VueItemController --resource

app/Http/Controllers/VueItemController.php

namespace AppHttpControllers;

use IlluminateHttpRequest;
use AppHttpControllersController;
use AppModelsItem;

class VueItemController extends Controller
{

    public function manageVue()
    {
        return view('manage-vue');
    }

    public function index(Request $request)
    {
        $items = Item::latest()->paginate(5);

        $response = [
            'pagination' => [
                'total' => $items->total(),
                'per_page' => $items->perPage(),
                'current_page' => $items->currentPage(),
                'last_page' => $items->lastPage(),
                'from' => $items->firstItem(),
                'to' => $items->lastItem()
            ],
            'data' => $items
        ];

        return response()->json($response);
    }

    public function store(Request $request)
    {
        $this->validate($request, [
            'title' => 'required',
            'description' => 'required',
        ]);

        $create = Item::create($request->all());

        return response()->json($create);
    }

    public function update(Request $request, $id)
    {
        $this->validate($request, [
            'title' => 'required',
            'description' => 'required',
        ]);

        $edit = Item::find($id)->update($request->all());

        return response()->json($edit);
    }

    public function destroy($id)
    {
        Item::find($id)->delete();
        return response()->json(['done']);
    }
}

Step 4: Create Blade File

Bước theo chúng ta tạo file blade để hiển thì item trong việc xử lý CRUD với vuejs Ở đây, mình muốn sử dụng nhanh thao tác để các bạn hiểu rõ hơn cách thử hoạt động của vuejs ở client nên mình sử dụng CDN cho những thư viện mình dụng như jquery, bootstrap js and css, vue.js, vue-resource.min.js và toastr js để thông báo notification. Còn trong phát triển dự án, các bạn nên sử dụng webpack quản lí các module bundler cũng như để biên dịch file .Vue mà laravel đã tích hợp trong project của mình. Nó giúp cho chúng ta dễ dàng làm việc với component của Vuejs hơn. Thật tuyệt vời phải không nào resources/views/manage-vue.blade.php

<!DOCTYPE html>
<html>
<head>
    <title>Laravel Vue JS Item CRUD</title>
    <meta id="token" name="token" value="{{ csrf_token() }}">
    <link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha/css/bootstrap.css">
</head>
<body>

    <div class="container" id="manage-vue">
        <div class="row">
            <div class="col-lg-12 margin-tb">
                <div class="pull-left">
                    <h2>Laravel Vue JS Item CRUD</h2>
                </div>
                <div class="pull-right">
                    <button type="button" class="btn btn-success" data-toggle="modal" data-target="#create-item">
                    Create Item
                    </button>
                </div>
            </div>
        </div>

         
        <table class="table table-bordered">
            <tr>
                <th>Title</th>
                <th>Description</th>
                <th awidth="200px">Action</th>
            </tr>
            <tr v-for="item in items">
                <td>@{{ item.title }}</td>
                <td>@{{ item.description }}</td>
                <td>
                    <button class="btn btn-primary" @click.prevent="editItem(item)">Edit</button>
                    <button class="btn btn-danger" @click.prevent="deleteItem(item)">Delete</button>
                </td>
            </tr>
        </table>

         
        <nav>
            <ul class="pagination">
                <li v-if="pagination.current_page > 1">
                    <a href="#" aria-label="Previous"
                       @click.prevent="changePage(pagination.current_page - 1)">
                        <span aria-hidden="true">«</span>
                    </a>
                </li>
                <li v-for="page in pagesNumber" v-bind:class="[ page == isActived ? 'active' : ']">
                    <a href="#" @click.prevent="changePage(page)">@{{ page }}</a>
                </li>
                <li v-if="pagination.current_page < pagination.last_page">
                    <a href="#" aria-label="Next" @click.prevent="changePage(pagination.current_page + 1)">
                        <span aria-hidden="true">»</span>
                    </a>
                </li>
            </ul>
        </nav>

         
        <div class="modal fade" id="create-item" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
          <div class="modal-dialog" role="document">
            <div class="modal-content">
                <div class="modal-header">
                    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
                    <h4 class="modal-title" id="myModalLabel">Create Item</h4>
                </div>
                <div class="modal-body">
                     
                    <form method="POST" enctype="multipart/form-data" v-on:submit.prevent="createItem">
                        <div class="form-group">
                            <label for="title">Title:</label>
                            <input type="text" name="title" class="form-control" v-model="newItem.title" />
                            <span v-if="formErrors['title']" class="error text-danger">@{{ formErrors['title'] }}</span>
                        </div>

                        <div class="form-group">
                            <label for="title">Description:</label>
                            <textarea name="description" class="form-control" v-model="newItem.description"></textarea>
                            <span v-if="formErrors['description']" class="error text-danger">@{{ formErrors['description'] }}</span>
                        </div>

                        <div class="form-group">
                            <button type="submit" class="btn btn-success">Submit</button>
                        </div>
                    </form>
                </div>
            </div>
          </div>
        </div>

         
        <div class="modal fade" id="edit-item" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
            <div class="modal-dialog" role="document">
                <div class="modal-content">
                    <div class="modal-header">
                        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
                        <h4 class="modal-title" id="myModalLabel">Edit Item</h4>
                    </div>
                    <div class="modal-body">
                         
                        <form method="POST" enctype="multipart/form-data" v-on:submit.prevent="updateItem(fillItem.id)">
                            <div class="form-group">
                                <label for="title">Title:</label>
                                <input type="text" name="title" class="form-control" v-model="fillItem.title" />
                                <span v-if="formErrorsUpdate['title']" class="error text-danger">@{{ formErrorsUpdate['title'] }}</span>
                            </div>

                            <div class="form-group">
                                <label for="title">Description:</label>
                                <textarea name="description" class="form-control" v-model="fillItem.description"></textarea>
                                <span v-if="formErrorsUpdate['description']" class="error text-danger">@{{ formErrorsUpdate['description'] }}</span>
                            </div>

                            <div class="form-group">
                                <button type="submit" class="btn btn-success">Submit</button>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>

    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0-alpha/js/bootstrap.min.js"></script>

    <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/js/toastr.min.js"></script>
        <link href="//cdnjs.cloudflare.com/ajax/libs/toastr.js/latest/css/toastr.min.css" rel="stylesheet">

    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
    <script type="text/javascript" src="https://cdn.jsdelivr.net/vue.resource/0.9.3/vue-resource.min.js"></script>

    <script type="text/javascript" src="/js/item.js"></script>

</body>
</html>

Step 5: Create JS File

Bước tiếp theo chúng ta xây dựng vuejs xử lí tác vụ CRUD khéo léo đến mức nào nha. public/js/item.js

Vue.http.headers.common['X-CSRF-TOKEN'] = $("#token").attr("value");

new Vue({

  el: '#manage-vue',

  data: {
    items: [],
    pagination: {
        total: 0, 
        per_page: 2,
        from: 1, 
        to: 0,
        current_page: 1
      },
    offset: 4,
    formErrors:{},
    formErrorsUpdate:{},
    newItem : {'title':','description':'},
    fillItem : {'title':','description':','id':'}
  },

  computed: {
        isActived: function () {
            return this.pagination.current_page;
        },
        pagesNumber: function () {
            if (!this.pagination.to) {
                return [];
            }
            var from = this.pagination.current_page - this.offset;
            if (from < 1) {
                from = 1;
            }
            var to = from + (this.offset * 2);
            if (to >= this.pagination.last_page) {
                to = this.pagination.last_page;
            }
            var pagesArray = [];
            while (from <= to) {
                pagesArray.push(from);
                from++;
            }
            return pagesArray;
        }
    },

  ready : function(){
  		this.getVueItems(this.pagination.current_page);
  },

  methods : {

        getVueItems: function(page){
          this.$http.get('/vueitems?page='+page).then((response) => {
            this.$set('items', response.data.data.data);
            this.$set('pagination', response.data.pagination);
          });
        },

        createItem: function(){
		  var input = this.newItem;
		  this.$http.post('/vueitems',input).then((response) => {
		    this.changePage(this.pagination.current_page);
			this.newItem = {'title':','description':'};
			$("#create-item").modal('hide');
			toastr.success('Item Created Successfully.', 'Success Alert', {timeOut: 5000});
		  }, (response) => {
			this.formErrors = response.data;
	    });
	},

      deleteItem: function(item){
        this.$http.delete('/vueitems/'+item.id).then((response) => {
            this.changePage(this.pagination.current_page);
            toastr.success('Item Deleted Successfully.', 'Success Alert', {timeOut: 5000});
        });
      },

      editItem: function(item){
          this.fillItem.title = item.title;
          this.fillItem.id = item.id;
          this.fillItem.description = item.description;
          $("#edit-item").modal('show');
      },

      updateItem: function(id){
        var input = this.fillItem;
        this.$http.put('/vueitems/'+id,input).then((response) => {
            this.changePage(this.pagination.current_page);
            this.fillItem = {'title':','description':','id':'};
            $("#edit-item").modal('hide');
            toastr.success('Item Updated Successfully.', 'Success Alert', {timeOut: 5000});
          }, (response) => {
              this.formErrorsUpdate = response.data;
          });
      },

      changePage: function (page) {
          this.pagination.current_page = page;
          this.getVueItems(page);
      }

  }

});

Tổng kết

Qua ví dụ trên chúng ta cũng hiểu một phần nào đó về cách thức hoạt động của vuejs trong project của mình, việc áp dụng kiến thức vuejs vào trong thực tế sẽ có rất nhiều vấn đề. Ở đây mình chia sẽ kiến thức basic, giống như chương trình "hello world" cho những ai đang và mới tìm hiểu của vuejs. Đối với riêng mình vuejs là một framework khá tuyệt vời đầy hứa hẹn. Chúc các bạn thành công. Link demo: http://demo.itsolutionstuff.com/manage-vue Tham khảo https://vuejs.org/v2/guide/ http://itsolutionstuff.com

0