Jump to content
David Karich

Page Hit Counter – Simple Page View Tracking

Recommended Posts

The Page Hit Counter module for ProcessWire implements a simple page view counter in backend. Page views of visitors are automatically tracked on defined templates, with monitoring of multiple page views. This gives you a quick overview of how many visitors have read a news or a blog post, for example, without first having to open complex tools such as Google Analytics. This module quickly provides simple information, e.g. for editors. Or, for example, to sort certain news by most page views. For example for "Trending Topics".

pagehitcounter-example.thumb.png.597a4d8a40d79d6401cd6459631f535b.png
 

Works with ProCache and AdBlockers. With a lightweight tracking code of only ~320 bytes (gzipped). And no code changes necessary! In addition GDPR compliant, since no personal data or IP addresses are stored. Only session cookies are stored without information. 

In addition, there are some options, for example filtering IP addresses (for CronJobs) and filtering bots, spiders and crawlers. You can also configure the lifetime of the session cookies. Repeated page views are not counted during this period. It is also possible to exclude certain roles from tracking. For example, logged in editors who work on a page are not counted as page views.

pagehitcounter-config-example.thumb.png.0bae24959b8263681a53a0108fe5e7e2.png

Sort by hits and access page views (hit value)

Each trackable template has an additional field called phits. For example, you want to output all news sorted by the number of page views.

// It is assumed that the template, e.g. with the name "news", has been configured for tracking.
$news = $pages->find("template=news, sort=-phits");

To output the page views of a tracked page, use:

echo $page->phits;

Example: Tracking a page hit via API and jQuery

If you want to track a template that does not represent a full page to automatically inject a tracking script, you can define allowed API templates in the module that you can track. Below is an example of how you can track a click on news tag using jQuery. This will allow you to find out which keywords are clicked the most. For example, you can sort and display a tag cloud by the number of hits. Suppose your keywords have the template "news_tag". The template "news_tag" was also configured in the Page Hit Counter Module as a trackable API template.

Example PHP output of keywords / tags:

// Required: the data attribute "data-pid" with the ID of the template to be tracked.
echo $pages->find("template=news_tag, sort=-phits")->each("<a href='{url}' class='news_tag' data-pid='{id}'>{title}</a>");

Example Tracking Script with jQuery:

/**
 * Required: Data attribute "data-pid" with the ID of the news tag template
 * Required: Send the POST request to the URL "location.pathname.replace(/\/?$/, '/') + 'phcv1'"
 * Required: The POST parameter "pid" with the ID of the template
 */
$(function(){
    if($('a.news_tag').length > 0) {
        $('a.news_tag').each(function(){
            var tPID = $(this).data("pid");
            if(tPID) {
                $(this).on("click", function(){
                    $.post(location.pathname.replace(/\/?$/, '/') + 'phcv1', {pid: tPID});
                });
            }
        });
    }
});

So simply every click on a tag is counted. Including all checks as for automatic tracking. Like Bot Filtering, Session Lifetime, etc.

_______________________________________________________

Background: This module is the result of a customer requirement, where the editors are overwhelmed with analytics or no tracking tools were allowed to be used. However, a way had to be found to at least count page views in a simple form for evaluations. Furthermore, by using ProCache, a way had to be found to count views of a page without clearing the cache.

_______________________________________________________

Pros

  • Automatic Page View Tracking
  • Lightweight tracking code, only ~320 bytes (gzipped)
  • No code or frontend changes necessary
  • Works with ProCache! Even if no PHP is executed on the cached page, the tracking works
  • Works with browser AdBlockers
  • No cache triggers (for example, ProCache) are triggered. The cache remains persistent
  • GDPR compliant, session-based cookie only, no personal information
  • Filtering of IPs and bots possible
  • Exclude certain roles from tracking
  • Ability to reset Page Views
  • Works with all admin themes
  • Counter database is created as write-optimized InnoDB
  • API to track events for templates that are not viewable
  • No dependencies on libraries, pure VanillaJS (Automatic tracking script)
  • Works in all modern browsers
  • Pages are sortable by hits

Cons

  • Only for ProcessWire version 3.0.80 or higher (Requires wireCount())
  • Only for PHP version 5.6.x or higher
  • No support for Internet Explorer <= version 9 (Because of XMLHttpRequest())
  • No historical data, just simple summation (Because of GDPR)

Planned Features / ToDos

  • API access to hit values Since version 1.2.1
  • Possibility to sort the pages by hits (Request by @Zeka) Since version 1.2.0
  • Don't track logged in users with certain roles (Request by @wbmnfktr) Since version 1.1.0
  • Possibility to reset the counter for certain pages or templates (Request by @wbmnfktr) Since version 1.1.0
  • Better bot filter Since version 1.1.0
  • Disable session lifetime, don't store cookies to track every page view (Request by @matjazp) Since version 1.2.1
  • Option to hide the counter in the page tree (Request by @matjazp) Since version 1.2.1
  • Option to hide the counter in the page tree on certain templates Since version 1.2.1
  • API to track events for templates that are not viewable Since version 1.2.2

Changelog
1.2.3

  • Bug-Fix: Tracking script triggers 404 if pages are configured without slash (#3) Reported by @maxf5
  • Enhancement: Reduction of the tracking script size if it's gzipped (~320 bytes)
  • Enhancement: Documentation improvement
  • Enhancement: Corrected few typos

1.2.2

  • New feature: API to track events for templates that are not viewable
  • Enhancement: Documentation improvement

1.2.1

  • API access to hit values Use $page->phits
  • Bug-Fix: No tracking on welcomepage (Reported by wbmnfktr; Thx to matjazp)
  • Bug-Fix: Tracking script path on subfolders (Reported by matjazp)
  • Bug-Fix: Tracking on pages with status "hidden"
  • Enhancement: Change database engine to InnoDB for phits field
  • Enhancement: Option to disable session lifetime set session lifetime to 0, no cookies
  • Enhancement: Better installation check
  • Enhancement: AJAX Request asyncron
  • Enhancement: Reduction of the tracking script size by ~20%
  • Enhancement: Option to hide the counter in the page tree You can output the counter with the field name "phits"
  • Enhancement: Option to hide the counter in the page tree on certain templates
  • Enhancement: Option for activate general IP validation
  • Enhancement: Reduction of tracking overhead up to ~30ms
  • Enhancement: Better bot list for detection

1.2.0

  • New feature: Sort pages by hits – New field phits
  • Migrate old counter data to new field

1.1.0

  • New feature: Exclude tracking of certain roles
  • New feature: Reset Page Views
  • Better bot filter and detection

1.0.0

  • Initial release

Notes
By default, the page views are stored as INT in the database. This allows a maximum counter value of 4.2 billion views (4,294,967,295) per page. If you need more, change the type to BIGINT directly in the database. But I recommend to use Google Analytics or similar tools if you have such a large number of users.

_______________________________________________________

Download GitHub: ProcessWire Page Hit Counter (Version 1.2.3)
PW Module Directory: ProcessWire Page Hit Counter (Version 1.2.3)
Install via ProcessWire (Classname): PageHitCounter

_______________________________________________________

Update information
If you have used version 1.2.1 from the DEV branch, please replace it completely with the new master version.

  • Like 13
  • Thanks 3

Share this post


Link to post
Share on other sites
2 minutes ago, Zeka said:

@David Karich Great module. Just curious is it possible to sort pages by page views?

Thank you. Not yet, because the data is stored in an extra table.  But I will write the possibility for a future version on the ToDo-List. 🙂 

  • Like 1

Share this post


Link to post
Share on other sites
31 minutes ago, David Karich said:

Thank you. Not yet, because the data is stored in an extra table.  But I will write the possibility for a future version on the ToDo-List. 🙂 

This module looks very cool, thank you!

I've built a similar solution (although simpler in the config side) where I run a cronjob daily to update the count on a field on each page. It works like a charm, but of course, there are other ways. 

  • Like 1

Share this post


Link to post
Share on other sites
8 minutes ago, Sergio said:

This module looks very cool, thank you!

I've built a similar solution (although simpler in the config side) where I run a cronjob daily to update the count on a field on each page. It works like a charm, but of course, there are other ways. 

I was just considering this approach, but it did not meet the requirements in my case. It should be live data, due to quick reactions at hot topics of the editors. And since some templates of the page in the ProCache have lifetimes of more than a week or more, an update of pages with a daily CronJob would also delete the cache unnecessarily. But as you already say, many approaches for very special requirements. 🙂

  • Like 1

Share this post


Link to post
Share on other sites

Wow... that's indeed really nice.

Two things came up while playing around with it.

  1. Is it planned to exclude logged-in users from tracking?
    I know that several of my clients publish new content and therefore check the new site over and over again.
    Right now they would create page counts that are kind of fake.
  2. Is it planned to reset all or some counters?
    Right now I created ~50 page counts and would like to reset those to zero again.

 

  • Like 2

Share this post


Link to post
Share on other sites
23 hours ago, wbmnfktr said:

Wow... that's indeed really nice.

Two things came up while playing around with it.

  1. Is it planned to exclude logged-in users from tracking?
    I know that several of my clients publish new content and therefore check the new site over and over again.
    Right now they would create page counts that are kind of fake.
  2. Is it planned to reset all or some counters?
    Right now I created ~50 page counts and would like to reset those to zero again.

 

Good suggestions. 👌 Both functionalities are implemented in the current Dev branch. You can test them here: https://github.com/FlipZoomMedia/PageHitCounter/tree/dev

screenshot-localhost-2019_01.16-16-27-07.thumb.png.ffa4991e3efa202d74e2b3191f14f246.png

  • Like 2

Share this post


Link to post
Share on other sites

Quick update: 

  1. excluding specific roles works as expected 🙂 
  2. resetting page counts works as expected, too 🙂 

 

But... it won't count visits on the homepage.

Tried the prior version as well with the same result.

Dev console shows Page Hit Counter: View was counted

Share this post


Link to post
Share on other sites
1 hour ago, wbmnfktr said:

Quick update: 

  1. excluding specific roles works as expected 🙂 
  2. resetting page counts works as expected, too 🙂 

 

But... it won't count visits on the homepage.

Tried the prior version as well with the same result.

Dev console shows Page Hit Counter: View was counted

Do I understand this correctly: A counter is displayed and tracked for your homepage template even though you have not configured the template? Or do you use a template for your homepage which is also used for other pages?

Also a quick update: Version 1.2.0 with Sortable selector

Version 1.2.0 is now available on the Dev-Branch, which makes it possible to sort the pages by hits. Please check the upgrade for test environments first, because the DB structure will be changed. Previously tracked data will be taken over, a new fieldtype will be installed. Simply replace all files and trigger a module refresh in the PW.

Infos and download: https://github.com/FlipZoomMedia/PageHitCounter/tree/dev

  • Like 1

Share this post


Link to post
Share on other sites
1 minute ago, David Karich said:

Do I understand this correctly: A counter is displayed and tracked for your homepage template even though you have not configured the template? Or do you use a template for your homepage which is also used for other pages?

No... not quite, yet.

In the module settings I enabled the homepage template (home).

First try: The homepage page got a counter badge but counts didn't appear. It stayed and stays at zero.

Second try: Tried another template on the homepage but the counter stays at zero. Other pages with that template work as expected.

Share this post


Link to post
Share on other sites
17 minutes ago, wbmnfktr said:

No... not quite, yet.

In the module settings I enabled the homepage template (home).

First try: The homepage page got a counter badge but counts didn't appear. It stayed and stays at zero.

Second try: Tried another template on the homepage but the counter stays at zero. Other pages with that template work as expected.

All right. I'll take a look and check. 👍

  • Like 1

Share this post


Link to post
Share on other sites

Hi, Thanks for this module. Nice and organized code!

is there an option to disable session lifetime, like set it to 0? The minimal value is 1 due to how you check for 0.

I see you are sending ajax requests to location.pathname.substring(1) + "/phcv1" but at least on my localhost location.pathname is /somepage/ so ajax is sent to http://localhost/mypage/mypage//phcv1 Shouldn't you use just location.pathname + "phcv1"

Counter on the home page isn't working as you are passing an int to ctype_digit() function which expects a string. What I find odd is that ctype_digit(1) returns false while ctype_digit(1054) returns true. I'm not sure if ctype_digit is actually needed.

The downside of this script is that sending ajax request put some load to the server itself and at least one findOne(). I'm not sure I like counters on the page list, would prefer table layout somewhere else.

Share this post


Link to post
Share on other sites

Looked into it again... 

Good news!

With version 1.2.1 (from dev tree) hits on the homepage will be counted as well.

 

Share this post


Link to post
Share on other sites

1.2.1 is working fine, thanks @David Karich . I'm testing reset page views and putting template=basic-page in the selector, it returns a wrong number of pages. Or perhaps you are showing the number of pages that have counter different (larger) than 0?

Share this post


Link to post
Share on other sites
On 1/16/2019 at 8:18 PM, wbmnfktr said:

No... not quite, yet.

In the module settings I enabled the homepage template (home).

First try: The homepage page got a counter badge but counts didn't appear. It stayed and stays at zero.

Second try: Tried another template on the homepage but the counter stays at zero. Other pages with that template work as expected.

This problem has now been fixed in version 1.2.1 (master). Thanks for the report.

On 1/16/2019 at 8:59 PM, matjazp said:

Hi, Thanks for this module. Nice and organized code!

is there an option to disable session lifetime, like set it to 0? The minimal value is 1 due to how you check for 0.

I see you are sending ajax requests to location.pathname.substring(1) + "/phcv1" but at least on my localhost location.pathname is /somepage/ so ajax is sent to http://localhost/mypage/mypage//phcv1 Shouldn't you use just location.pathname + "phcv1"

Counter on the home page isn't working as you are passing an int to ctype_digit() function which expects a string. What I find odd is that ctype_digit(1) returns false while ctype_digit(1054) returns true. I'm not sure if ctype_digit is actually needed.

The downside of this script is that sending ajax request put some load to the server itself and at least one findOne(). I'm not sure I like counters on the page list, would prefer table layout somewhere else.

19 hours ago, matjazp said:

1.2.1 is working fine, thanks @David Karich . I'm testing reset page views and putting template=basic-page in the selector, it returns a wrong number of pages. Or perhaps you are showing the number of pages that have counter different (larger) than 0?

All in version 1.2.1 (Master)

  • You can now set the session lifetime to 0, which will disable cookies and count each page view
  • Thanks for the bug report, the problem with location.pathname is fixed
  • The problem was in fact the ctype test. Good found, thanks and fixed
  • I have optimized the request a bit, the overhead is reduced
  • I've added an option that lets you hide the counters. By accessing the field "phits" you can make your own output according to your ideas
  • This is correct, only counters are reset which already have a value greater than 0. I have formulated this differently in the descriptions and in the output to make it clearer and more understandable

Thanks to @matjazp and @wbmnfktr for testing and reporting. 👌

 

  • Like 2

Share this post


Link to post
Share on other sites
On 1/14/2019 at 4:44 PM, Zeka said:

@David Karich Great module. Just curious is it possible to sort pages by page views?

In version 1.2.1 (Master) it is now possible. Every trackable template has a field called "phits". You can use this field in selectors. Either to sort:

template=news, sort=-phits

or as a value selector, for example:

template=news, phits>=100

For an output in the frontend, simply access the field with:

$page->phits

This makes it very easy to output "trending topics", for example. Select news with a date field in a certain period and sort by "phits".

  • Like 2

Share this post


Link to post
Share on other sites

Version 1.2.2 as new master version available

In version 1.2.2 a new option was introduced which allows to send page hits to templates which are not directly viewable or cannot be tracked automatically. This allows you to implement tracking that best fits your environment. An example with jQuery can be found in the first post.

  • Like 3

Share this post


Link to post
Share on other sites

Hi Sir, thanks for the module - great addition!

I always get the 404 errors from the script and those pages dont hit into the counter.
Whats the deal with this "phcv1"?

image.png.763127e64637a4d59c519cb0fe8f01ca.png

  • Like 1

Share this post


Link to post
Share on other sites
29 minutes ago, maxf5 said:

Hi Sir, thanks for the module - great addition!

I always get the 404 errors from the script and those pages dont hit into the counter.
Whats the deal with this "phcv1"?

image.png.763127e64637a4d59c519cb0fe8f01ca.png

Thanks for the report. This is because your pages or templates don't end with a slash. I will add it as a bug. 👍 

The "/phcv1" is the request endpoint over which the AJAX requests pass to trigger a counter. In your case the URL should actually be "/produkte/phcv1".

Share this post


Link to post
Share on other sites
21 hours ago, maxf5 said:

Hi Sir, thanks for the module - great addition!

I always get the 404 errors from the script and those pages dont hit into the counter.
Whats the deal with this "phcv1"?

image.png.763127e64637a4d59c519cb0fe8f01ca.png

The problem is fixed in the current version 1.2.3. Thanks for the report.

Share this post


Link to post
Share on other sites

@David Karich , very useful, thanks! I mainly appreciate that there is no need to modify the template. Above you mention the ProCache module. Do I understand correctly that the counting will work correctly even with the standard PW template cache mechanism?

One helpful feature would be the ability to display the page hits in the page tree only for given roles, that is using permissions. The reason behind is not secrecy but cleanliness, no clutter.

Regards!

~loukote

  • Like 2

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.


  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By Harmen
      I want to add a few pages to an AsmSelect Page field inside a repeater using the following code:
      $trialsPage = wire("pages")->get(28422); // Get the page $trialsPage->of(false); $newTrial = $ordersPage->trial_repeater_orders->getNewItem(); // Add item to repeater foreach ($selectedProducts as $selectedProduct){ $productPage = $pages->get("template=product, reference=$selectedProduct"); $newTrial->trial_selected_products->add($productPage); } $newTrial->save(); $trialsPage->save(); However, when I check the page where the field is located it doesn't have the new values as expected. The selected pages exist, the field is in the right location, made sure that the output formatting is turned off: $page->of(false); But it still doesn't work with a variable. No matter what I try, it doesn't work.
      It only works when I replace $selectedProduct with a hardcoded string. Am I doing something wrong here?
    • By louisstephens
      So I have a form, once completed, will create new pages. All in all, this is eazy-peezy for me now. I guess I need a bit of guidance on how to actually structure the rest of my code.  I thought I could just write a function (_func.php) and pass the fields to the function and let it do its' thing. However, I am kinda hitting a road block when I do it this way. 
      I currently am passing first name, last name, city, state (options field), and making pages based on the first/last names. I guess where I run into some issues is I am trying to check to see if the "page" already exists, and if it does, throw out an error:
      In the home template:
      if(isset( $_POST['submit'])) { $firstName =Trim (stripslashes($_POST['firstname'])); $lastName = Trim(stripslashes($_POST['lastname'])); $fullName = $firstName . $lastName; $city = Trim(stripslashes($_POST['city'])); $state = Trim(stripslashes($_POST['state'])); $lowerCaseName = strtolower($fullName); $people = $pages->find("template=person"); foreach ($people as $person) { $checkFirstName = $person->first_name; $checkLastName = $person->last_name; $checkFullName = $checkFirstName . $checkLastName; if ($checkFullName === $lowerCaseName) { echo "<p>" . "This person has already created a page. Please choose a different name." . "</p>"; } else { echo "hey"; processNewPerson(need_to_pass_person_details_to_function); } } // end foreach In _func.php:
      function processNewPerson($list) { $u = new Page(); $u->template = "person"; $u->parent = wire('pages')->get("/people/"); $u->title = ; $u->first_name = ; $u->last_name = ; $u->state = ; $u->city = ; $u->save(); $u->setOutputFormatting(false); } I am a little unsure of how to actually pass all the information to the template, as well as if this is even the best approach to do this. Would it make more sense to do this in a class, or keep it the way it is?
    • By quickjeff
      Hi Guys, I have seen some other threads that talk about this but nothing recent nor in line with what I need. 
       
      I have basically created a page in which I am tracking the number of views. I update the counter which is tied to a field I added to the page's template that I am tracking. The field is hidden from the website admin and only used for this data. 
       
      However, now I need to track it on a daily basis. I am thinking of keeping track of all time view total for 30 days and perhaps saving that and allowing the user to see the page views of last 7 days, each day as a number and then 30 days of data. This way if I want to build in reporting functionality I can. 
       
      What I need help with adding a date to the page_views field but also, knowing how many page_views for today, yesterday, day before etc. 
      Here is what I have so far and its working. Now I need to associate a date with every day. NOT every update of the page_views field. 
      if (!$user->hasRole('superuser')) { $page->page_views += 1; $page->of(false); $page->save('page_views'); $page->of(true); } echo $page->page_views; Any guidance, help, suggestions is absolutely appreciated. 
      Thanks!!!!
    • By quickjeff
      Hi Guys,
      Right now, I am using parents as breadcrumbs but I have pages under a parent page just to keep things organized. The pages are called in other areas of the site. 
      I am trying to create user path history breadcrumbs.
      Meaning: If the user clicked a link to the page, it will show the path he took. Has anyone done this? Would love to see who has before I dive in.
      Thanks. 
    • By sam-vital
      Hi,
      I'm creating a News/Updates section for a client and they would like the 3 most recent Updates previewed on the home screen. So this would be Title and Date Posted. These blog posts will be child pages of a News and Updates page, which is a child of the Home page. So it's like Home -> News -> Blog posts. I don't need this to be a nav menu, just something to show the newest updates.
      All help is appreciated 🙂
×
×
  • Create New...