Today, all store websites have a system where customers can enter their opinions on the website. This system not only improves the SEO of the website but can be used to ask the opinions, suggestions, and criticisms of the customers about the product. We discussed the comment system using PHP and React in the comment system. In this article, we want to talk about PHP and React rating system with MySQL database.
This system is similar to PHP and React comment system, which you can read for a better understanding of this article.
What is a rating system?
Rating system is a mechanism that is mostly used in store websites to receive customer opinions, criticisms and suggestions about a product. In this system, the customer first determines his level of satisfaction with the product from 1 to 5, which is displayed with a star.
Then he writes his opinion about the product using the comment system. This comment may be critical or suggestive. Because this system causes the product page to be updated, it is also effective in website SEO.
What is PHP?
In order to respond to user requests, we will use the server-side scripting language PHP. We will use PHP to establish a connection to the MySQL database where our data is kept and respond to client requests.
What is react?
JavaScript was used to create the technology known as ReactJS, which is not a programming language. React will be used in this tutorial’s user-side design. This technique improves both the user experience and the speed of development. We now find it easy to send, receive, and show data from the server thanks to React. This technology transmits and receives data using AJAX. If you are unfamiliar with AJAX, you can learn the fundamentals of it from the article How to use AJAX in WordPress that we have provided for you.
What is MySQL?
Our data can be kept in a MySQL database. One of the most effective and well-liked databases used today as a database on many websites is this one. WordPress, which is built with about 30% of the websites on the Internet today, also uses MySql as a database. If you are interested in how to work with a database in WordPress, we have provided an article with this title for you.
MySQL database design for PHP and React rating system
We will develop and implement the database first. As we previously stated, the rating system is a component of the website that requires the implementation of several tables in the database. However, since the sole focus of this tutorial is the implementation of the rating system, we will only implement the components necessary for this system.
In the database, we need two tables in the following order.
- Product: Each review belongs to a product, and we put the information about that product in the product table.
- Review: The review table includes all user reviews that are stored in this table.
Notes about the Comment table
- Each product can have infinite reviews, but each review is specific to one product, so we store the product ID in this table.
- Each product can have infinite reviews, but each review is specific to one product, so we store the product ID in this table.
- This rating system supports nested reviews, so we store the parent ID in parent_id. If there is no parent review, 0 will be entered.
Now open phpMyAdmin and create a database named “rating_system”. Then enter the following queries to create the required tables that we have designed above so that the tables are created.
1. product
CREATE TABLE IF NOT EXISTS `product` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` text NOT NULL,
`description` text,
`thumbnail` text,
`price` double NOT NULL,
`discount` tinyint(4) DEFAULT NULL,
PRIMARY KEY (`id`)
)
2. review
CREATE TABLE IF NOT EXISTS `review` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`review` text CHARACTER SET utf8 NOT NULL,
`rate` tinyint(4) DEFAULT NULL,
`customer_email` varchar(255) CHARACTER SET utf8 NOT NULL,
`customer_name` text CHARACTER SET utf8 NOT NULL,
`date` datetime NOT NULL,
`parent_id` int(11) DEFAULT NULL,
`status` tinyint(4) NOT NULL,
`product_id` int(11) NOT NULL,
PRIMARY KEY (`id`)
)
How to create PHP and React rating system
It is required to establish a project that supports both ReactJS and PHP because the rating system uses React as the front end and PHP as the back end. The restriction is on PHP, as NodeJS can run React in any folder on your machine. Navigate to the location you specified while setting up Wamp or Xampp. Go to the “www” folder if you’re using Wamp and the “htdocs
” folder if you’re using Xampp.
Open VS Code in this folder and enter the following code in the terminal section.
npx create-react-app hs-rating-system
The React project installation will begin with this command. The project’s name is hs-rating-system, but you can give it any other name you like.
After installing React, open the hs-rating-system folder using VS Code.
Before running the project, we have to install the necessary components. We use Material design rating stars the open the terminal and execute the following code.
npm install @mui/material @emotion/react @emotion/styled
A sample project that will execute the sample created page that we will later change is included in the React project. To run the React project, type the following command into the Terminal.
npm start
You will observe that NodeJS by default launches your browser and launches the project at the following address after launching the project.
http://localhost:3000
Database class
In the root folder of the project, create a folder called classes and create database.php inside it and open it.
Enter the following codes in database.php.
<?php
class Database {
private $server_name = 'localhost';
private $database_username = 'root';
private $database_password = '';
private $database_name = 'rating_system';
private $connection = null;
public function getProduct($id) {
$this->connection = new mysqli(
$this->server_name,
$this->database_username,
$this->database_password,
$this->database_name
);
$this->connection->set_charset('utf8');
$sql = $this->connection->prepare( 'SELECT * FROM `product` WHERE id=?' );
$sql->bind_param('i', $id);
$sql->execute();
$result = $sql->get_result();
if ($result->num_rows > 0) {
$post = $result->fetch_assoc();
$sql->close();
$this->connection->close();
return $post;
}
$sql->close();
$this->connection->close();
return false;
}
public function getReviews($id) {
$this->connection = new mysqli(
$this->server_name, $this->database_username,
$this->database_password,
$this->database_name
);
$this->connection->set_charset('utf8');
$sql = $this->connection->prepare( 'SELECT * FROM `review` WHERE product_id=? ORDER BY date' );
$sql->bind_param('i', $id);
$sql->execute();
$result = $sql->get_result();
if ($result->num_rows > 0) {
$reviews = [];
while ($row = $result->fetch_assoc()) {
array_push($reviews, $row);
}
$sql->close();
$this->connection->close();
return $reviews;
}
$sql->close();
$this->connection->close();
return false;
}
public function insertReview($review) {
$this->connection = new mysqli(
$this->server_name,
$this->database_username,
$this->database_password,
$this->database_name
);
$this->connection->set_charset('utf8');
$sql = $this->connection->prepare( 'INSERT INTO `review`(`review`, `rate`, `customer_email`, `customer_name`, `date`, `parent_id`, `status`, `product_id`) VALUES (?,?,?,?,?,?,?,?)' );
$sql->bind_param(
'sisssiii',
$review['review'],
$review['rate'],
$review['customer_email'],
$review['customer_name'],
$review['date'],
$review['parent_id'],
$review['status'],
$review['product_id']
);
if ($sql->execute()) {
$sql->close();
$this->connection->close();
return true;
}
$error = $this->connection->errno;
$sql->close();
$this->connection->close();
return $error;
}
}
The database class, which is in charge of connecting to the database, is contained in this file. These three additional functions of this class are as follows.
This project is implemented in the form of three-layer programming, where the database.php file is actually the data layer.
- getProduct: get product information from the database.
- getReviews: get product reviews from the database.
- inserReview: Save the review and rate in the MySQL database
API file
All requests are directed to the api.php file because this system operates as a REST API. Create a file called api.php in the project’s root folder and save the following codes there.
<?php include './classes/database.php';
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$uri = explode('/', $uri);
$action = $uri[3];
$database = new Database();
if ($action === 'product') {
$product_id = $uri[4];
if ($product = $database->getProduct($product_id)) {
return_json(['product' => $product]);
}
} elseif ($action === 'addreview') {
$rest_json = file_get_contents('php://input');
$_POST = json_decode($rest_json, true);
$product_id = $_POST['productId'];
$review = $_POST['review'];
$rate = $_POST['rate'];
$name = $_POST['name'];
$email = $_POST['email'];
$parentId = $_POST['parentId'];
if (
$database->insertReview([
'review' => $review,
'rate' => $rate,
'customer_email' => $email,
'customer_name' => $name,
'date' => date('Y-m-d H:i:s'),
'parent_id' => $parentId,
'status' => 0,
'product_id' => $product_id,
])
) {
return_json(['reviews' => $database->getReviews($product_id)]);
}
} elseif ($action === 'reviews') {
$product_id = $uri[4];
return_json(['reviews' => $database->getReviews($product_id)]);
}
return_json(['status' => 0]);
function return_json($arr)
{
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Headers: *');
header('Content-Type: application/json; charset=utf-8');
echo json_encode($arr);
exit();
}
All requests are sent to this file, which is as follows.
In three-layer programming, this api.php
file is actually the business layer.
- product: It is used to receive product information.
- addreview: It is used to enter a review in the database.
- reviews: Returns all product reviews.
- return_json function: displays the information received from the database as JSON for the user.
Just as customers can reply to other people’s comments, website admins can also do this. There are different ways to identify the logged-in user and customer. We have prepared an article on this website called How To Create Login Form with PHP and React for you.
File index.js
By default, all React projects start executing codes from the index.js file. Open the index.js file and put the following codes in it.
import React from 'react'
import ReactDOM from 'react-dom/client'
import './index.css'
import App from './App'
import reportWebVitals from './reportWebVitals'
import { BrowserRouter, Route, Routes } from 'react-router-dom'
const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
<React.StrictMode>
<BrowserRouter>
<Routes>
<Route path="/" element={<App />} />
</Routes>
</BrowserRouter>
</React.StrictMode>,
)
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals()
App.js file
Like the index.js
file, React projects also have App.js
file by default. Open this file and put the following codes in it.
import './App.css'
import { useCallback, useEffect, useState } from 'react'
import { Rating } from '@mui/material'
function App() {
const [product, setProduct] = useState()
const [reviews, setReviews] = useState()
const [name, setName] = useState('')
const [email, setEmail] = useState('')
const [parentId, setParentId] = useState({ id: 0, name: '' })
const [review, setReview] = useState('')
const [rate, setRate] = useState(0)
const getProduct = useCallback(async () => {
try {
await fetch('http://localhost/hs-rating-system/api/product/1')
.then((respose) => {
if (respose.ok) {
return respose.json()
}
throw new Error('error')
})
.then(async (data) => {
setProduct(data.product)
await fetch(
`http://localhost/hs-rating-system/api/reviews/${data.product.id}`,
)
.then((respose) => {
if (respose.ok) {
return respose.json()
}
throw new Error('error')
})
.then((data) => {
if (data.reviews) {
setReviews(data.reviews)
} else {
//set error
}
})
})
} catch (error) {
console.log(error.message)
}
}, [])
useEffect(() => {
getProduct()
}, [getProduct])
const submitHandler = async (event) => {
event.preventDefault()
try {
await fetch('http://localhost/hs-rating-system/api/addreview', {
method: 'POST',
body: JSON.stringify({
productId: 1,
review: review,
name: name,
email: email,
parentId: parentId.id,
rate: rate,
}),
})
.then((respose) => {
if (respose.ok) {
return respose.json()
}
throw new Error('error')
})
.then((data) => {
if (data.reviews) {
setReviews(data.reviews)
setParentId({ id: 0, name: '' })
setName('')
setEmail('')
setReview('')
setRate(0)
} else {
//set error
}
})
} catch (error) {
console.log(error.message)
}
}
const arrangeReviews = (parent_id) => {
return (
<>
{reviews.map((review) => {
return (
review.parent_id === parent_id && (
<>
<div
className="review"
style={{
border: '1px solid #333333',
padding: '10px',
borderRadius: '5px',
marginBottom: '10px',
}}
>
<div style={{ display: 'flex', alignItems: 'center' }}>
<div style={{ width: '50%' }}>{review.customer_name}</div>{' '}
<div
style={{
width: '50%',
display: 'flex',
justifyContent: 'end',
}}
>
{review.parent_id === 0 && (
<span>
<Rating value={review.rate} readOnly />{' '}
</span>
)}
{review.date}
<span
style={{
marginLeft: '15px',
border: '1px solid #333',
display: 'inline-block',
padding: '5px',
cursor: 'pointer',
}}
onClick={() => {
setParentId({
id: review.id,
name: review.customer_name,
})
}}
>
{' '}
Reply{' '}
</span>
</div>
</div>
<div>{review.review}</div>
</div>
<div className="sub-comment" style={{ marginLeft: '30px' }}>
{has_children(review.id) === true &&
arrangeReviews(review.id)}
</div>{' '}
</>
)
)
})}
</>
)
}
function has_children(id) {
let result = false
reviews.forEach((review) => {
if (review.parent_id === id) {
result = true
}
})
return result
}
return (
<div className="App" style={{ marginTop: '15px', marginBottom: '15px' }}>
{product && (
<div
style={{
maxWidth: '1170px',
marginLeft: 'auto',
marginRight: 'auto',
}}
>
{' '}
<article>
<div>
{product.thumbnail && (
<img
src={product.thumbnail}
alt={product.name}
style={{ maxWidth: '600px' }}
/>
)}
</div>{' '}
<h1>{product.name}</h1>
<p>{product.description}</p>
</article>{' '}
<section>
<div>{reviews && <>{arrangeReviews(0)}</>}</div>
<form
className="comment-form"
onSubmit={submitHandler}
style={{
border: '1px solid #333',
padding: '10px',
maxWidth: '50%',
}}
>
{parentId.id > 0 ? (
<div>
{' '}
Reply to: {parentId.name}
<span
style={{
marginLeft: '30px',
border: '1px solid #333',
padding: '3px',
cursor: 'pointer',
borderRadius: '2px',
}}
onClick={() => {
setParentId({ id: 0, name: '' })
}}
>
{' '}
X{' '}
</span>{' '}
</div>
) : (
<div>
{' '}
<Rating
name="simple-controlled"
value={rate}
onChange={(event, newValue) => {
setRate(newValue)
}}
/>
</div>
)}
<div style={{ display: 'flex', marginTop: '15px' }}>
<div style={{ width: '50%' }}>
<label style={{ width: '100%' }}>Name</label>
<input
style={{ width: '100%', padding: '5px' }}
type="text"
value={name}
onChange={(event) => {
setName(event.target.value)
}}
/>{' '}
</div>{' '}
<div style={{ width: '50%' }}>
<label style={{ width: '100%' }}>Email</label>
<input
style={{ width: '100%', padding: '5px' }}
type="text"
value={email}
onChange={(event) => {
setEmail(event.target.value)
}}
/>
</div>
</div>
<div style={{ marginTop: '15px' }}>
<label style={{ width: '100%' }}>Comment</label>
<textarea
style={{ width: '100%', padding: '5px' }}
value={review}
onChange={(event) => {
setReview(event.target.value)
}}
/>{' '}
</div>{' '}
<div>
<input
type="submit"
value="Send"
style={{
padding: '15px',
background: '#333',
color: '#fff',
border: 'none',
marginTop: '15px',
}}
/>
</div>
</form>{' '}
</section>{' '}
</div>
)}{' '}
</div>
)
}
export default App
This file is responsible for displaying data and sending and receiving information to the server.
The getProduct() function sends a request to the “API” address. Because this code is a sample code, at the end of the address, the ID of the product is entered statically (number 1), which in real projects, this number will be dynamically placed in the address.
const getProduct = useCallback(async () => {
try {
await fetch('http://localhost/hs-rating-system/api/product/1')
.then((respose) => {
if (respose.ok) {
return respose.json()
}
throw new Error('error')
})
.then(async (data) => {
setProduct(data.product)
await fetch(
`http://localhost/hs-rating-system/api/reviews/${data.product.id}`,
)
.then((respose) => {
if (respose.ok) {
return respose.json()
}
throw new Error('error')
})
.then((data) => {
console.log(data)
if (data.reviews) {
setReviews(data.reviews)
} else {
//set error
}
})
})
} catch (error) {
console.log(error.message)
}
}, [])
useEffect(() => {
getProduct()
}, [getProduct])
Once the proper information has been supplied and received, this code will receive reviews after receiving the product information. To lessen the server load in actual projects, only one request is sent to the server, and all the product details and reviews are returned.
In this code, the useEffect
hook is executed once when entering the page and calls the getProduct function.
const submitHandler = async (event) => {
event.preventDefault()
try {
await fetch('http://localhost/hs-rating-system/api/addreview', {
method: 'POST',
body: JSON.stringify({
productId: 1,
review: review,
name: name,
email: email,
parentId: parentId.id,
rate: rate,
}),
})
.then((respose) => {
if (respose.ok) {
return respose.json()
}
throw new Error('error')
})
.then((data) => {
if (data.reviews) {
setReviews(data.reviews)
setParentId({ id: 0, name: '' })
setName('')
setEmail('')
setReview('')
setRate(0)
} else {
//set error
}
})
} catch (error) {
console.log(error.message)
}
}
The submitHandler
function also sends the review form information to the server to be stored in the database. If sending and saving are done correctly, all the reviews of that product and the new review will be returned from the server side to be displayed in the list.
As we said, the rating system displays nested reviews in this project. There are two ways to do this, one is to sort it in the database using SQL commands and the other is to use React on the user side. We do this nested sorting on the user side in the rating system using PHP and React. To reduce the server load.
const arrangeReviews = (parent_id) => {
return (
<>
{reviews.map((review) => {
return (
review.parent_id === parent_id && (
<>
{' '}
<div
className="review"
style={{
border: '1px solid #333333',
padding: '10px',
borderRadius: '5px',
marginBottom: '10px',
}}
>
<div style={{ display: 'flex', alignItems: 'center' }}>
<div style={{ width: '50%' }}>{review.customer_name}</div>
<div
style={{
width: '50%',
display: 'flex',
justifyContent: 'end',
}}
>
{review.parent_id === 0 && (
<span>
<Rating value={review.rate} readOnly />
</span>
)}{' '}
{review.date}{' '}
<span
style={{
marginLeft: '15px',
border: '1px solid #333',
display: 'inline-block',
padding: '5px',
cursor: 'pointer',
}}
onClick={() => {
setParentId({
id: review.id,
name: review.customer_name,
})
}}
>
{' '}
Reply{' '}
</span>{' '}
</div>{' '}
</div>{' '}
<div>{review.review}</div>{' '}
</div>{' '}
<div className="sub-comment" style={{ marginLeft: '30px' }}>
{has_children(review.id) === true && arrangeReviews(review.id)}{' '}
</div>{' '}
</>
)
)
})}{' '}
</>
)
}
function has_children(id) {
let result = false
reviews.forEach((review) => {
if (review.parent_id === id) {
result = true
}
})
return result
}
The above two functions are responsible for sorting and displaying nested reviews of the product. The list of reviews is stored in the reviews variable after receiving it from the server, and we use these two functions to display its nested reviews.
return (
<div className="App" style={{ marginTop: '15px', marginBottom: '15px' }}>
{product && (
<div
style={{ maxWidth: '1170px', marginLeft: 'auto', marginRight: 'auto' }}
>
<article>
<div>
{' '}
{product.thumbnail && (
<img
src={product.thumbnail}
alt={product.name}
style={{ maxWidth: '600px' }}
/>
)}{' '}
</div>{' '}
<h1>{product.name}</h1>
<p>{product.description}</p>{' '}
</article>{' '}
<section>
<div>{reviews && <>{arrangeReviews(0)}</>}</div>{' '}
<form
className="comment-form"
onSubmit={submitHandler}
style={{
border: '1px solid #333',
padding: '10px',
maxWidth: '50%',
}}
>
{parentId.id > 0 ? (
<div>
{' '}
Reply to: {parentId.name}{' '}
<span
style={{
marginLeft: '30px',
border: '1px solid #333',
padding: '3px',
cursor: 'pointer',
borderRadius: '2px',
}}
onClick={() => {
setParentId({ id: 0, name: '' })
}}
>
{' '}
X{' '}
</span>{' '}
</div>
) : (
<div>
<Rating
name="simple-controlled"
value={rate}
onChange={(event, newValue) => {
setRate(newValue)
}}
/>{' '}
</div>
)}{' '}
<div style={{ display: 'flex', marginTop: '15px' }}>
<div style={{ width: '50%' }}>
<label style={{ width: '100%' }}>Name</label>
<input
style={{ width: '100%', padding: '5px' }}
type="text"
value={name}
onChange={(event) => {
setName(event.target.value)
}}
/>{' '}
</div>{' '}
<div style={{ width: '50%' }}>
<label style={{ width: '100%' }}>Email</label>
<input
style={{ width: '100%', padding: '5px' }}
type="text"
value={email}
onChange={(event) => {
setEmail(event.target.value)
}}
/>{' '}
</div>{' '}
</div>{' '}
<div style={{ marginTop: '15px' }}>
<label style={{ width: '100%' }}>Comment</label>
<textarea
style={{ width: '100%', padding: '5px' }}
value={review}
onChange={(event) => {
setReview(event.target.value)
}}
/>{' '}
</div>{' '}
<div>
{' '}
<input
type="submit"
value="Send"
style={{
padding: '15px',
background: '#333',
color: '#fff',
border: 'none',
marginTop: '15px',
}}
/>
</div>{' '}
</form>{' '}
</section>{' '}
</div>
)}{' '}
</div>
)
In this section, we display the product information after receiving it and then call the arrangeReviews
function to display the reviews.
PHP and React rating system conclusion
With PHP as the backend and front end, React as the front end, MySQL as the database using AJAX technology, and a reply mechanism for other users’ reviews, you learned how to create a nested rating system for your website products in this post.