CodeIgniter 4: Система рейтинга 5 звезд с jQuery AJAX в CodeIgniter 4
Система 5-звездочного рейтинга позволяет пользователям оценивать статьи по шкале от 1 до 5.
Это популярный способ для посетителей торговых сайтов оценить качество товаров и услуг. Она также часто используется для оценки качества пользовательского контента, например, отзывов и комментариев.
В этом руководстве я создаю 5-звездочную рейтинговую систему в CodeIgniter 4, где рейтинги будут храниться в базе данных, а средняя оценка будет отображаться вместе со статьей. Пользователи могут оценивать статью, и на основе рейтинга средний рейтинг будет обновляться.
Я не буду описывать установку Codeigniter 4 и настройку .env-файла, дабы не терять время, и сразу начнем создавать систему рейтинга в нашем приложении.
1. Некоторые настройки
Откройте файл app/Config/Filters.php. Раскомментируйте 'csrf' в 'before', если он был закомментирован.
// Always applied before every request
public $globals = [
'before' => [
// 'honeypot',
'csrf',
// 'invalidchars',
],
'after' => [
'toolbar',
// 'honeypot',
// 'secureheaders',
],
];
2. Создадим таблицу
Я создам 2 таблицы с помощью миграции
- posts - Хранит записи постов.
- post_ratings - Хранит оценки пользователей по постам.
1. posts –
php spark migrate:create create_posts_table
Теперь перейдите в папку app/Database/Migrations/ из корня проекта.
Найдите PHP-файл, который заканчивается CreatePostsTable, откройте его.
Определим структуру таблицы в методе up().
С помощью метода down() удалим таблицу posts, которая вызывается при отмене миграции.
<?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');
}
}
2. post_ratings
php spark migrate:create create_post_ratings_table
Снова перейдите в папку app/Database/Migrations/ из корня проекта.
Найдите PHP-файл, который заканчивается CreatePostRatingsTable, откройте его.
Определим структуру таблицы в методе up(). Добавим внешний ключ для поля post_id. Я не добавил внешний ключ на поле user_id, но вы можете добавить его.
С помощью метода down() удалите таблицу post_ratings, которая вызывается при отмене миграции.
<?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');
}
}
Запустите миграцию
php spark migrate
3. Модель
Создайте 2 модели
- Posts
- PostRatings
Posts Model
- Создадим модель Posts
php spark make:model Posts
Откройте файл app/Models/Posts.php.
В массиве $allowedFields укажите имена полей - ['title','description','link'], которые могут быть установлены при вставке и обновлении.
Готовый код
<?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
- Создайте модель PostRatings
php spark make:model PostRatings
Откройте файл app/Models/PostRatings.php.
В массиве $allowedFields укажите имена полей - ['user_id','post_id','rating'], которые могут быть установлены при вставке и обновлении.
Готовый код
<?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 = [];
}
4. Маршруты (Routes)
Откройте файл app/Config/Routes.php.
Определите 2 маршрута
- / - Отображение списка записей с рейтинговой шкалой.
- post/updaterating - Обновление рейтинга записи пользователя.
$routes->get('/', 'PostsController::index');
$routes->get('post/updaterating', 'PostsController::updateRating');
5. Контроллеры (Controllers)
Создайте контроллер PostsController
php spark make:controller PostsController
Откройте файл app/Controllers/PostsController.php.
Импортируйте модель Posts и PostRatings.
Я создал переменную класса $userid для хранения идентификатора пользователя (id) для рейтинга. Вы можете обновить значение с помощью идентификатора пользователя (id), вошедшего в SESSION.
Создайте 2 метода
- index() - Выборка всех записей из таблицы posts. Выполним цикл по полученным записям для получения рейтинга пользователя в таблице post_ratings и вычислим средний рейтинг поста.
Передадим $userrating и $avgrating вместе с данными поста.
Загрузим представление index и передадим массив $data.
- updateRating() - С помощью этого метода обрабатываются AJAX-запросы.
Прочитаем переданные post_id и rating и присвоим их переменным. Проверим, есть ли у пользователя рейтинг для $post_id или нет в таблице post_ratings.
Если значение существует, обновим значение поля рейтинга с помощью $rating, иначе вставим новую запись в таблицу post_ratings.
Вычислим средний рейтинг для $post_id и присвоим его $data['avgrating'].
Вернем массив $data в формате JSON.
Готовый код
<?php
namespace App\Controllers;
use App\Controllers\BaseController;
use App\Models\Posts;
use App\Models\PostRatings;
class PostsController extends BaseController
{
public $userid = 2; // Замените это значение на вашу пользовательскую переменную SESSION
public function index(){
$postsObj = new Posts();
$postRatingsObj = new PostRatings();
// Получим все сообщения
$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();
// Проверяем рейтинг пользователей
$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);
}
// Рассчитаем средний рейтинг
$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);
}
}
6. Представления (Views)
- Создайте файл index.php в папке app/Views/.
- Я использую плагин bootstrap-star-rating jQuery для панели рейтинга. Вы можете скачать его отсюда.
- Распакуйте zip-файл в папку public/.
- Подключите Bootstrap, Fontawesome, jQuery и библиотеку bootstrap-star-rating
<!-- 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/jаvascript" src="<?= base_url('bootstrap-star-rating/js/star-rating.min.js') ?>"></script>
Выполним цикл по $posts. Отображение данных поста с полосой рейтинга 5 звезд.
Добавление строки рейтинга
Для добавления строки рейтинга необходимо добавить элемент <input> с классом rating rating-loading и установить атрибуты data.
<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'] ?>"
>
Я установил следующие атрибуты данных:
- data-post_id - Хранит идентификатор поста. Используйте это значение в jQuery при изменении рейтинга.
- data-min - Минимальное значение рейтинга.
- data-max - Максимальное значение рейтинга.
- data-step - Установите значение 0.5, если вы хотите разрешить половинный рейтинг, иначе установите значение 1 для полного рейтинга.
- data-show-clear – Я установил значение false, чтобы скрыть кнопку очистки рейтинга.
- data-show-caption – Я установил значение false, чтобы скрыть метку рейтинга.
- data-size – Размер полосы рейтинга. Я установил значение md, но вы можете изменить его на xl, lg, sm и xs.
В атрибуте value хранится рейтинг пользователя $post['userrating'].
Для отображения среднего рейтинга создается <span>. Его значение обновляется с помощью jQuery при изменении рейтинга.
jQuery
Определим событие изменения (event) для класса rating, чтобы обнаружить изменение рейтинга. Присвоим выбранный рейтинг в переменной rating, а также считаем post_id из атрибута data.
Отправим AJAX-запрос на post/updaterating. Передадим {post_id: post_id,rating:rating} как данные. При успешном обратном вызове обновляем средний рейтинг в <span>.
Готовый код
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Система рейтинга 5 звезд с jQuery AJAX в 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/jаvascript" 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>
<!-- Отображение среднего рейтинга -->
Average Rating : <span id='avgrating_<?= $post['id'] ?>'><?= $post['avgrating'] ?></span>
</div>
</div>
<?php } ?>
</div>
<!-- Script -->
<script type="text/jаvascript">
$(document).ready(function(){
// Обнаружение изменения рейтинга
$('.rating').on(
'change', function () {
var rating = $(this).val(); // Selected Rating
var post_id = $(this).data('post_id');
// AJAX-запрос
$.ajax({
url:"<?=site_url('post/updaterating')?>",
dаta: {post_id: post_id,rating:rating},
dataType: 'json',
success: function(response){
// Обновление среднего рейтинга
$('#avgrating_'+post_id).text(response.avgrating)
}
});
}
);
});
</script>
</body>
</html>
В примере я указал фиксированный userid для рейтинга. Для реализации этого в вашем проекте замените его значение на id вошедшего пользователя.
Посетители, находящиеся в группе Гости, не могут оставлять комментарии к данной публикации.