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é.
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
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 react-router@2.8.1
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:
- 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 ( <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:
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 IlluminateSupportFacadesSchema; use IlluminateDatabaseSchemaBlueprint; use IlluminateDatabaseMigrationsMigration; 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 AppHttpControllers; use IlluminateHttpRequest; use AppItem; class ItemController extends Controller { /** * Display a listing of the resource. * * @return IlluminateHttpResponse */ public function index() { $items = Item::all(); return response()->json($items); } /** * 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([ 'name' => $request->get('name'), 'price' => $request->get('price') ]); $item->save(); return response()->json('Successfully added'); } /** * 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); return response()->json($item); } /** * Update the specified resource in storage. * * @param IlluminateHttpRequest $request * @param int $id * @return IlluminateHttpResponse */ 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 IlluminateHttpResponse */ 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 IlluminateDatabaseEloquentModel; 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:
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 CorsServiceProvider cho config/app.php
BarryvdhCorsServiceProvider::class,
Để allow CORS cho tất cả route, add HandleCors
trong $middleware
của app/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
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