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:
Updates the page data
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.
Hi @bastianallgeier ,
There is an option to update the page via frontend with $page->update(). This is great and works good.
In my case I use it to store signups from users for a birtdayparty into a structure-field. To add an item to the structurefield I have to get the whole field first, convert it to an array, appaned the new data to the array, convert back to yaml and then update the page.
it would be great to have something like:
$page->mystructure()->add(array( key => value));
(It would…
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?