Xây dựng ứng dụng đơn giản với ReactJS và Laravel
Bài đăng này đã không được cập nhật trong 6 năm
Xin chào các bạn, hôm nay mình sẽ tiếp tục làm ví dụ đơn giản làm App Todo cho Reactjs với RESTful sử dụng Laravel.
Nội dung
Khởi tạo server bằng Laravel
Step 1: Khởi tạo project:
$ composer create-project --prefer-dist laravel/laravel laract "5.5.*"
Ở đây mình sử dụng Laravel 5.5 với laract là tên của project.
Bạn nhớ chỉnh sửa file .env
cho phù hợp với kết nối của bạn nhé.
Step 2: Tạo migration
$ php artisan make:migration create_products_table
Trong file migration vừa tạo tại đường dẫn laract/database/migrations/ bạn sửa như sau:
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateProductsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('products', function (Blueprint $table) {
$table->increments('id');
$table->string('title');
$table->text('body');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('products');
}
}
Xong bạn nhớ sử dụng lệnh này nhé:
$ php artisan migrate
Step 3: Tạo model
$ php artisan make:model Product
Bạn vào file Product.php trong thư mục app và chỉnh sửa như sau:
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Product extends Model
{
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = [
'title', 'body'
];
}
Tìm hiểu thêm về Mass Asignment trong Laravel
Step 4: Tạo API
Vào thư mục routes/api
đăng kí Api:
Route::resource('products', 'ProductController');
Step 5: Tạo Controller
Tiếp theo chúng ta sẽ tạo một Resource Controller:
php artisan make:controller ProductController -resource
Tìm hiểu thêm về Resource Controller trong Laravel.
Sau khi tạo Controller, bạn vào thư mục app/Http/Controller/ProductController
sẽ thấy có 7 action. Bạn sửa như sau:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Product;
class ProductController extends Controller
{
/**
* Display a listing of the resource.
*
* @return \Illuminate\Http\Response
*/
public function index()
{
$products = Product::all();
return response()->json($products);
}
/**
* 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)
{
$product = new Product([
'title' => $request->get('title'),
'body' => $request->get('body')
]);
$product->save();
return response()->json('Product Added Successfully.');
}
/**
* 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)
{
$product = Product::find($id);
return response()->json($product);
}
/**
* Update the specified resource in storage.
*
* @param \Illuminate\Http\Request $request
* @param int $id
* @return \Illuminate\Http\Response
*/
public function update(Request $request, $id)
{
$product = Product::find($id);
$product->title = $request->get('title');
$product->body = $request->get('body');
$product->save();
return response()->json('Product Updated Successfully.');
}
/**
* Remove the specified resource from storage.
*
* @param int $id
* @return \Illuminate\Http\Response
*/
public function destroy($id)
{
$product = Product::find($id);
$product->delete();
return response()->json('Product Deleted Successfully.');
}
}
OK, vậy là ta đã tạo xong phần backend.
Tạo front-end bằng react-app
Step 1: Tạo react-app
Mở terminal lên và làm như sau:
$ npx create-react-app react-app
Với react-app là tên của project. Giờ bạn hãy thử xem app của bạn có chạy được không bằng cách:
$ cd react-app
$ npm start
Sau khi chạy 2 lệnh này thì trình duyệt sẽ tự động mở một đường dẫn có địa chỉ localhost:3000
có giao diện dưới đây:
-
Tiếp theo, bạn copy vào file package.json
{
"name": "react-complete-guide",
"version": "0.1.0",
"private": true,
"dependencies": {
"axios": "^0.18.0",
"bootstrap": "^4.0.0",
"react": "^16.0.0",
"react-dom": "^16.0.0",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"react-scripts": "1.0.13"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
},
"devDependencies": {
"eslint": "^4.18.2",
"eslint-plugin-react": "^7.7.0"
}
}
Sau đó chạy npm install
để cài các package trong file này.
Trong thư mục src
bạn tạo thư mục components
. Trong components
bạn tạo các file sau:
components/Product/CreateProduct/CreateProduct.js
:
import React, {Component} from 'react';
import axios from 'axios';
import './createProduct.css';
import config from '../../../config';
class CreateProduct extends Component {
constructor(props){
super(props);
this.state = {productTitle: '', productBody: ''};
this.handleChangeTitle = this.handleChangeTitle.bind(this);
this.handleChangeBody = this.handleChangeBody.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChangeTitle(e){
this.setState({
productTitle: e.target.value
});
}
handleChangeBody(e){
this.setState({
productBody: e.target.value
});
}
handleSubmit(e){
e.preventDefault();
const products = {
title: this.state.productTitle,
body: this.state.productBody
};
let uri = `${config.API_SERVER_URL}products`;
axios.post(uri, products).then((response) => {
this.props.history.push( '/display-item', products );
});
}
render() {
return (
<div className="container">
<form onSubmit={this.handleSubmit}>
<div className="row">
<div className="col-25">
<label htmlFor="title">Product Title</label>
</div>
<div className="col-75">
<input type="text" onChange={this.handleChangeTitle} />
</div>
</div>
<div className="row">
<div className="col-25">
<label htmlFor="body">Product Body</label>
</div>
<div className="col-75">
<input type="text" onChange={this.handleChangeBody}/>
</div>
</div>
<div className="row">
<input type="submit" value="Add product" />
</div>
</form>
</div>
);
}
}
export default CreateProduct;
components/Product/DisplayProduct/DisplayProduct.js
:
import React, { Component } from 'react';
import Product from '../Product';
import { Link } from 'react-router-dom';
import axios from 'axios';
import { findIndex } from 'lodash';
import '../DisplayProduct/displayProduct.css';
import config from '../../../config';
class DisplayProduct extends Component {
constructor(props) {
super(props);
this.state = {
products: []
}
this.deleteProduct = this.deleteProduct.bind(this);
}
deleteProduct = (id, state) => {
axios.delete(`${config.API_SERVER_URL}products/` + id).then((response) => {
let currentProduct = [...this.state.products];
let productIndex = findIndex(currentProduct, p => p.id === id);
currentProduct.splice(productIndex, 1);
this.setState({
products: currentProduct
})
});
}
componentDidMount() {
axios.get(`${config.API_SERVER_URL}products`)
.then(response => {
this.setState({ products: response.data });
})
.catch(function (error) {
console.log(error);
})
}
render() {
const products = this.state.products.map((product) => {
return <Product click={this.deleteProduct} key={product.id} id={product.id} title={product.title} body={product.body} />;
})
return (
<div>
<h1>Products</h1>
<div className="row">
<div className="col-md-2">
<Link to="/add-item">Create Product</Link>
</div>
</div><br />
<table>
<thead>
<tr>
<th>ID</th>
<th>Product Title</th>
<th>Product Body</th>
<th>Edit</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{products}
</tbody>
</table>
</div>
)
}
}
export default DisplayProduct;
components/Product/UpdateProduct/UpdateProduct.js
:
import React, { Component } from 'react';
import axios from 'axios';
import { Link } from 'react-router-dom';
import './updateProduct.css';
import config from '../../../config';
class UpdateProduct extends Component {
constructor(props) {
super(props);
this.state = { title: '', body: '' };
this.handleChangeTitle = this.handleChangeTitle.bind(this);
this.handleChangeBody = this.handleChangeBody.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
componentDidMount() {
axios.get(`${config.API_SERVER_URL}products/${this.props.match.params.id}/edit`)
.then(response => {
this.setState({ title: response.data.title, body: response.data.body });
})
.catch(function (error) {
console.log(error);
})
}
handleChangeTitle(e) {
this.setState({
title: e.target.value
})
}
handleChangeBody(e) {
this.setState({
body: e.target.value
})
}
handleSubmit(event) {
event.preventDefault();
const products = {
title: this.state.title,
body: this.state.body
}
let uri = `${config.API_SERVER_URL}products/` + this.props.match.params.id;
axios.put(uri, products).then((response) => {
this.props.history.push('/display-item', products);
});
}
render() {
return (
<div>
<h1>Update Product</h1>
<div>
<Link to="/display-item">Return to Product</Link>
</div>
<div className="container">
<form onSubmit={this.handleSubmit}>
<div className="row">
<div className="col-25">
<label htmlFor="title">Product Title</label>
</div>
<div className="col-75">
<textarea type="text"
value={this.state.title}
onChange={this.handleChangeTitle} />
</div>
</div>
<div className="row">
<div className="col-25">
<label htmlFor="body">Product Body</label>
</div>
<div className="col-75">
<textarea className="form-control"
onChange={this.handleChangeBody} value={this.state.body}></textarea>
</div>
</div>
<div className="row">
<input type="submit" value="Update" />
</div>
</form>
</div>
</div>
)
}
}
export default UpdateProduct;
components/Product/Product.js
:
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import './product.css';
class Product extends Component {
removeProduct = (event, id) => {
event.preventDefault();
this.props.click(this.props.id);
}
render () {
return (
<tr>
<td>
{ this.props.id}
</td>
<td>
{ this.props.title}
</td>
<td>
{ this.props.body}
</td>
<td>
<Link to={"/edit/" + this.props.id} className="button button2">Edit</Link>
</td>
<td>
<form id="form-delete" onSubmit={this.removeProduct}>
<input type="submit" value="Delete" className="btn btn-danger"/>
</form>
</td>
</tr>
)
}
}
export default Product;
components/Master.js
:
import React, { Component } from 'react';
import { Link } from 'react-router-dom';
import './master.css';
class Master extends Component {
render () {
return (
<div className="container">
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/add-item">Create Product</Link></li>
<li><Link to="/display-item">Products</Link></li>
</ul>
<h1>HELLO</h1>
<div>
{this.props.children}
</div>
</div>
);
}
}
export default Master;
src/hoc/asyncComponent.js
:
import React, { Component } from 'react';
const asyncComponent = (importComponent) => {
return class extends Component {
state = {
component: null
}
componentDidMount () {
importComponent ()
.then(cmp => {
this.setState({
component: cmp.default
});
});
}
render () {
const C = this.state.component;
return C ? <C {...this.props} /> : null;
}
}
}
export default asyncComponent;
src/config.js
:
const config = {
'API_SERVER_URL' : 'http://localhost:8000/api/'
}
export default config;
Bạn tạo một thư mục có tên là containers
trong src
. Sau đó di chuyển file App.js
và App.test.js
vào trong thư mục này. Ở đây mình muốn sắp xếp thư mục cho gọn gàng và dễ nhìn nên mình làm như vậy.
Vậy là xong
Ở đây mình chỉ làm chức năng nên về giao diện nó không đẹp lắm.
Xong rồi. Giờ các bạn vào terminal, di chuyển đến thư mục chứa source code laravel lúc đầu bạn tạo và chạy:
$ php artisan serve
Bạn mở thêm 1 terminal lên và di chuyển đến thư mục chứa source code của react app:
$ npm start
Vậy là chúng ta đã làm xong 1 ví dụ Todos về Reactjs + laravel rồi. Bài viết khá dài, cảm ơn các bạn đã theo dõi. Hi vọng bài viết có ích cho các bạn. Đây là source code trên github: Laravel + Reactjs.
All rights reserved