Like page (photo) for logged in user

Recommended Posts

Hi all,

Im working on a photography website where users can comment and like on photos (which are pages). I solved the comment part with the comment module, but I also want a 'like' system. Which includes;

- Like a photo. Like count on the photo adds up.
- If user has a few favourite photos, i would like a 'Favourites' section on the user profile page, with all of the liked photos.
- Apart from the likes, i would like to have a page 'Most liked photos of this week', so each week the community can see the photos with the most likes that week.

I came across the Like plug-in, but i would like to have a little more tailored solution and keep it to logged-in users only and be able to see all of the liked photos of a user.

Any idea how to keep track of the likes and build a 'Most liked this week' page?
Help would be appreciated!


Share this post

Link to post
Share on other sites

hi sirhc,

you could create a page reference field on your photo-template and when a user clicks on like you can add this user to the pagefield. if you have lots of users (likes) that could cause some problems on the page edit screen i guess, but if you set the field to hidden it should work.

like count is as easy as counting referenced pages (users).

show all liked photos would be an easy selector like "template=photo,likes=youruserid"


most likes this week... hmmm... that's not possible with the setup above. then maybe it's better to create 2 new templates:

1) likes (just the parent for all likes)
2) like (pagefield referencing the photo, user that liked)

then you can create a new "like" page whenever a user likes a photo and then you can also count all likes of that week: $pages->count("template=like,photo=yourphotoid,created>xxx,created<yyy");

and counting all likes for one photo would be even easier: $pages->count("template=like,photo=yourphotoid");

  • Like 1

Share this post

Link to post
Share on other sites

I would have taken another way. 

As each photo are Page, he could create a module which work with a custom MySQL table where he update the like of a page with some informations aside, like the userID (the user who like the page), the pageID (the page being liked) , a likeStatus (like or unliked) and a timestamp.


I made a small module to show the idea :



<?php namespace ProcessWire;

class LikeSystem extends WireData implements Module {

    const minVersion = '3.0.0';
    protected $tableName = 'likes_system';

    public static function getModuleInfo()
        return [
            'title' => 'LikeSystem',
            'version' => "0.0.1",
            'summary' => 'Like System for ProcessWire.',
            'author' => 'flydev',
            'href' => '',
            'singular' => false,
            'autoload' => false,
            'icon' => 'thumbs-up',
            'requires' => [

    protected function createTable() {

        $sql =  "CREATE TABLE IF NOT EXISTS " . $this->tableName . " ( " .
                "id int unsigned NOT NULL AUTO_INCREMENT, " .
                "userId int unsigned NOT NULL, " .
                "pageId int unsigned NOT NULL, " .
                "likes boolean NOT NULL," .
                "ts timestamp NULL DEFAULT CURRENT_TIMESTAMP, " .
                "PRIMARY KEY (id) " .
        $sql = "CREATE UNIQUE INDEX userId_pageId ON " . $this->tableName . " ('userId', 'pageId');";


    public function ___install() {

        if (version_compare($this->config->version, self::minVersion, '<'))
            throw new WireException("This module requires ProcessWire " . self::minVersion . " or newer.");


    public function ___uninstall() {
        $sql = "DROP TABLE IF EXISTS " . $this->tableName;

    public function init() {
        // check if the button 'like' as been clicked
        if(wire('input')->post->likethispage) {
            $status = (wire('input')->post->likethispage === 'like') ? 1 : 0;

    // Like or unlike a page
    protected function likePage($status) {
        $userId = wire('user')->id;
        $sql = "INSERT INTO " . $this->tableName . "(userId, pageId, likes) VALUES ('".$userId."', '".wire('page')->id. "', '" . $status . "')
                ON DUPLICATE KEY UPDATE 
                likes = $status,
                ts = CURRENT_TIMESTAMP;";

    // get the total of like of $pageId or current page
    public function getTotal($pageId = '') {
        $pageId = empty($pageId) ? wire('page')->id : $pageId;
        $sql = "SELECT pageId FROM " . $this->tableName . " WHERE pageId = '" . $pageId . "' and likes = '1';";
        $res = $this->database->query($sql);
        $row = $res->fetchAll();
        return count($row);

    // get the most liked page, if $limit is defined, it return the N most liked page
    public function getMostLikedPage($limit = 5) {
        $sql = "SELECT pageId, count(pageId) as likesCount FROM " . $this->tableName .
               " WHERE likes='1' GROUP BY pageId ORDER BY likesCount DESC LIMIT $limit;";
        $res = $this->database->query($sql);
        $row = $res->fetchAll();

        return $row;

    public function render() {

        $modules = wire('modules');

        $sql = "SELECT pageId, likes FROM " . $this->tableName . " WHERE pageId = '" . wire('page')->id . "' and userId = '". wire('user')->id ."';";
        $res = $this->database->query($sql);
        $row = $res->fetch();
        $label = 'like';
        if(count($row)) {
            if($row['likes'] == 1) $label = 'unlike';

        $form = $modules->get("InputfieldForm");
        $form->action = './';
        $form->method = 'post';
        $form->attr('id+name', 'likeSystemForm');

        $field = $modules->get("InputfieldSubmit");
        $field->value = $label;
        $field->icon = "thumbs-up";
        $field->attr('id+name', 'likethispage');


        return $form->render();



then in the frontend, you can render a 'like' button on choosen templates, and finaly get the total number of likes of a page and the most liked page, see:


<?php namespace ProcessWire;

$likesmod = $modules->get('LikeSystem');
// render a 'like' button
$content = $likesmod->render();
// total like of the page
$content .= "Number of likes for this page: " . $likesmod->getTotal($page->id);
// most liked page
$limit = 1
$mostliked = $likesmod->getMostLikedPage($limit);
$content .= "<br>Most liked page: " . $pages->get($mostliked[0]['pageId'])->title . "(" . $mostliked[0]['pageId'] . ") " . " (N likes: ". $mostliked[0]['likesCount'] . ")";


Edited by flydev
code typo
  • Like 5

Share this post

Link to post
Share on other sites
7 hours ago, bernhard said:

most likes this week... hmmm... that's not possible with the setup above.

There is the Page Reference with Date Field module that might be useful for this sort of thing. I've never used it and the module page doesn't indicate that it is compatible with PW3.

But the real problem with using Page Reference fields for likes or any purpose that could potentially use thousands of pages is that there is no pagination or limiting possible when getting the field value. So it's not just a matter of dealing with the inputfield in Page Edit but also the impact on memory when getting the value via the API.

There's an open request about this but no sign of activity.

  • Like 2

Share this post

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By ridgedale
      Reference: PW 3.0.111 and uikit3 based site using the Regular-Master profile.
      I'm trying to automatically redirect a logged-in user to a custom profile page using $session->redirect() and need to add $user->name to the redirect path.
      All my attempts appear to have failed:
      $session->redirect('/user-profile/')->name; $session->redirect('/user-profile/')->$user->name; $session->redirect('/user-profile/' . get($user->name . '/')); $session->redirect('/user-profile/' & get($user->name)); Can anyone point out where I am going wrong?
    • By matsn0w
      Hey all,
      I am working on a website and I want to style the login page, but I'm a bit confused. 
      I want either the existing login page styled in my own way using some CSS (I guess I prefer that) or I want to create a custom page with a form to login. (Which I could style too).
      I used the code from Ryan and Renobird posted here - which works great - but that doesn't replace the original login page. 
      Is there a way to some sort of 'disable' the original login?
      I hope my question is clear and thanks in advance,
    • By louisstephens
      So, I really don't know where to begin with this. I have the thought in my head (the basics), but not really sure how to set it up.
      The desired effect:
      The homepage is set up with a foreach loop that outputs all the child pages (basically just a bunch of pages with pdfs, docs etc stored in a file field). When A user is actually logged in, they can "star" an item or drag and drop (using Dragula) the page to a sidebar. Also, they would be able to reorder the pages in the sidebar.
      The question(s):
      I guess I have multiple questions regarding how to achieve this.
      Would it be best to create a page field in the user template to save the page(s)? How would you actually save the page state (sidebar output, reordering, etc) once the drag and drop has been used? Thanks in advance for any help! A lot of this was just what was in my head, so it might not make full sense. However, I will update this if I can actually wrap my head around everything.
    • By Lex Sanchez
      Hi everyone:
      I do not know if someone before using ProcessWire with AWS CloudFront, currently I have problems with the login, it does not work for any reason, when I check in the logs generated by ProcessWire, it only indicates This request was aborted because it appears to be forged. (in /wire/core/SessionCSRF.php line 190).
      I have allowed CloudFront to forward all headers, cookies and allow all methods (GET, POST, PUT).
      When I perform the same process from the ip server if it works or from the balancer.
    • By flydev
      OAuth2Login for ProcessWire
      A Module which give you ability to login an existing user using your favorite thrid-party OAuth2 provider (i.e. Facebook, GitHub, Google, LinkedIn, etc.)..
      You can login from the backend to the backend directly or render a form on the frontend and redirect the user to a choosen page.
      Built on top of ThePhpLeague OAuth2-Client lib.
      Registration is not handled by this module but planned.
      Howto Install
      Install the module following this procedure:
      Next step, in order to use a provider, you need to use Composer to install each provider
      ie: to install Google, open a terminal, go to your root directory of pw and type the following command-line: composer require league/oauth2-google
      Tested providers/packages :
          Google :  league/oauth2-google     Facebook: league/oauth2-facebook     Github: league/oauth2-github     LinkedIn: league/oauth2-linkedin
      More third-party providers are available there. You should be able to add a provider by simply adding it to the JSON config file.

      Howto Use It
      First (and for testing purpose), you should create a new user in ProcessWire that reflect your real OAuth2 account information. The important informations are, Last Name, First Name and Email. The module will compare existing users by firstname, lastname and email; If the user match the informations, then he is logged in.
      ie, if my Google fullname is John Wick, then in ProcessWire, I create a new user  Wick-John  with email
      Next step, go to your favorite provider and create an app in order to get the ClientId and ClientSecret keys. Ask on the forum if you have difficulties getting there.
      Once you got the keys for a provider, just paste it into the module settings and save it. One or more button should appear bellow the standard login form.
      The final step is to make your JSON configuration file.
      In this sample, the JSON config include all tested providers, you can of course edit it to suit your needs :
      { "providers": { "google": { "className": "Google", "packageName": "league/oauth2-google", "helpUrl": "" }, "facebook": { "className": "Facebook", "packageName": "league/oauth2-facebook", "helpUrl": "", "options": { "graphApiVersion": "v2.10", "scope": "email" } }, "github": { "className": "Github", "packageName": "league/oauth2-github", "helpUrl": "", "options": { "scope": "user:email" } }, "linkedin": { "className": "LinkedIn", "packageName": "league/oauth2-linkedin", "helpUrl": "" } } }  
      Backend Usage
      In ready.php, call the module :
      if($page->template == 'admin') { $oauth2mod = $modules->get('Oauth2Login'); if($oauth2mod) $oauth2mod->hookBackend(); }  
      Frontend Usage
      Small note: At this moment the render method is pretty simple. It output a InputfieldForm with InputfieldSubmit(s) into wrapped in a ul:li tag. Feedbacks and ideas welcome!
      For the following example, I created a page login and a template login which contain the following code :
      <?php namespace ProcessWire; if(!$user->isLoggedin()) { $options = array( 'buttonClass' => 'my_button_class', 'buttonValue' => 'Login with {provider}', // {{provider}} keyword 'prependMarkup' => '<div class="wrapper">', 'appendMarkup' => '</div>' ); $redirectUri = str_lreplace('//', '/', $config->urls->httpRoot . $page->url); $content = $modules->get('Oauth2Login')->config( array( 'redirect_uri' => $redirectUri, 'success_uri' => $page->url ) )->render($options); }
      The custom function lstr_replace() :
      /* * replace the last occurence of $search by $replace in $subject */ function str_lreplace($search, $replace, $subject) { return preg_replace('~(.*)' . preg_quote($search, '~') . '~', '$1' . $replace, $subject, 1); }