Laravel 5.5 và Reactjs: Xây dựng CRUD (Create, Read, Update, Delete) từ đầu

Trước khi vào bài này bạn cần biết về tính năng Front end preset của Laravel. Laravel 5.5 đem đến một preset mới là Reactjs, bài hướng dẫn này sẽ giúp anh em tiết kiệm thời gian.

1.Cài đặt Laravel 5.5 và cấu hình DB

Chạy lệnh:

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

Sau khi cài đặt xong, chúng ta cần cài đặt Javascript dependencies cho project. Chạy lệnh:

npm install

Bây giờ mình tạo 1 database trong phpMyAdmin hay MySQL work bench (Mac thì cài Sequel pro mà quản lý DB).

Cấu hình thông số DB trong file .env:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=LaravelReactjstuts
DB_USERNAME=root
DB_PASSWORD=mysql

Bước tiếp theo chúng ta cd vào root folder bằng terminal rồi chạy lệnh migrate

php artisan migrate

Lệnh này tạo 2 bảng mặc định là users và password_resets. Xong phần cài Laravel, tiếp đến cài React nhé.

  Laravel, bạn đã viết đúng?

2.React Preset

Bạn có thể sử dụng lệnh preset với tùy chọn là react:

php artisan preset react

Lệnh này sẽ tạo cho bạn một khung sườn cơ bản (scaffold). Bạn có thể compile assets bằng cách sử dụng command

npm install && npm run dev

Tiếp theo chúng ta vào LaravelReactJSTuts  >> resources  >>  assets  >>   js , tại đó có 1 folder và 2 file js. Folder có tên là components, đây là react component với 2 file là app.js và boostrap.js

Đi đến resources  >>  views  >>  welcome.blade.php và xóa code đang có trong file, sau đó chèn đoạn code bên dưới:

<!doctype html>
<html lang="{{ app()->getLocale() }}">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Laravel</title>
        <link href="{{asset('css/app.css')}}" rel="stylesheet" type="text/css">
    </head>
    <body>
        <div id="example"></div>
        <script src="{{asset('js/app.js')}}" ></script>
    </body>
</html>

Mở terminal và chạy lệnh:

npm run dev

Lệnh này nó sẽ compile tất cả assets và để bundled js vào trong public >> js  >>  app.js

Để run project chúng ta chạy lệnh

php artisan serve

Visit http://localhost:8000

Laravel 5.5 và Reactjs: Xây dựng CRUD (Create, Read, Update, Delete) từ đầu

3.Chúng ta cần cài đặt vài dependencies liên quan đến reactjs

Đầu tiên chúng ta cần cài gói react-router cho phần điều hướng.

npm install [email protected]

Theo kinh nghiệm thì mình cài version cũ hơn 1 xíu để ổn định. Mở terminal và chạy lệnh:

npm run watch

Nó sẽ xem những thay đổi trong thư mục assets và và tự động compile lại. Bây giờ mình sẽ chỉnh sửa lại cái ReactJS scaffold theo ý mình bằng cách paste đoạn code bên dưới vào App.js:

// app.js

require('./bootstrap');
import React from 'react';
import { render } from 'react-dom';
import { Router, Route, browserHistory } from 'react-router';

import Example from './components/Example';

render(<Example />, document.getElementById('example'));

Thay đổi tiếp theo là modify Example.js component:

// Example.js

import React, { Component } from 'react';

export default class Example extends Component {
    render() {
        return (
            <div className="container">
                <div className="row">
                    <div className="col-md-8 col-md-offset-2">
                        <div className="panel panel-default">
                            <div className="panel-heading">Example Component</div>

                            <div className="panel-body">
                                I am an example component!
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        );
    }
}

Sau khi save, Laravel Mix sẽ compile lại tất cả các assets và build 1 bundled app.js file. Mở trình duyệt và refresh lại page thì bạn sẽ không thấy sự thay đổi gì cả, nhưng bên trong nó vẫn chạy theo flow quy định của chúng ta.

Bây giờ chúng ta cần tạo một component khác gọi là Master.js bên trong components folder.

/ Master.js

import React, {Component} from 'react';
import { Router, Route, Link } from 'react-router';

class Master extends Component {
  render(){
    return (
      <div className="container">
        <nav className="navbar navbar-default">
          <div className="container-fluid">
            <div className="navbar-header">
              <a className="navbar-brand" href="#">AppDividend</a>
            </div>
            <ul className="nav navbar-nav">
              <li className="active"><a href="#">Home</a></li>
              <li><a href="#">Page 1</a></li>
              <li><a href="#">Page 2</a></li>
              <li><a href="#">Page 3</a></li>
            </ul>
          </div>
      </nav>
          <div>
              {this.props.children}
          </div>
      </div>
    )
  }
}
export default Master;

Tiếp tục lại modify app.js một lần nữa và để component này như là root component.

// app.js

require('./bootstrap');
import React from 'react';
import { render } from 'react-dom';
import { Router, Route, browserHistory } from 'react-router';

import Master from './components/Master';

render(<Master />, document.getElementById('example'));

4.Cấu hình ReactJS routes

Tạo thêm 3 components trong component folder bao gồm:

  1. CreateItem.js
  2. DisplayItem.js
  3. EditItem.js

Rồi ta tạo form trong CreateItem.js để save item data

// CreateItem.js

import React, {Component} from 'react';

class CreateItem extends Component {
    render() {
      return (
      <div>
        <h1>Create An Item</h1>
        <form>
          <div className="row">
            <div className="col-md-6">
              <div className="form-group">
                <label>Item Name:</label>
                <input type="text" className="form-control" />
              </div>
            </div>
            </div>
            <div className="row">
              <div className="col-md-6">
                <div className="form-group">
                  <label>Item Price:</label>
                  <input type="text" className="form-control col-md-6" />
                </div>
              </div>
            </div><br />
            <div className="form-group">
              <button className="btn btn-primary">Add Item</button>
            </div>
        </form>
  </div>
      )
    }
}
export default CreateItem;

Chúng ta cần cấu hình route trong app.js

// app.js

require('./bootstrap');
import React from 'react';
import { render } from 'react-dom';
import { Router, Route, browserHistory } from 'react-router';

import Master from './components/Master';
import CreateItem from './components/CreateItem';

render(
  <Router history={browserHistory}>
      <Route path="/" component={Master} >
        <Route path="/add-item" component={CreateItem} />
      </Route>
    </Router>,
        document.getElementById('example'));

Nếu mọi thứ ổn thì thằng Laravel Mix sẽ recompile và chạy lệnh php artisan serve, mở trình duyệt chạy URL http://localhost:8000/

Thử nhấp vào Create Item xem sao:

Laravel 5.5 và Reactjs: Xây dựng CRUD (Create, Read, Update, Delete) từ đầu

5.Sử dụng axios để làm AJAX Post request

Thêm một số event để lấy input data từ form và gửi đến server bằng AJAX post request.

// CreateItem.js

import React, {Component} from 'react';

class CreateItem extends Component {
  constructor(props){
    super(props);
    this.state = {productName: '', productPrice: ''};

    this.handleChange1 = this.handleChange1.bind(this);
    this.handleChange2 = this.handleChange2.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }
  handleChange1(e){
    this.setState({
      productName: e.target.value
    })
  }
  handleChange2(e){
    this.setState({
      productPrice: e.target.value
    })
  }
  handleSubmit(e){
    e.preventDefault();
    const products = {
      name: this.state.productName,
      price: this.state.productPrice
    }
    let uri = 'http://localhost:8000/items';
    axios.post(uri, products).then((response) => {
      // browserHistory.push('/display-item');
    });
  }

    render() {
      return (
      <div>
        <h1>Create An Item</h1>
        <form onSubmit={this.handleSubmit}>
          <div className="row">
            <div className="col-md-6">
              <div className="form-group">
                <label>Item Name:</label>
                <input type="text" className="form-control" onChange={this.handleChange1}/>
              </div>
            </div>
            </div>
            <div className="row">
              <div className="col-md-6">
                <div className="form-group">
                  <label>Item Price:</label>
                  <input type="text" className="form-control col-md-6" onChange={this.handleChange2}/>
                </div>
              </div>
            </div><br />
            <div className="form-group">
              <button className="btn btn-primary">Add Item</button>
            </div>
        </form>
  </div>
      )
    }
}
export default CreateItem;

6.Backend dùng Laravel (tất nhiên)

Chúng ta sẽ quản lý các hoạt động CRUD (Create, Read, Update, Delete) của item data. Trước mắt cần tạo controller và routes. Chạy lệnh:

php artisan make:model Item -m

Nó tạo ra 2 file là Model và Migration. Mở file migration trong database  >>  migrations  >>  create_items_table và paste đoạn code này

<?php

// create_items_table

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

class CreateItemsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('items', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->integer('price');
            $table->timestamps();
        });
    }

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

Rồi chạy lệnh

php artisan migrate

Lệnh này tạo bảng items trong DB. Ngoài ra, 1 file model được tạo trong folder app tên là Item.php

Cần phải tạo một controller là ItemController

php artisan make:controller ItemController --resource

ItemController chứa đựng tất cả các hàm về quản lý CRUD. Mình thử thêm code vào xem sao:

<?php

// ItemController.php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Item;

class ItemController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        $items = Item::all();
        return response()->json($items);
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        //
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        $item = new Item([
          'name' => $request->get('name'),
          'price' => $request->get('price')
        ]);
        $item->save();
        return response()->json('Successfully added');
    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        //
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function edit($id)
    {
        $item = Item::find($id);
        return response()->json($item);
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, $id)
    {
        $item = Item::find($id);
        $item->name = $request->get('name');
        $item->price = $request->get('price');
        $item->save();

        return response()->json('Successfully Updated');
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy($id)
    {
      $item = Item::find($id);
      $item->delete();

      return response()->json('Successfully Deleted');
    }
}

Chúng ta cũng cần tạo 1 protected field $fillable trong Item.php nếu không thì nó sẽ báo ‘mass assignment exception

<?php

// Item.php

namespace App;

use Illuminate\Database\Eloquent\Model;

class Item extends Model
{
    protected $fillable = ['name', 'price'];
}

Update file routes >> web.php

<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

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

Nếu bạn insert giá trị vào DB, sẽ có thể bạn gặp vấn đề này:

Laravel 5.5 và Reactjs: Xây dựng CRUD (Create, Read, Update, Delete) từ đầu

Error này là do vấn đề bảo mật CORS. Giải pháp là ta cần allow origin cho Laravel server side. Tạo 1 middleware tại backend và dùng nó cho tất cả các API request.

Tải package CORS dưới đây

composer require barryvdh/laravel-cors

Add Cors\ServiceProvider cho config/app.php

Barryvdh\Cors\ServiceProvider::class,

Để allow CORS cho tất cả route, add HandleCors trong        $middleware của app/Http/Kernel.php

protected $middleware = [
    // ...
    \Barryvdh\Cors\HandleCors::class,
];

Rồi chạy lệnh:

php artisan vendor:publish --provider="Barryvdh\Cors\ServiceProvider"

Xong, bây giờ bạn thử save data vào DB thử xem.

7.Hiển thị data ra ReactJS Frontend

Mở DiplayItem.js component trong folder components, nếu chưa tạo thì bạn tạo đi nhé

// DisplayItem.js

import React, {Component} from 'react';
import axios from 'axios';
import { Link } from 'react-router';
import TableRow from './TableRow';

class DisplayItem extends Component {
  constructor(props) {
       super(props);
       this.state = {value: '', items: ''};
     }
     componentDidMount(){
       axios.get('http://localhost:8000/items')
       .then(response => {
         this.setState({ items: response.data });
       })
       .catch(function (error) {
         console.log(error);
       })
     }
     tabRow(){
       if(this.state.items instanceof Array){
         return this.state.items.map(function(object, i){
             return <TableRow obj={object} key={i} />;
         })
       }
     }

  render(){
    return (
      <div>
        <h1>Items</h1>

        <div className="row">
          <div className="col-md-10"></div>
          <div className="col-md-2">
            <Link to="/add-item">Create Item</Link>
          </div>
        </div><br />

        <table className="table table-hover">
            <thead>
            <tr>
                <td>ID</td>
                <td>Item Name</td>
                <td>Item Price</td>
                <td>Actions</td>
            </tr>
            </thead>
            <tbody>
              {this.tabRow()}
            </tbody>
        </table>
    </div>
    )
  }
}
export default DisplayItem;

Tạo thêm TableRow.js component.

// TableRow.js

import React, { Component } from 'react';

class TableRow extends Component {
  render() {
    return (
        <tr>
          <td>
            {this.props.obj.id}
          </td>
          <td>
            {this.props.obj.name}
          </td>
          <td>
            {this.props.obj.price}
          </td>
          <td>
            <button className="btn btn-primary">Edit</button>
          </td>
          <td>
            <button className="btn btn-danger">Delete</button>
          </td>
        </tr>
    );
  }
}

export default TableRow;

Register route này nhé

// app.js

import DisplayItem from './components/DisplayItem';

render(
  <Router history={browserHistory}>
      <Route path="/" component={Master} >
        <Route path="/add-item" component={CreateItem} />
        <Route path="/display-item" component={DisplayItem} />
      </Route>
    </Router>,
        document.getElementById('example'));

Chúng ta cần thay đổi 1 thứ nữa là cần phải redirect đến component này sau khi save data. Vì vậy trong tập tin CreateItem.js, sửa code như sau:

// CreateItem.js

import {browserHistory} from 'react-router';

axios.post(uri, products).then((response) => {
      browserHistory.push('/display-item');
    });

8. Edit và update data

Mở EditItem.js :

// EditItem.js

import React, {Component} from 'react';
import axios from 'axios';
import { Link } from 'react-router';

class EditItem extends Component {
  constructor(props) {
      super(props);
      this.state = {name: '', price: ''};
      this.handleChange1 = this.handleChange1.bind(this);
      this.handleChange2 = this.handleChange2.bind(this);
      this.handleSubmit = this.handleSubmit.bind(this);
  }

  componentDidMount(){
    axios.get('http://localhost:8000/items/${this.props.params.id}/edit')
    .then(response => {
      this.setState({ name: response.data.name, price: response.data.price });
    })
    .catch(function (error) {
      console.log(error);
    })
  }
  handleChange1(e){
    this.setState({
      name: e.target.value
    })
  }
  handleChange2(e){
    this.setState({
      price: e.target.value
    })
  }

  handleSubmit(event) {
    event.preventDefault();
    const products = {
      name: this.state.name,
      price: this.state.price
    }
    let uri = 'http://localhost:8000/items/'+this.props.params.id;
    axios.patch(uri, products).then((response) => {
          this.props.history.push('/display-item');
    });
  }
  render(){
    return (
      <div>
        <h1>Update Item</h1>
        <div className="row">
          <div className="col-md-10"></div>
          <div className="col-md-2">
            <Link to="/display-item" className="btn btn-success">Return to Items</Link>
          </div>
        </div>
        <form onSubmit={this.handleSubmit}>
            <div className="form-group">
                <label>Item Name</label>
                <input type="text"
                  className="form-control"
                  value={this.state.name}
                  onChange={this.handleChange1} />
            </div>

            <div className="form-group">
                <label name="product_price">Item Price</label>
                <input type="text" className="form-control"
                  value={this.state.price}
                  onChange={this.handleChange2} />
            </div>

            <div className="form-group">
                <button className="btn btn-primary">Update</button>
            </div>
        </form>
    </div>
    )
  }
}
export default EditItem;

và register route trong app.js:

// app.js

import EditItem from './components/EditItem';

render(
  <Router history={browserHistory}>
      <Route path="/" component={Master} >
        <Route path="/add-item" component={CreateItem} />
        <Route path="/display-item" component={DisplayItem} />
        <Route path="/edit/:id" component={EditItem} />
      </Route>
    </Router>,
        document.getElementById('example'));

Update thêm cho TableRow.js

<Link to={"edit/"+this.props.obj.id} className="btn btn-primary">Edit</Link>

9. Xóa data

Để xóa data, chúng ta cần define hàm delete trong TableRow.js

// TableRow.js

import React, { Component } from 'react';
import { Link, browserHistory } from 'react-router';

class TableRow extends Component {
  constructor(props) {
      super(props);
      this.handleSubmit = this.handleSubmit.bind(this);
  }
  handleSubmit(event) {
    event.preventDefault();
    let uri = 'http://localhost:8000/items/${this.props.obj.id}';
    axios.delete(uri);
      browserHistory.push('/display-item');
  }
  render() {
    return (
        <tr>
          <td>
            {this.props.obj.id}
          </td>
          <td>
            {this.props.obj.name}
          </td>
          <td>
            {this.props.obj.price}
          </td>
          <td>
            <Link to={"edit/"+this.props.obj.id} className="btn btn-primary">Edit</Link>
          </td>
          <td>
          <form onSubmit={this.handleSubmit}>
           <input type="submit" value="Delete" className="btn btn-danger"/>
         </form>
          </td>
        </tr>
    );
  }
}

export default TableRow;

Chúc mọi người làm thành công nhé.

Tham khảo thêm tìm việc làm ReactJS , việc làm Laravel hấp dẫn lương cao tại Topdev

  Sử dụng Laravel Mix với Webpack cho tất cả các assets