likes counter with AJAX (and Symfony) - php

I've got likes system on my website (one like button that becomes empty or filled when clicking) and it works perfectly fine in terms of server side. My issue is the likes counter. I want it to refresh likes count without page reload and cannot really figure out how. I'm kinda newbie with AJAX and that's why I'm here.
Here is my code:
CommentLikeController.php
/**
* #Route("/comments/like-unlike", name="app_comment_like_unlike")
*/
public function like(Request $request, EntityManagerInterface $entityManager): Response
{
if ($request->getMethod() === 'POST'){
//retrieve commentId from AJAX request
$commentId = $request->request->get('entityId');
//check if comment exists
$comment = $entityManager->getRepository(Comment::class)->findOneBy(['id'=> $commentId]);
// return void if not
if (!$comment){
return new JsonResponse();
}
// retrieve csrf Token from AJAX request
$submittedToken = $request->request->get('csrfToken');
// check if it's valid
if ($this->isCsrfTokenValid('comment' . $comment->getId(), $submittedToken)){
$user = $this->getUser();
$commentAlreadyLiked = $entityManager->getRepository(CommentLike::class)
->findOneBy(['user' => $user, 'comment' => $comment]);
// if user unlikes the comment, remove it from DB
if($commentAlreadyLiked){
$entityManager->remove($commentAlreadyLiked);
$entityManager->flush();
return new JsonResponse();
// if user likes the comment, add record to DB
} else{
$like = new CommentLike();
$like->setUser($user);
$like->setComment($comment);
$entityManager->persist($like);
$entityManager->flush();
}
}
}
return new JsonResponse();
}
index.html.twig
<div class="js-likes-unlikes">
<a href="{{ path('app_comment_like_unlike') }}"
id="comment-like"
data-entity-id="{{ comment.id }}"
data-csrf-token="{{ csrf_token('comment' ~ comment.id) }}"
data-liked="{{ comment.commentLikes.isEmpty == true ? '0' : '1' }}"
data-likes-counter="{{ comment.commentLikes.count }}"
class="btn-like">
<i class="♡ {{ comment.commentLikes.isEmpty == true ? '' : 'liked' }}">
{{ comment.commentLikes.isEmpty == true ? '♡' : '♥' }}
</i>
</a>
<span class="counter">{{ comment.commentLikes.count }}</span>
</div>
{% block javascripts %}
{{ parent() }}
<script src="{{ asset('js/comments_like_unlike.js') }}"></script>
{% endblock %}
comments_like_unlike.js
$(document).on('click', '#comment-like', function (e) {
e.preventDefault();
var url = $(this).attr('href');
var entityId = $(this).attr('data-entity-id');
var csrfToken = $(this).attr('data-csrf-token');
var isLiked = $(this).attr('data-liked');
var likesCounter = $(this).attr('data-likes-counter');
if (isLiked === '0') {
$(this).attr('data-liked', 1);
$('.♡').addClass('liked').text('♥')
likesCounter++;
} else {
$(this).attr('data-liked', 0);
$('.♡').removeClass('liked').text('♡')
likesCounter--;
}
$.ajax({
type: 'POST',
dataType: 'json',
data: {'entityId': entityId, 'csrfToken': csrfToken, 'likesCounter': likesCounter},
url: url,
success: function () {
console.log("success");
},
error: function () {
}
});
});
Your help and any explanations are greatly appreciated!
I tried checking and using others' examples of this part on the web but it didn't work out

Here you can do return number likes from server:
else{
$like = new CommentLike();
$like->setUser($user);
$like->setComment($comment);
$entityManager->persist($like);
$entityManager->flush();
return new JsonResponse(
'countLikes' => $like->getComment()->getCommentLikes()->count()
)
}
In twig add id to this span
<span class="counter" id="count-likes-{{ comment.id }}">{{ comment.commentLikes.count }}</span>
Ajax
success: function (response) {
// countLikes is returned in json response from the server
$('#count-likes-' + entityId).text(response['countLikes']);
},
or you can do it directly in the success function without any change in the controller and without return the numbers of likes from the server
success: function (response) {
$('#count-likes-' + entityId).text(parseInt(likesCounter) + 1);
},

Related

Laravel Ajax Pagination: No request

I have some difficulties with my ajax pagination linked to a filter. Here's how it should work. The user can access via a specific page to a form. When clicking the submit button, a raw sql request is made in JS and a POST ajax request is achieved to get the results at the bottom of the page with a pagination menu. This part works. But I have some issues with the pagination menu because the links don't work. For example, by clicking the "page 2" link, nothing happens.
Here are the different parts of my code:
Routes
Route::get('articles/filter', 'ArticleController#filterx');
Route::post('articles/request/ajax/articles/filter', 'ArticleController#filtery');
Route::get('articles/request/ajax/articles/filter', 'ArticleController#filtery');
Controller
ArticleController
public function filterx() { // get filter page
return view('filter');
}
public function filtery(Request $request) { // filter ajax function
$articles = Article::paginate(2);
if($request->ajax()) {
// partial view returned in html
return $html = view('filterResults', compact('articles'));
}
}
Views
filter.blade.php
#extends('layouts/app')
#section('title')
Title
#endsection
#section('content')
<div class="container">
<!-- filter -->
<h2>Filter</h2>
<div class="content-card content">
<form method="POST" action="">
<!-- form code... -->
</form>
</div>
<div id="filter-results">
</div>
</div>
#endsection
filterResults.blade.php
#foreach($articles as $article)
<p>{{ $article->name }}</p>
#endforeach
{{ $articles->links() }}
Javascript
$("#submit-button").click(function(e) {
e.preventDefault();
// ajax request (raw mysql request)
var requestQuery = ...; // (quite long) raw request
console.log(requestQuery); // console verification of request
$.ajax({
headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') },
url: '../articles/request/ajax/articles/filter',
type: 'POST',
data: {
request: requestQuery
},
success: function(html) {
$("#filter-results").empty().html(html);
}
});
});
$(window).on('hashchange', function() {
// if hash in url
if (window.location.hash) {
// page contains hash value
var page = window.location.hash.replace('#', '');
if (page == Number.NaN || page <= 0) {
return false;
}
// if ok ->getData returned
else {
getData(page);
}
}
});
$(document).on('click', '.pagination a', function(e) {
e.preventDefault();
$('.pagination li').removeClass('active');
$(this).parent('li').addClass('active');
var url = $(this).attr('href');
var page = $(this).attr('href').split('page=')[1];
getData(page,url);
});
function getData(page,url) {
$.ajax(
{
url: url,
type: 'get',
datatype: 'html',
done: function(data) {
console.log('ok');
$('#filter-results').empty().html(data);
location.hash = page;
},
fail: function(jqXHR, ajaxOptions, thrownError) {
console.log('No response from server');
}
});
}
I don't understand why it is not working, I thing I misunderstood something.
Thanks and have a good day
Laravel uses the page value from the request, or query string, by convention. If you choose not to use that, you can set your own. If following convention, you'd need to append the query string page=# to your url in the ajax request.
The fourth argument of the Builder::paginate is the page number:
public function paginate($perPage = null, $columns = ['*'], $pageName = 'page', $page = null)
{
$page = $page ?: Paginator::resolveCurrentPage($pageName);
$perPage = $perPage ?: $this->model->getPerPage();
$results = ($total = $this->toBase()->getCountForPagination())
? $this->forPage($page, $perPage)->get($columns)
: $this->model->newCollection();
return $this->paginator($results, $total, $perPage, $page, [
'path' => Paginator::resolveCurrentPath(),
'pageName' => $pageName,
]);
}
You could also define your own page resolver. The default is set in PaginationServiceProvider:
Paginator::currentPageResolver(function ($pageName = 'page') {
$page = $this->app['request']->input($pageName);
if (filter_var($page, FILTER_VALIDATE_INT) !== false && (int) $page >= 1) {
return (int) $page;
}
return 1;
});
Route::get('articles/filter/{page}', 'ArticleController#filter');
public function filter(Request $request, int $page) {
$articles = Article::paginate($page);
}
it's all

Trouble creating button to delete all products from a database for one user in Symfony

Took a few snippets from the different areas I worked on the code. I tried copying and altering the code to delete one product. Currently there is no response when clicking the delete all button I created.
Service
public function deleteAllProductsByVendorId($vendorId)
{
$this->productRepository->deleteAllProductsByVendorId($vendorId);
}
Repository
public function deleteAllProductsByVendorId($vendorId)
{
// #Todo: revisit this at a later date and determine if the vendor id is actually required
$qb = $this->getEntityManager()->createQueryBuilder();
$qb->delete("Thinkfasttoys\Mapwatch\Entity\Product","p")
->andWhere($qb->expr()->eq('p.vendor_id', ':vendor_id'))
->setParameter(':vendor_id', $vendorId)
->getQuery()
->getResult();
}
Controller
/**
* #Route("/admin/vendor/{vendorId}/product/deleteAll", name="admin_vendor_product_delete_all_ajax", defaults={"vendorId"=""})
* #Route("/admin/vendor/{vendorId}/product/deleteAll", name="admin_vendor_product_delete_all"))
* #Secure(roles="ROLE_ADMIN")
*/
public function deleteAllProductForVendorAction($vendorId)
{
$request = $this->container->get('request');
if ($id == '') {
$this->get('session')->getFlashBag()->add('notice-error', 'Vendor Id must be supplied!');
return $this->redirect($this->generateUrl('admin_vendors_list'));
}
$vendorService = $this->get('Thinkfasttoys.MapWatch.Vendors');
$vendor = $vendorService->getProfileById($id);
if (!$vendor) {
$this->get('session')->getFlashBag()->add('notice-error', 'Vendor does not exist!');
return $this->redirect($this->generateUrl('admin_vendors_list'));
}
$user = $vendor->getUser();
if (!$user) {
$this->get('session')->getFlashBag()->add('notice-error', 'User for this Vendor does not exist!');
return $this->redirect($this->generateUrl('admin_vendors_list'));
}
if ($request->isXmlHttpRequest()) {
$productService = $this->get('Thinkfasttoys.MapWatch.Products');
$productService->deleteAllProductsByVendorId($vendorId);
return new Response(json_encode(array('status' => 'ok')),200,array('Content-Type'=>'application/json'));//make sure it has the correct content type
} else {
return $this->redirect($this->generateUrl('admin_vendor_products_edit', array('id' => $vendorId)));
}
}
View
<div class="btn-toolbar">
<a data-toggle="modal" href="#importProductsModal" id="importProducts" class="btn btn-danger">Import Products</a>
<a data-toggle="modal" href="#productModal" id="addProduct" class="btn btn-primary pull-right">Add New Product</a>
<a class="btn btn-danger pull-right" id="deleteall">Delete All</a>
</div>
Created Var deletePathAll
var vendorId = {{ vendorId }};
var getPath = '{{ path('admin_vendor_product_get_ajax', { id: vendorId } ) }}';
var editPath = '{{ path('admin_vendor_product_edit', { id: vendorId } ) }}';
var deletePath = '{{ path('admin_vendor_product_delete_ajax', { id: vendorId } ) }}';
var deletePathAll = '{{ path('admin_vendor_product_delete_all_ajax', { vendorId: vendorId } ) }}';
JS
$('#deleteall').on('click', 'table#products', function(e){
e.preventDefault();
var aData = $('#products').dataTable().fnGetData(aPos);
row.find('a').attr('disabled', true);
var rowId = aData['id'];
$.ajax({
type: "POST",
url: deletePathAll,
success: function(data) {
oTable.fnReloadAjax();
},
error: function(data) {
row.find('a').attr('disabled', false);
}
});
});
Pretty sure your Product entity doesn't have the property vendor_id but vendor.
In this case this should correct the issue
->andWhere($qb->expr()->eq('p.vendor_id', ':vendor_id'))
should be replaced with
->andWhere($qb->expr()->eq('p.vendor', ':vendor_id'))

Laravel 5.4 - Undefined variable: passing variable from controller to view

I'm new to Laravel. I have searched this site but can't find specific help. I'm trying to pass a value of a variable(which is $Like) from Controller to the blade view, but the browser gives an error of
Undefined variable: Like
Here is the part of my view code (dashboard.blade.php):
#foreach($posts as $post)
<article class="post" data-postid="{{ $post->id }}">
<div class="interaction">
{{$Like }} |
</div>
</article>
#endforeach
<script>
var token = '{{ Session:: token() }}';
var urlLike = '{{ route('like') }}';
</script>
part of my controller(PostController.php) code:
public function LikePost(Request $request) // post type
{
$post_id = $request['postId']; // postId from script.js
$is_Like = $request['isLike'] === 'true'; // from script.js ...
$update = false;
$post = Post::find($post_id);
$user = Auth::user();
$like = $user->likes()->where('post_id', $post_id)->first();
$like->like = $is_Like;
$like->user_id = $user->id;
$like->post_id = $post->id;
$Like = $like ? $like->like == 1 ? 'You like this' : 'Like' : 'Like';
return redirect()->route('dashboard')->with('Like', $Like);
}
here is the part of my route code:
//for like post
Route::post('/like', [
'uses'=> 'PostController#LikePost',
'as'=> 'like'
]);
here is my script.js:
$('.like').on('click', function (event) {
event.preventDefault();
postId = event.target.parentNode.parentNode.dataset['postid'];
// check whether previous element of like/dislike is null or not
var isLike = event.target.previousElementSibling == null;
$.ajax({
method: 'POST',
url: urlLike,
data: {
isLike: isLike,
postId: postId,
_token: token
}
}).done(function () {
event.target.innerText = isLike ? event.target.innerText == 'Like' ? 'You like this' : 'Like' : event.target.innerText == 'Dislike' ? 'You dislike this' : 'Dislike';
if (isLike) {
event.target.nextElementSibling.innerText = 'Dislike';
} else {
event.target.previousElementSibling.innerText = 'Like';
}
});
});
I tried in many ways. but every time it shows me an error,
undefined variable: Like in dashboard.blade.php
Anyone help please.....
You are redirecting to a route, which in turn calls its own controller method that passes down variables to dashboard.blade.php
When you do redirect()->route('dashboard')->with('Like', $Like) you are essentially flashing some data to the session.
You need to use session('Like')to access variables in blade when redirecting to a route and flashing variables.
#foreach($posts as $post)
<article class="post" data-postid="{{ $post->id }}">
<div class="interaction">
#if (session('Like'))
<div class="alert alert-success">
{{ session('Like') }} |
</div>
#endif
</div>
</article>
#endforeach
<script>
var token = '{{ Session:: token() }}';
var urlLike = '{{ route('like') }}';
</script>
Read more here https://laravel.com/docs/5.4/responses#redirecting-with-flashed-session-data

Post Error 500 adding a new row using Eloquent

I'm trying to follow a tutorial making a social network, the functionality of liking and disliking when no row is available in the database using Ajax (adding a new entry) is broken. Tutorial i'm following - this video is where he creates the controller: https://www.youtube.com/watch?v=drM19VKbCgU&list=PL55RiY5tL51oloSGk5XdO2MGjPqc0BxGV&index=20
Development Enviroment:
PhpStorm v9.0
Xampp v3.2.2
jQuery v1.12.0
Google Chrome
Error image:
Error image link
The Whole cycle:
Like and Dislike Button (The View):
<a href="#" class="like">{{ Auth::user()->likes()->where('post_id', $post->id)->first() ?
Auth::user()->likes()->where('post_id', $post->id)->first()->like == 1 ?
'You like this post':'Like' : 'Like' }}</a> |
<a href="#" class="like">{{ Auth::user()->likes()->where('post_id', $post->id)->first() ?
Auth::user()->likes()->where('post_id', $post->id)->first()->like == 0 ?
'You don\'t like this post':'Dislike' : 'Dislike' }}</a>
<script>
var urlLike = '{{ route('like') }}';
</script>
route:
Route::post('/like', [
'uses' => 'PostController#postLikePost',
'as' => 'like'
]);
listener + Ajax Call:
$('.like').on('click', function(event){
event.preventDefault();
postId = event.target.parentNode.parentNode.dataset['postid'];
var isLike = event.target.previousElementSibling == null;
//console.log(postId+' '+isLike);
$.ajax({
method: 'POST',
url: urlLike,
data: {isLike: isLike, postId: postId, _token: token}
})
.done(function(){
console.log(event.target.innerText);
event.target.innerText = isLike ? event.target.innerText == 'Like' ? 'You like this post':'Like' : event.target.innerText == 'Dislike' ? 'You don\'t like this post':'Dislike' ;
if(isLike){
event.target.nextElementSibling.innerText = 'Dislike';
} else{
event.target.nextElementSibling.innerText = 'Like';
}
});
});
Controller:
public function postLikePost(Request $request){
$post_id = $request['postId'];
$is_like = $request['isLike'] === 'true';
$update = false;
$post = Post::find($post_id);
if(!$post){
return null;
}
$user = Auth::user();
$like = $user->likes()->where('post_id', $post_id)->first();
if($like){
$already_like = $like->like;
$update = true;
if($already_like == $is_like){
$like->delete();
return null;
}
}else{
$like->like = $is_like;
$like->post_id = $post_id;
$like->user_id = $user_id;
}
// This is working when the controller is updating but broken when saving
if($update){
$like->update();
} else{
$like->save();
}
return null;
}
P.S:
I'm new to phpstorm and laravel, if you know a good way to debug/log/watch variable values like in eclipse, that would be appreciated.
from what I see the ajax request is using a wrong url it's requesting localhost/public/likes instead of localhost/your_project/public/like
$.ajax({
method: 'POST',
url: 'like',
data: {isLike: isLike, postId: postId, _token: token}
})
should work... or
<script>
var urlLike = '{{ URL::to('like') }}';
</script>
not sure why route() is not working though

AJAX throws error 500 only when PHP function is empty

I'm completely puzzled to why this happens, I've been messing on this for a few hours and I'm going crazyyyy! I am trying to update my DB when a checkbox is toggled on or off. The success response works if my PHP function I'm calling is empty, but fails whenever I add PHP. Note I'm on Laravel 3, and I've tried enabling or disabling CSRF filtering, no luck.
My JS:
$seenTD = $('td.seen_by_user');
$seenTD.each(function() {
$this = $(this);
var $seenLabel = $this.find('label');
var $seenInput = $this.find(':checkbox');
$seenInput.change(function() {
var _csrf = $('input[name="csrf_token"]').val();
var chkName = $(this).attr('name');
var checkVal = $(':checkbox[name='+chkName+']').prop('checked'); //true or false
var id = $this.find('input[name="reminder_id"]').val();
$.ajax({
url: 'update',
type: 'POST',
data: 'seen='+checkVal+'&reminder_id='+id+'&csrf_token='+_csrf,
success: function(data) {
console.log(data);
if($seenInput.is(':checked')) {
$seenLabel.removeClass('unchecked').addClass('checked');
$seenLabel.find('span').text('Oui');
}
else {
$seenLabel.removeClass('checked').addClass('unchecked');
$seenLabel.find('span').text('Non');
}
}
});
});
});
My PHP
public function post_update() {
$request = Request::instance();
$content = $request->getContent();
$id = $content['id'];
$seen = $content['seen'];
if($seen == 'true') {
$seen = 1;
}
if($seen == 'false') {
$seen = 0;
}
DB::table('reminders')->where('id', '=', $id)->update(
array(
'seen_by_user' => $seen
));
}
For the sake of maybe helping someone, as this is my first working AJAX, I'll explain how I got it to work, as well as supply working code. I'm not claiming this is the best way to do it, so if anyone has their word to say, don't hesitate :)
There were multiple issues, from Javascript insconsistency returning the row ID I needed for the database update, to the PHP function, and the way I was grabbing the POST data.
To get it to work, I played on Fiddler, retrieved the error message that Laravel throws at me. And I could debug from there :)
My working code is :
JS:
$('td.seen_by_user :checkbox').change(function() {
$this = $(this);
var $label = $this.siblings('label');
var id = $this.attr('data-id');
var _csrf = $this.siblings('input[name="csrf_token"]').val();
var value = $this.prop('checked');
$.ajax({
url: 'update',
type: 'POST',
data: {"seen_by_user": value, "id": id, "csrf_token": _csrf},
success: function(data) {
if($this.is(':checked')) {
$label.removeClass('unchecked').addClass('checked');
$label.find('span').text('Oui');
}
else {
$label.removeClass('checked').addClass('unchecked');
$label.find('span').text('Non');
}
}
});
});
PHP
function post_update() {
$id = $_POST['id'];
$seen = $_POST['seen_by_user'];
if($seen == 'true') {
$seen = 1;
}
if($seen == 'false') {
$seen = 0;
}
$update_reminder = DB::table('reminders')->where('id', '=', $id)->update(
array('seen_by_user' => $seen));
}
And my HTML (Blade Template from Laravel, where {{ }} brackets are simply echo's, and #foreach is a )
#foreach ($reminders as $reminder)
...
<td class="seen_by_user">
<form class="ajax" action="update" method="POST">
{{ Form::token() }}
{{ Form::checkbox('seen_'.$reminder->id, 1, $reminder->seen_by_user, array('id' => 'seen_'.$reminder->id, 'data-id' => $reminder->id)) }}
<label class="seen {{ ($reminder->seen_by_user == 1 ? 'checked' : 'unchecked' ) }}"for="{{ 'seen_'.$reminder->id }}"><i class="read"></i><span>{{ ($reminder->seen_by_user == 1 ? 'Oui' : 'Non') }}</span></label>
</form>
</td>
...
#endforeach
data should be an object like this
data: {"seen": checkVal, "reminder_id": id, "csrf_token": _csrf},
The $.ajax method will take care of the presentation and transmission.

Categories