'CodeIgniter 4: Like and Dislike Functionality
I have implemented the following like/dislike code inspired by Code with AWA.
EDIT I'm using CodeIgniter now if that changes anything. I have updated my question with the code in MVC style. I also have CSRF tokens enabled and that might be my issue?
The post request appears to be working (when I click like or dislike buttons the correct values are displayed in inspect mode. But other than that, nothing else is working (i.e. updating database, displaying like/dislike counts, etc.).
This is my Ajax script view_image.js
:
$(document).ready(function() {
// if the user clicks the subscribe button
$(".subscibe-button").on("click", function() {
var user = $(this).data("user");
$clicked_btn = $(this);
if ($clicked_btn.hasClass("fa-rss")) {
action = "subscribe";
} else if ($clicked_btn.hasClass("fa-user-check")) {
action = "unsubscribe";
}
$.ajax ({
url: "<?= site_url('images/view_image/index') ?>",
type: "post",
dataType: 'json',
data: {
"action": action,
"user": user
},
success: function(data) {
var res = JSON.parse(data);
$("input[name='csrf_test_name']").val(result['csrf']);
}
});
});
// if the user clicks on the like button
$(".like-button").on("click", function() {
var viewkey = $(this).data("viewkey");
$clicked_btn = $(this);
if ($clicked_btn.hasClass("fa-thumbs-o-up")) {
action = "like";
} else if ($clicked_btn.hasClass("fa-thumbs-up")) {
action = "unlike";
}
$.ajax({
url: "<?= site_url('images/view_image/index') ?>",
type: "post",
dataType: "json",
data: {
"action": action,
"viewkey": viewkey
},
success: function(data) {
var res = JSON.parse(data);
if (action == "like") {
$clicked_btn.removeClass("fa-thumbs-o-up");
$clicked_btn.addClass("fa-thumbs-up");
} else if (action == "unlike") {
$clicked_btn.removeClass("fa-thumbs-up");
$clicked_btn.addClass("fa-thumbs-o-up");
}
// display number of likes and dislikes
$clicked_btn.siblings('span.likes').text(res.likes);
$clicked_btn.siblings('span.dislikes').text(res.dislikes);
// Change button styling of the other button if user is reacting the second time to image
$clicked_btn.siblings("i.fa-thumbs-down").removeClass("fa-thumbs-down").addClass("fa-thumbs-o-down");
}
});
});
// If the user clicks on the dislike button
$(".dislike-button").on("click", function() {
var viewkey = $(this).data("viewkey");
$clicked_btn = $(this);
if ($clicked_btn.hasClass("fa-thumbs-o-down")) {
action = "dislike";
} else if ($clicked_btn.hasClass("fa-thumbs-down")) {
action = "undislike";
}
$.ajax({
url: "<?= site_url('images/view_image/index') ?>",
type: "post",
dataType: "json",
data: {
"action": action,
"viewkey": viewkey
},
success: function(data) {
var res = JSON.parse(data);
if (action == "dislike") {
$clicked_btn.removeClass("fa-thumbs-o-down");
$clicked_btn.addClass("fa-thumbs-down");
} else if (action == "undislike") {
$clicked_btn.removeClass("fa-thumbs-down");
$clicked_btn.addClass("fa-thumbs-o-down");
}
// display number of likes and dislikes
$clicked_btn.siblings('span.likes').text(res.likes);
$clicked_btn.siblings('span.dislikes').text(res.dislikes);
// Change button styling of the other button if user is reacting the second time to image
$clicked_btn.siblings("i.fa-thumbs-up").removeClass("fa-thumbs-up").addClass("fa-thumbs-o-up");
}
});
});
This is an excerpt from my View view_image.php
file showing where the Ajax request is referring to.
<div class='view-image-info-r'>
<i <?php if ($userLiked): ?>
class='fa fa-thumbs-up like-button'
<?php else: ?>
class='fa fa-thumbs-o-up like-button'
<?php endif ?>
data-viewkey="<?= $image['viewkey']; ?>"></i>
<span class='likes'><?= esc($likes); ?></span>
</div>
<div class='view-image-info-r'>
<i <?php if ($userDisliked): ?>
class='fa fa-thumbs-down dislike-button'
<?php else: ?>
class='fa fa-thumbs-o-down dislike-button'
<?php endif ?>
data-viewkey="<?= esc($image['viewkey']); ?>"></i>
<span class='dislikes'><?= esc($dislikes); ?></span>
</div>
And this is my Model ActionModel.php
, containing the functions that insert/update which action, be it like/unlike/dislike/undislike.
public function insertAction($input, $where)
{
$this->$db = \Config\Database::connect();
$this->$builder = $db->table('actions');
$data = [];
$viewkey = $input['viewkey'];
switch($input['action']) {
case 'dislike':
$input['action'] = 0;
if (userLiked($viewkey) == false) {
$this->builder->insert($input);
} else {
$this->builder->where($where);
$this->builder->update($input);
}
break;
case 'undislike':
$where = [
'action' => 0,
];
$this->builder->where($where)->delete();
break;
case 'like':
$data = [
'action' => 1,
];
if (userDisliked($viewkey) == false) {
$this->builder->insert($input);
} else {
$this->builder->where($where);
$this->builder->update($input);
}
break;
case 'unlike':
$where = [
'action' => 1,
];
$this->builder->where($where)->delete();
break;
case 'view':
$data = [
'action' => 2,
];
if (userViewed($viewkey) == false) {
$this->builder->insert($input);
} else {
$this->builder->where($where);
$this->builder->update($input);
}
break;
default:
break;
}
return getActions($viewkey);
exit(0);
}
public function getActions($viewkey)
{
$actionModel = new ActionModel();
$data = [];
$data['dislikes'] = $actionModel->getDislikes($viewkey);
$data['likes'] = $actionModel->getLikes($viewkey);
$data['views'] = $actionModel->getViews($viewkey);
$data['favorites'] = $actionModel->getFavorites($viewkey);
return json_encode(['data' => $data, 'csrf' => csrf_hash()]);
}
public function userDisliked($viewkey)
{
$actionModel = new ActionModel();
$where = [];
$where = [
'viewkey' => $viewkey,
'username' => session()->get('username'),
'action' => 0,
];
if ($actionModel->where($where)->first()) {
return true;
} else {
return false;
}
}
public function userLiked($viewkey)
{
$actionModel = new ActionModel();
$where = [];
$where = [
'viewkey' => $viewkey,
'username' => session()->get('username'),
'action' => 1,
];
if ($actionModel->where($where)->first()) {
return true;
} else {
return false;
}
}
Here is my Controller View_Image.php
:
public function index()
{
$viewkey = $this->request->uri->getSegment(4);
if ($this->request->isAJAX()) {
$request = service('request')->getPost('data');
$postData = $request->getPost();
$data = [];
$data['token'] = csrf_hash();
$validation = \Config\Services::validation();
if ($validation->withRequest($this->request)->run() == FALSE) {
if (! empty($this->request->getVar('action')) && ! empty($this->request->getVar('user')) && ! empty(session()->get('username'))) {
$data = $input = [];
$input = [
'subscriber' => $session()->get('username'),
'user_profile' => $this->request->getVar('user'),
'action' => $this->request->getVar('action'),
];
$this->subscribeModel->insertSubscriber($input);
}
if (! empty($this->request->getVar('action')) && ! empty($viewkey) && ! empty(session()->get('username'))) {
$data = $where = [];
$input = [
'viewkey' => $viewkey,
'username' => session()->get('username'),
'action' => $this->request->getVar('action'),
'modified' => date('Y-m-d H:i:s'),
];
$where = [
'viewkey' => $viewkey,
'username' => session()->get('username'),
];
$actionCount = $this->actionModel->insertAction($input, $where);
return $actionCount;
}
}
}
I have tried to limit pasting of my code here so all the echo views and namespace/class declarations have been excised.
Solution 1:[1]
I solved my own question. This was a security issue. This is how you can regenerate the CSRF token within the AJAX request. Be sure to add the line <?= csrf_field() ?>
in your view somewhere. And pass the csrf_token()
and csrf_hash()
in your controller/model (wherever you are returning the JSON response. I do so in my model.
<script>
$(document).ready(function() {
var csrfName = "<?= csrf_token(); ?>";
var csrfHash = "<?= csrf_hash(); ?>";
// if the user clicks on the like button
$(".like-button").on("click", function() {
var viewkey = $(this).data("viewkey");
$clicked_btn = $(this);
if ($clicked_btn.hasClass("fa-thumbs-o-up")) {
action = "like";
} else if ($clicked_btn.hasClass("fa-thumbs-up")) {
action = "unlike";
}
$.ajax({
url: "<?= base_url('/images/view_image/index'); ?>",
type: "post",
dataType: "json",
data: {
[csrfName]: csrfHash,
"action": action,
"viewkey": viewkey,
},
headers: {
'X-Requested-With': 'XMLHttpRequest',
},
success: function(data) {
var res = data;
csrfName = data.csrfName;
csrfHash = data.csrfHash;
if (action == "like") {
$clicked_btn.removeClass("fa-thumbs-o-up");
$clicked_btn.addClass("fa-thumbs-up");
} else if (action == "unlike") {
$clicked_btn.removeClass("fa-thumbs-up");
$clicked_btn.addClass("fa-thumbs-o-up");
}
// display number of likes and dislikes
$clicked_btn.siblings('span.likes').text(res.likes);
$clicked_btn.siblings('span.dislikes').text(res.dislikes);
// Change button styling of the other button if user is reacting the second time to image
$clicked_btn.siblings("i.fa-thumbs-down").removeClass("fa-thumbs-down").addClass("fa-thumbs-o-down");
}
});
});
// If the user clicks on the dislike button
$(".dislike-button").on("click", function() {
var viewkey = $(this).data("viewkey");
$clicked_btn = $(this);
if ($clicked_btn.hasClass("fa-thumbs-o-down")) {
action = "dislike";
} else if ($clicked_btn.hasClass("fa-thumbs-down")) {
action = "undislike";
}
$.ajax({
url: "<?= base_url('/images/view_image/index'); ?>",
type: "post",
dataType: "json",
data: {
[csrfName]: csrfHash,
"action": action,
"viewkey": viewkey
},
headers: {
'X-Requested-With': 'XMLHttpRequest',
},
success: function(data) {
var res = data;
csrfName = data.csrfName;
csrfHash = data.csrfHash;
if (action == "dislike") {
$clicked_btn.removeClass("fa-thumbs-o-down");
$clicked_btn.addClass("fa-thumbs-down");
} else if (action == "undislike") {
$clicked_btn.removeClass("fa-thumbs-down");
$clicked_btn.addClass("fa-thumbs-o-down");
}
// display number of likes and dislikes
$clicked_btn.siblings('span.likes').text(res.likes);
$clicked_btn.siblings('span.dislikes').text(res.dislikes);
// Change button styling of the other button if user is reacting the second time to image
$clicked_btn.siblings("i.fa-thumbs-up").removeClass("fa-thumbs-up").addClass("fa-thumbs-o-up");
}
});
});
});
</script>
Here is my controller View_Image.php index function:
$viewkey = $this->request->uri->getSegment(4);
if ($this->request->IsAjax()) {
if (! empty($this->request->getPost('action')) && ! empty($this->request->getPost('viewkey')) && ! empty(session()->get('username'))) {
$input = $where = [];
$input = [
'action' => $this->request->getPost('action'),
'username' => session()->get('username'),
'viewkey' => $this->request->getPost('viewkey'),
'modified' => date('Y-m-d H:i:s'),
];
$actionCount = $this->actionModel->insertAction($input);
return $actionCount;
}
}
ActionModel.php
public function insertAction($input)
{
$db = \Config\Database::connect();
$builder = $db->table('actions');
$data = $where = [];
$viewkey = $input['viewkey'];
switch($input['action']) {
case 'dislike':
$data = [
'username' => $input['username'],
'action' => 0,
'viewkey' => $input['viewkey'],
'modified' => $input['modified'],
];
if ($this->userLiked($viewkey) == false) {
$builder->insert($data);
} else {
$where = [
'username' => $input['username'],
'viewkey' => $input['viewkey'],
'action' => 1,
];
$builder->where($where)->delete();
$builder->insert($data);
}
break;
case 'undislike':
$where = [
'action' => 0,
];
$builder->where($where)->delete();
break;
case 'like':
$data = [
'username' => $input['username'],
'action' => 1,
'viewkey' => $input['viewkey'],
'modified' => $input['modified'],
];
if ($this->userDisliked($viewkey) == false) {
$builder->insert($data);
} else {
$where = [
'username' => $input['username'],
'viewkey' => $input['viewkey'],
'action' => 0,
];
$builder->where($where)->delete();
$builder->insert($data);
}
break;
case 'unlike':
$where = [
'action' => 1,
];
$builder->where($where)->delete();
break;
return $this->getActions($viewkey);
exit(0);
}
public function userDisliked($viewkey)
{
$actionModel = new ActionModel();
$where = [];
$where = [
'viewkey' => $viewkey,
'username' => session()->get('username'),
'action' => 0,
];
if ($actionModel->where($where)->first()) {
return true;
} else {
return false;
}
}
public function userLiked($viewkey)
{
$actionModel = new ActionModel();
$where = [];
$where = [
'viewkey' => $viewkey,
'username' => session()->get('username'),
'action' => 1,
];
if ($actionModel->where($where)->first()) {
return true;
} else {
return false;
}
}
public function getActions($viewkey)
{
$data = [];
$data = [
'csrfName' => csrf_token(),
'csrfHash' => csrf_hash(),
'dislikes' => $this->getDislikes($viewkey),
'likes' => $this->getLikes($viewkey),
'views' => $this->getViews($viewkey),
'favorites' => $this->getFavorites($viewkey),
];
return json_encode($data);
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | AgBRAT |