چطور لاراول و ری اکت جی اس را با هم به کار گیری کنیم؟
محتوای این مقاله احتیاج به بروزرسانی و بازنگری دارد، چون زمان زیادی از انتشار آن گذشته است.
چطور لاراول و ری اکت جی اس را با هم به کار گیری کنیم؟
بسیاری از فرانت اند کارهایی که مبتنی بر فریم ورک ری اکت جی اس برنامه نویسی می کنند علاقه دارند که بتوانند از فریم ورک لاراول پی اچ پی هم استفاده بکنند و بالعکس بسیاری از توسعه دهندگان لاراول کار هستند که بسیار مشتاقند که بتوانند با استفاده از فریم ورک قدرتمند ری اکت جی است رابط کاربری پروژه هایشان را توسعه بدهند. در این مقاله روش رسیدن به این هدف رو به هر دو گروه توضیح می دهیم.
محوریت این مقاله ذیل عناوین زیر است:
1. چطور API لاراول را به ری اکت متصل کنیم
2. چطور پروژه ی ری اکت و لاراول را ساختار بندی کنیم
3. چطور با استفاده از لاراول میکس ری اکت را اسکلت بندی کنیم
لیست کارهایی که باید انجام دهیم:
1. پیش نیازهای انجام این مثال
2. Laravel reac preset
3. نصب و پیکربندی لاراول
4. ری اکت را فریم ورک فرانت اند لاراول قرار می دهیم
5. تعدادی از ماژولهای ضروری مرتبط با ری اکت را نصب می کنیم
6. پیکربندی مسیریاب ری اکت برای این پروژه
7. استفاده از axios برای در خواست ای جکس به سرور لاراول
8. نمایش دیتا در فرانت اند با ری اکت جی اس
9. ویرایش و بروزرسانی اطلاعات
10. حذف اطلاعات
دستور laravel react preset
لاراول مجهز به دستوری به نام ری اکت پری ست است
php artisan preset react
با این دستور می توانیم اسکلت اولیه ری اکت جی اس را بر روی لاراول بسازیم.
گام اول: نصب لاراول و پیکر بندی پایگاه داده
دستور زیر را در کامندلاین وارد و اجرا کنید تا لاراول نصب شود
composer create-project --prefer-dist laravel/laravel ReactJSLaravelTutorial
بعد از نصب لاراول، باید وابستگی های جاوا اسکریپت را نصب کنیم.
به طور پیش فرض فایل package.json وجود دارد، بنابراین ما فقط باید دستور زیر را اجرا کنیم تا وابستگی های npm نصب گردد.
npm install
حالا می ریم به نرم افزار مدیریت پایگاه داده که می تونه phpmyadmin یا mysql workbench و ... باشه و پایگاه داده مون رو درست می کنیم
و سپس در فایل .env مشخصات پایگاه دادمون رو وارد می کنیم
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=LaravelReact
DB_USERNAME=root
DB_PASSWORD=mysql
قدم بعدی اینه که بریم به پوشه ی ریشه ی پروژه و در کامند لاین دستور پایین را اجرا بکنیم
php artisan migrate
این دستور دو جدول که به طور پیش فرض همراه لاراول هستند رو برای ما داخل پایگاه داده ایجاد می کنه
قدم دوم: ری اکت جی اس رو به عنوان فرانت اند برای بک اند لاراول قرار می دهیم.
همونطور که قبلاً هم اشاره کردیم دستور پایین رو باید در کامند لاین وارد و اجرا کنیم
php artisan preset react
که قاعدتاً پس از اجرای دستور بالا شما باید خروجی پایین رو دریافت کنید
React scaffolding installed successfully.
Please run “npm install && npm run dev” to compile your new scaffolding.
در این مرحله اگر به آدرسresources>>assests>>js برید یک پوشه و دو فایل باید ببینید
پوشه ای به نام components که کامپوننت های ری اکت در اون قرار می گیرند و دو فایل با نامهای app.js و bootstrap.js
سپس به آدرس resources >> views >> welcome.blade.php برید و کدهای که در تصویر زیر هست رو در اون کپی و جایگزین کدهای قبلی کنید
<!-- welcome.blade.php -->
<!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>
سپس در کامندلاین کد پایین رو اجرا کنید
npm run dev
اجرای این دستور تمام asset ها کامپایل می شوند و فایل باندل نهایی جاوااسکریپت در پوشه ی public >> js >> app.js قرار می گیره
دوباره به کامند لاین برید و دستور پایین رو اجرا کنید
php artisan serve
این دستورسرور توسعه رو بوت می کنه که در پورت 8000 قابل دسترس خواهد بود
قدم سوم: ما باید تعدادی از وابستگی های ری اکت رو نصب بکنیم
اولین چیزی که باید نصب کنیم react-router برای مسیریابی برنامه مون هست. بنابراین دستور زیر رو در کامند لاین اجرا می کنیم
npm install react-router@2.8.1
برای تجربه ی کاربری بهتر من نسخه ی قدیمی تر react-router رو نصب کردم
برید به کامند لاین و دستور زیر رو اجرا کنید
npm run watch
این تمام تغییرات پوشه ی assets رو رصد می کنه و در صورت هر تغییری به طور خودکار دوباره کامپایل رو انجام میده
ما ری اکت رو با تغییرات پیش فرض باقی گذاشتیم ولی پروژه مون رو به شکلی که نیاز داریم سازماندهی کردیم
در این مرحله کد پایین رو در فایل 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'));
و سپس فایل Example.js رو باید تغییر بدید و کد زیر رو در اون وارد و جایگزین کدهای قبلی کنید
// 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>
);
}
}
بعد از ذخیره ی فایل قبلی، لاراول میکس تمام asset های ما رو دوباره کامپایل می کنه و فایل باندل به نام app.js رو تولید می کنه
در این مرحله اگر به مرورگر برگردید و صفحه را رفرش کنید تغییری مشاهده نمی کنید اما هنوز کار ما تمام نشده است و باید مراحل را دنبال کنیم
ما باید کامپوننت دیگری ایجاد کنیم به نام Master.js درون پوشه ی کامپوننت
// 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;
اکنون فایل 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';
render(<Master />, document.getElementById('example'));
قدم چهارم: پیکربندی روتر ری اکت جی است برای برنامه
سه کامپوننت زیر را در پوشه ی کامپوننت ایجاد کنید
- CreateItem.js
- DisplayItem.js
- EditItem.js
فرم CreateItem.js را برای ذخیره اطلاعات آیتم ها ایجاد می کنیم
// 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;
فایل 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'));
وقتی که تمام کارهای بالا را درست انجام دادید و فایلها را ذخیره کردید، لاراول میکس فایلها را دوباره کامپایل می کند.
سپس اگر سرور توسعه ی لاراول را روشن نکرده اید لطفاً با دستور php artisan serve این سرور را روشن کنید
و سپس به آدرس http://localhost:8000/ بروید
حالا درصورتی که روی لینک CreateItem کلیک بکنید شما صفحه ی زیر را می بینید و البته در این زمان آدرس صفحه به صورت http://localhost:8000/add-item خواهد بود
قدم پنجم: استفاده از axios برای فراخوانی درخواست ای جکس از سرور توسعه ی لاراول
برای فراخوانی درخواست شبکه به سرور ما از axios استفاده می کنیم
تعدادی رویداد برای گرفتن اطلاعات از ورودی های فروم و ارسال درخواست پست به سرور اضافه می کنیم
// 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 : لاراول را به عنوان بک اند پروژه قرار می دهیم
در این مرحله ما از ری اکت به لاراول حرکت می کنیم برای اینکه لاراول را به عنوان بک اند پروژه مورد استفاده قرار دهیم. ما از مسیریاب وب برای این پروژه استفاده خواهیم کرد، بنابراین ما تمام مسیرها را درون فایل routes >> web.php قرار می دهیم.
عملیات CRUD را برروی این آیتم ها انجام می دهیم. بنابراین اول طرح واره اولیه را برای این کار ایجاد می کنیم. سپس ما کنترلر و مسیریاب مربوطه را ایجاد می کنیم.
به کامند لاین بروید و دستور زیر رو تایپ کنید
php artisan make:model Item -m
این دستور دو فایل ایجاد می کند.
1. فایل مدل
2. فایل migration
فایل create_items_table را در آدرس database/migration/ باز کنید و کد زیر را در آن کپی کنید
<?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');
}
}
به کامند لاین بروید و دستور زیر را اجرا کنید
php artisan migrate
اجرای این دستور جدول آیتم را در دیتابیس ایجاد می کند. همچنین یک فایل مدل به نام item.php در پوشه ی app ایجاد می کند.
و به علاوه یک کنترلر resource به نام itemController نیز ایجاد می شود.
php artisan make:controller ItemController --resource
ItemController شامل تمام توابعی است که عملیات CRUD رو انجام میدهند. ما فقط باید کدهایمان را داخل این توابع وارد کنیم. من اکنون تمام کدها را در فایل زیر وارد کرده ام.
<?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');
}
}
ما همچنین باید پراپرتیه $fillable protected را در فایل item.php باید ایجاد کنیم در غیر اینصورت با استثنای mass assignment مواجه خواهیم شد.
<?php
// Item.php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Item extends Model
{
protected $fillable = ['name', 'price'];
}
فایل web.php در پوشه ی routes را به صورت زیر بروزرسانی کنید.
<?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');
حالا اگر شما بخواهید مقادیری را وارد پایگاه داده کنید ممکن است با پیغام خطای زیر مواجه شوید
Possible Errors: XMLHttpRequest cannot load http://localhost:8000/items. No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://localhost:3000’ is therefore not allowed access. The response had an HTTP status code 500.
این پیغام خطا به این خاطر CORS بوجود آمده است.
وقتی که مرورگر به آدرس دامین دیگری درخواست ارسال می کند به طور پیش فرض درخواست رد می شود.
Possible Solutions:
برای حل این مشکل ما باید این اجازه را به سرور لاراول بدهیم. بنابراین ما باید در بک اند یک middleware ایجاد کنیم و این middleware را بر روی ای پی آی ها اعمال کنیم. با قرار دادن این middleware ما به صراحت به لاراول اعلام می کنیم که اجازه ی دسترسی این درخواست ها به resource ها یا منابع ما را بدهد.
با اجرای دستور زیر پکیج CORS مختص لاراول را برای جلوگیری از این خطا دانلود و نصب کنید و بقیه ی مراحل را ادامه دهید.
composer require barryvdh/laravel-cors
Cors\serviceProvider را به آرایه ی provider های موجود در فایل config/app.php اضافه کنید.
Barryvdh\Cors\ServiceProvider::class,
برای اجازه ی CORS به تمام مسیرها، میدلویر handleCors را به پراپرتی $middleware از app/Http/Kernel.php::class
protected $middleware = [
// ...
\Barryvdh\Cors\HandleCors::class,
];
می توانید با دستور زیر فایل پیکربندی را پابلیش کنید(به سرور کپی کنید)
php artisan vendor:publish --provider="Barryvdh\Cors\ServiceProvider"
حالا دوباره امتحان کنید، این اطلاعات را در پایگاه داده ذخیره می کند.
قدم هفتم: نمایش اطلاعات در فرانت اند ری اکت جی است
کامپوننت DisplayItem.js را درون پوشه ی کامپوننت ها بسازید
// 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;
حالا کامپوننت TableRow.js را بسازید.
// 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;
این مسیر را در برنامه ثبت کنید.
ب// 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'));
این فایل را باید تغییر دهیم، وقتی که ما اطلاعات را ذخیره می کنی، ما باید به این کامپوننت ریدایرکت کنیم. بنابراین باید فایل CreateItem.js را اصلاح کنیم.
// CreateItem.js
import {browserHistory} from 'react-router';
axios.post(uri, products).then((response) => {
browserHistory.push('/display-item');
});
گام هشتم: ویرایش و به روزرسانی اطلاعات
کامپوننت 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;
حالا این مسیر را در فایل 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'));
حالا باید کامپوننت TableRow.js را بروزرسانی کنید.
<Link to={"edit/"+this.props.obj.id} className="btn btn-primary">Edit</Link>
گام نهم: حذف اطلاعات
برای حذف اطلاعات ما باید تابع delete را در 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;
بالاخره برنامه ی ما که برای انجام عملیات نوشتن و خواندن و بروزرسانی و حذف توسط لاراول و ری اکت طراحی شده بود به پایان رسید.