The 5 star rating system allows users to rate articles on a scale of 1 to 5.
It is a popular way for visitors to merchant websites to rate the quality of products and services. It is also commonly used to rate the quality of user-generated content, such as reviews and comments.
In this tutorial, I am creating a 5 star rating system in CodeIgniter 4 where the ratings will be stored in a database and the average rating will be displayed with the article. Users can rate an article and based on the rating the average rating gets updated.

Contents
1. Database configuration
- Open
.envfile which is available at the project root.
NOTE – If dot (.) not added at the start then rename the file to .env.
- Remove # from start of
database.default.hostname,database.default.database,database.default.username,database.default.password,database.default.DBDriver,database.default.DBPrefix, anddatabase.default.port. - Update the configuration and save it.
database.default.hostname = 127.0.0.1 database.default.database = codeigniterdb database.default.username = root database.default.password = root database.default.DBDriver = MySQLi database.default.DBPrefix = database.default.port = 3306
2. Enable CSRF
- Again open
.envfile. - Remove # from the start of the
security.tokenName,security.headerName,security.cookieName,security.expires,andsecurity.regenerate. - I update the
security.tokenNamevalue with'csrf_hash_name'. With this name read CSRF hash. You can update it with any other value. - If you don’t want to regenerate CSRF hash after each request then set
security.regenerate = false.
security.tokenName = 'csrf_hash_name' security.headerName = 'X-CSRF-TOKEN' security.cookieName = 'csrf_cookie_name' security.expires = 7200 security.regenerate = true
- Open
app/Config/Filters.phpfile. - Uncomment
'csrf'in'before'if commented.
// Always applied before every request
public $globals = [
'before' => [
// 'honeypot',
'csrf',
// 'invalidchars',
],
'after' => [
'toolbar',
// 'honeypot',
// 'secureheaders',
],
];
3. Create Table
I am creating 2 tables using migration –
- posts – Store post records.
- post_ratings – Store user rating on a post.
posts Table –
php spark migrate:create create_posts_table
- Now, navigate to
app/Database/Migrations/folder from the project root. - Find a PHP file that ends with
CreatePostsTableopen it. - Define the table structure in the
up()method. - Using the
down()method deletepoststable that calls when undoing migration.
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreatePostsTable extends Migration
{
public function up(){
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 10,
'unsigned' => true,
'auto_increment' => true,
],
'title' => [
'type' => 'VARCHAR',
'constraint' => '100',
],
'description' => [
'type' => 'TEXT',
'null' => true,
],
'link' => [
'type' => 'VARCHAR',
'constraint' => '255',
],
]);
$this->forge->addKey('id', true);
$this->forge->createTable('posts');
}
public function down(){
$this->forge->dropTable('posts');
}
}
post_ratings Table –
php spark migrate:create create_post_ratings_table
- Again, navigate to the
app/Database/Migrations/folder from the project root. - Find a PHP file that ends with
CreatePostRatingsTableopen it. - Define the table structure in the
up()method. Added foreign key onpost_idfield. I didn’t add a foreign key onuser_idbut you can add it will implementing. - Using the
down()method deletepost_ratingstable that calls when undoing migration.
<?php
namespace App\Database\Migrations;
use CodeIgniter\Database\Migration;
class CreatePostRatingsTable extends Migration
{
public function up(){
$this->forge->addField([
'id' => [
'type' => 'INT',
'constraint' => 10,
'unsigned' => true,
'auto_increment' => true,
],
'user_id' => [
'type' => 'INT',
'constraint' => 10,
'unsigned' => true,
],
'post_id' => [
'type' => 'INT',
'constraint' => 10,
'unsigned' => true,
],
'rating' => [
'type' => 'VARCHAR',
'constraint' => 10
]
]);
$this->forge->addKey('id', true);
$this->forge->addForeignKey('post_id', 'posts', 'id', 'CASCADE', 'CASCADE');
$this->forge->createTable('post_ratings');
}
public function down(){
$this->forge->dropTable('post_ratings');
}
}
- Run the migration –
php spark migrate
4. Model
Create 2 Models –
- Posts
- PostRatings
Posts Model –
- Create
PostsModel –
php spark make:model Posts
- Open
app/Models/Posts.phpfile. - In
$allowedFieldsArray specify field names –['title','description','link']that can be set during insert and update.
Completed Code
<?php
namespace App\Models;
use CodeIgniter\Model;
class Posts extends Model
{
protected $DBGroup = 'default';
protected $table = 'posts';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $insertID = 0;
protected $returnType = 'array';
protected $useSoftDeletes = false;
protected $protectFields = true;
protected $allowedFields = ['title','description','link'];
// Dates
protected $useTimestamps = false;
protected $dateFormat = 'datetime';
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
protected $deletedField = 'deleted_at';
// Validation
protected $validationRules = [];
protected $validationMessages = [];
protected $skipValidation = false;
protected $cleanValidationRules = true;
// Callbacks
protected $allowCallbacks = true;
protected $beforeInsert = [];
protected $afterInsert = [];
protected $beforeUpdate = [];
protected $afterUpdate = [];
protected $beforeFind = [];
protected $afterFind = [];
protected $beforeDelete = [];
protected $afterDelete = [];
}
PostRatings Model –
- Create
PostRatingsModel –
php spark make:model PostRatings
- Open
app/Models/PostRatings.phpfile. - In
$allowedFieldsArray specify field names –['user_id','post_id','rating']that can be set during insert and update.
Completed Code
<?php
namespace App\Models;
use CodeIgniter\Model;
class PostRatings extends Model
{
protected $DBGroup = 'default';
protected $table = 'post_ratings';
protected $primaryKey = 'id';
protected $useAutoIncrement = true;
protected $insertID = 0;
protected $returnType = 'array';
protected $useSoftDeletes = false;
protected $protectFields = true;
protected $allowedFields = ['user_id','post_id','rating'];
// Dates
protected $useTimestamps = false;
protected $dateFormat = 'datetime';
protected $createdField = 'created_at';
protected $updatedField = 'updated_at';
protected $deletedField = 'deleted_at';
// Validation
protected $validationRules = [];
protected $validationMessages = [];
protected $skipValidation = false;
protected $cleanValidationRules = true;
// Callbacks
protected $allowCallbacks = true;
protected $beforeInsert = [];
protected $afterInsert = [];
protected $beforeUpdate = [];
protected $afterUpdate = [];
protected $beforeFind = [];
protected $afterFind = [];
protected $beforeDelete = [];
protected $afterDelete = [];
}
5. Routes
- Open
app/Config/Routes.phpfile. - Define 2 routes –
- / – Display records list with rating bar.
- post/updaterating – Update user post rating.
$routes->get('/', 'PostsController::index');
$routes->get('post/updaterating', 'PostsController::updateRating');
6. Controller
- Create
PostsControllerController –
php spark make:controller PostsController
- Open
app/Controllers/PostsController.phpfile. - Import
PostsandPostRatingsModel. - I created
$useridclass variable to store user id for rating. You can update its value with logged-in SESSION user id.’
Create 2 methods –
- index() – Fetch all records from the
poststable. Loop on the fetched records to get user rating on thepost_ratingstable and calculate average rating of the post.
Pass $userrating and $avgrating with post data.
Load index view and pass $data Array.
- updateRating() – Using this method handle AJAX requests.
Read passed post_id and rating and assign them to the variables. Check if user has any rating on the $post_id or not in the post_ratings table.
If exists then update the rating field value with $rating otherwise insert a new record in the post_ratings table.
Calculate the average rating of the $post_id and assign to $data['avgrating'].
Return $data Array in JSON format.
Completed Code
<?php
namespace App\Controllers;
use App\Controllers\BaseController;
use App\Models\Posts;
use App\Models\PostRatings;
class PostsController extends BaseController
{
public $userid = 2;// Replace this value with your user SESSION variable
public function index(){
$postsObj = new Posts();
$postRatingsObj = new PostRatings();
// Fetch all posts
$postData = $postsObj->select('*')->findAll();
foreach($postData as $key=>$post){
## User Rating
$postratingData = $postRatingsObj->select('rating')
->where('post_id',$post['id'])
->where('user_id',$this->userid)
->find();
$userrating = 0;
if(!empty($postratingData)){
$userrating = $postratingData[0]['rating'];
}
## Post average rating
$postratingData = $postRatingsObj->select('ROUND(AVG(rating),1) as averageRating')
->where('post_id',$post['id'])
->find();
$avgrating = $postratingData[0]['averageRating'];
if($avgrating == ''){
$avgrating = 0;
}
$postData[$key]['userrating'] = $userrating;
$postData[$key]['avgrating'] = $avgrating;
}
$data['posts'] = $postData;
return view('index',$data);
}
public function updateRating(){
$request = service('request');
$getData = $request->getGet();
$post_id = $getData['post_id'];
$rating = $getData['rating'];
$postRatingsObj = new PostRatings();
// Check user rating
$postratingData = $postRatingsObj->select('id')
->where('post_id',$post_id)
->where('user_id',$this->userid)
->find();
if(!empty($postratingData)){
## Update user rating
$postrating_id = $postratingData[0]['id'];
$postRatingsObj->set('rating', $rating);
$postRatingsObj->where('id', $postrating_id);
$postRatingsObj->update();
}else{
## Insert user rating
$insdata = [
'user_id' => $this->userid,
'post_id' => $post_id,
'rating' => $rating
];
$postRatingsObj->insert($insdata);
}
// Calculate average rating
$postratingData = $postRatingsObj->select('ROUND(AVG(rating),1) as averageRating')
->where('post_id',$post_id)
->find();
$avgrating = $postratingData[0]['averageRating'];
if($rating == ''){
$avgrating = 0;
}
$data['avgrating'] = $avgrating;
return $this->response->setJSON($data);
}
}
7. View
- Create
index.phpfile inapp/Views/folder. - I am using bootstrap-star-rating jQuery plugin for the rating bar. You can download it from here.
- Extract the zip file in
public/folder. - Include Bootstrap, Fontawesome, jQuery and bootstrap-star-rating library –
<!-- CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/fontawesome.min.css">
<link rel="stylesheet" href="<?= base_url('bootstrap-star-rating/css/star-rating.css') ?>" media="all" type="text/css"/>
<link rel="stylesheet" href="<?= base_url('bootstrap-star-rating/themes/krajee-svg/theme.css') ?>" media="all" type="text/css"/>
<!-- Script -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script type="text/javascript" src="<?= base_url('bootstrap-star-rating/js/star-rating.min.js') ?>"></script>
- Loop on the
$posts. Display post data with 5 star rating bar.
Adding Rating bar –
- For adding rating bar you need to add
<input >element with classrating rating-loadingand setdataattributes.
<input class="rating rating-loading" data-post_id="<?= $post['id'] ?>" data-min="0" data-max="5" data-step="0.5" data-show-clear="false" data-show-caption="false" data-size="md" value="<?= $post['userrating'] ?>" >
I have set the following data attributes –
- data-post_id – Store post id. Use this value in jQuery when the rating is been changed.
- data-min – Set minimum rating value.
- data-max – Set maximum rating value.
- data-step – Set it to 0.5 if you want to also allow half rating otherwise set it to 1 for full rating.
- data-show-clear – I set it to
falseto hide clear rating button. - data-show-caption – I set it to
falseto hide the rating label. - data-size – Rating bar size. I set it to
mdbut you can change it toxl,lg,sm, andxs.
In value attribute stored the user rating $post['userrating'].
To display the average rating created <span >. It value gets updated using jQuery when rating is been changed.
jQuery –
Define change event on rating class to detect rating change. Assign selected rating in rating variable and also read post_id from the data attribute.
Send AJAX request to post/updaterating. Pass {post_id: post_id,rating:rating} as data. On successful callback update average rating in <span >.
Completed Code
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>5 Star Rating system with jQuery AJAX in CodeIgniter 4</title>
<style type="text/css">
.content{
border: 0px solid black;
border-radius: 3px;
padding: 5px;
margin: 0 auto;
width: 50%;
}
.post{
border-bottom: 1px solid black;
padding: 10px;
margin-top: 10px;
margin-bottom: 10px;
}
.post:last-child{
border: 0;
}
.post h2{
font-weight: normal;
font-size: 30px;
}
.post a.link{
text-decoration: none;
color: black;
}
.post-text{
letter-spacing: 1px;
font-size: 15px;
font-family: serif;
color: gray;
text-align: justify;
}
.post-action{
margin-top: 15px;
margin-bottom: 15px;
}
</style>
<!-- CSS -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.3/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/fontawesome.min.css">
<link rel="stylesheet" href="<?= base_url('bootstrap-star-rating/css/star-rating.css') ?>" media="all" type="text/css"/>
<link rel="stylesheet" href="<?= base_url('bootstrap-star-rating/themes/krajee-svg/theme.css') ?>" media="all" type="text/css"/>
<!-- Script -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script type="text/javascript" src="<?= base_url('bootstrap-star-rating/js/star-rating.min.js') ?>"></script>
</head>
<body>
<div class="content">
<?php
foreach($posts as $post){
?>
<div class="post">
<h2>
<a href='<?= $post['link'] ?>' class='link' target='_blank'><?= $post['title'] ?></a>
</h2>
<div class="post-text">
<?= $post['description'] ?>
</div>
<div class="post-action">
<!-- Rating Bar -->
<input class="rating rating-loading"
data-post_id="<?= $post['id'] ?>"
data-min="0"
data-max="5"
data-step="0.5"
data-show-clear="false"
data-show-caption="false"
data-size="md"
value="<?= $post['userrating'] ?>"
>
<div style='clear: both;'></div>
<!-- Display average rating -->
Average Rating : <span id='avgrating_<?= $post['id'] ?>'><?= $post['avgrating'] ?></span>
</div>
</div>
<?php
}
?>
</div>
<!-- Script -->
<script type="text/javascript">
$(document).ready(function(){
// Detect Rating change
$('.rating').on(
'change', function () {
var rating = $(this).val(); // Selected Rating
var post_id = $(this).data('post_id');
// AJAX request
$.ajax({
url:"<?=site_url('post/updaterating')?>",
data: {post_id: post_id,rating:rating},
dataType: 'json',
success: function(response){
// Update average rating
$('#avgrating_'+post_id).text(response.avgrating)
}
});
}
);
});
</script>
</body>
</html>
8. Demo
9. Conclusion
A rating system is a powerful way to enhance user engagement on your website or application. By following the steps outlined in this tutorial, you can easily create a rating system that is both user-friendly and efficient.
In the example, I have fixed the userid for rating. While implementing this on your project replace its value with the logged-in user id.
You can view more CodeIgniter 4 tutorials on this site.
If you found this tutorial helpful then don't forget to share.