Hi,
I am stuck trying to create some kind of “Like-Button” – a button which any visitor can click on and which increases a counter, which is then saved to its page’s content, so the raised value is saved permanently until re-raised again. Unlike the familiar Like-button the one I am aiming for is not limited to “+1 / user” but can be raised repeatedly and endlessly.
Very important would be, that the value increase is displayed and saved immediately without page-reloading.
I looked at the following instructions, but could not figure out how to do it:
I wonder if it is possible to save a page from the frontend?
I have some user input related to content stored in a structure field. The user should select the existing content (via ) und save his entry without access to the panel.
https://forum.getkirby.com/t/add-method-to-append-to-structure-field/494
I am a beginner (especially when it comes to php functions and kirby controllers) so I would be really happy about any kind of lofi solution.
Cheers
Jens
Without page reloading always mean a JavaScript solution.
Basically,
user clicks button
your JS calls a route
in your route, you update the page ($page->update()
) and return the new value
your script inserts the new value in DOM
Can get tricky if the user hits the button a thousand times very fast, I guess and many users do that at the same time.
Thank you for your quick response, I will try out your suggestion!
Thanks again, I’m super excited, it seems to be working now!
And this is what the relevant parts looks like so far:
Blueprint article.yml
fields:
likes:
label: Likes
type: number
default: 0
min: 0
step: 1
before: ♥
Snippet like-button.php
<div class="like-button" data-slug="<?= $data->slug() ?>">
<span class="like-icon">♥</span>
<span class="like-value"><?= $data->likes()->html() ?></span>
</div>
config.php // updated with $page->increment()
'routes' => [
[
'pattern' => 'articles/(:any)/like',
'action' => function ($slug) {
if ($page = page('articles')->find($slug)) {
$counter = $page->increment('likes',1)->content()->get('likes');
$page->update([ 'likes' => $counter ]);
return strval( $counter );
}
}
]
]
jQuery
$(document).ready(function(){
$('.like-button').click(function(evt){
evt.stopPropagation();
evt.preventDefault();
var myVal = parseInt( $(this).find('.like-value').first().html() );
var mySlug = $(this).data('slug');
$(this).find('.like-value').first().load("/articles/" + mySlug + "/like", function(responseTxt, statusTxt, xhr){
if(statusTxt == "success")
console.log("External content loaded successfully!");
if(statusTxt == "error")
alert("Error: " + xhr.status + ": " + xhr.statusText);
});
});
});
If anyone sees any room for improvement I would be happy to here about it!
1 Like
Let me introduce you to the magic increment()
method
1 Like
Thank you for your suggestion! Apparently I still have to “get” the field value after incrementing it, otherwise the variable $counter seems to contain the page’s slug value.
$counter = $page->increment('likes',1)->content()->get('likes');
Could you try like that:
$counter = $page->increment('likes')->likes()->toInt();
One more addition:
It is necessary to use ->impersonate()
so any visitor can trigger the ->increment()
and ->update()
from the front-end.
So the route in config.php looks like this now:
[
'pattern' => 'articles/(:any)/like',
'action' => function ($slug) {
if ( $page = page('articles')->find($slug) ) {
if ( $page->likes()->exists() ) {
$kirby = kirby();
$kirby->impersonate('kirby');
//$counter = $page->increment('likes',1)->content()->get('likes');
$counter = $page->increment('likes')->likes()->toInt();
$page->update([ 'likes' => $counter ]);
return strval( $counter );
} else {
return 'Error';
}
}
}
],
1 Like
Yes, you need impersonate()
to page update (increment method works with update
too)
An alternative solution, you can give permission to specific or all roles in article.yml
like that:
title: Article
options:
update: true
1 Like
texnixe
September 9, 2019, 10:53am
10
That doesn’t help if you do stuff programmatically. You still need to authenticate.
1 Like
Guest
is role that included *: true
?
1 Like
texnixe
September 9, 2019, 11:22am
12
What is the guest
role? Not sure I get what you are asking?