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
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
Mở terminal và chạy lệnh:
npm run dev
Để run project chúng ta chạy lệnh
php artisan serve
Đầ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';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 ( I am an example component! ); } }Sau khi save,
Laravel Mix
sẽ compile lại tất cả các assets và build 1 bundled chúng tôi 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 ( {this.props.children} ) } } 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';4.Cấu hình ReactJS routes
Tạo thêm 3 components trong component folder bao gồm:
- CreateItem.js
- DisplayItem.js
- 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 ( ) } } 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( document.getElementById('example'));Thử nhấp vào Create Item xem sao:
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 } // browserHistory.push('/display-item'); }); } render() { return ( ) } } 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<?php // create_items_table use IlluminateSupportFacadesSchema; use IlluminateDatabaseSchemaBlueprint; use IlluminateDatabaseMigrationsMigration; class CreateItemsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('items', function (Blueprint $table) { }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('items'); } }Rồi chạy lệnh
php artisan migrateLệ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.phpCần phải tạo một controller là ItemController
php artisan make:controller ItemController --resourceItemController 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 AppHttpControllers; use IlluminateHttpRequest; use AppItem; class ItemController extends Controller { /** * Display a listing of the resource. * * @return IlluminateHttpResponse */ public function index() { $items = Item::all(); } /** * Show the form for creating a new resource. * * @return IlluminateHttpResponse */ public function create() { // } /** * Store a newly created resource in storage. * * @param IlluminateHttpRequest $request * @return IlluminateHttpResponse */ public function store(Request $request) { $item = new Item([ ]); } /** * Display the specified resource. * * @param int $id * @return IlluminateHttpResponse */ public function show($id) { // } /** * Show the form for editing the specified resource. * * @param int $id * @return IlluminateHttpResponse */ public function edit($id) { $item = Item::find($id); } /** * Update the specified resource in storage. * * @param IlluminateHttpRequest $request * @param int $id * @return IlluminateHttpResponse */ public function update(Request $request, $id) { $item = Item::find($id); } /** * Remove the specified resource from storage. * * @param int $id * @return IlluminateHttpResponse */ public function destroy($id) { $item = Item::find($id); } }Chúng ta cũng cần tạo 1 protected field $fillable trong chúng tôi nếu không thì nó sẽ báo ‘mass assignment exception‘
<?php // Item.php namespace App; use IlluminateDatabaseEloquentModel; class Item extends Model { protected $fillable = ['name', 'price']; }<?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:
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.
composer require barryvdh/laravel-corsAdd CorsServiceProvider cho
config/app.php
BarryvdhCorsServiceProvider::class,Để allow CORS cho tất cả route, add
HandleCors
trong$middleware
củaapp/Http/Kernel.php
protected $middleware = [ // ... BarryvdhCorsHandleCors::class, ];Rồi chạy lệnh:
php artisan vendor:publish --provider="BarryvdhCorsServiceProvider"Xong, bây giờ bạn thử save data vào DB thử xem.
7.Hiển thị data ra ReactJS Frontend
// 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(){ this.setState({ items: chúng tôi }); }) .catch(function (error) { console.log(error); }) } tabRow(){ if(this.state.items instanceof Array){ return this.state.items.map(function(object, i){ }) } } render(){ return ( {this.tabRow()} ) } } export default DisplayItem;Tạo thêm TableRow.js component.
// TableRow.js import React, { Component } from 'react'; class TableRow extends Component { render() { return ( {this.props.obj.id} {this.props.obj.name} {this.props.obj.price} ); } } export default TableRow;// app.js import DisplayItem from './components/DisplayItem'; render( 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'; browserHistory.push('/display-item'); });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(){ 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 } this.props.history.push('/display-item'); }); } render(){ return ( <input type="text" className="form-control" value={this.state.name} <input type="text" className="form-control" value={this.state.price} ) } } export default EditItem;và register route trong app.js:
// app.js import EditItem from './components/EditItem'; render( document.getElementById('example'));Update thêm cho chúng tôi
9. Xóa data
Để xóa data, chúng ta cần define hàm delete trong chúng tôi
// 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(); axios.delete(uri); browserHistory.push('/display-item'); } render() { return ( {this.props.obj.id} {this.props.obj.name} {this.props.obj.price} ); } } export default TableRow;