This Craft CMS plugin helps you to have the site's and page's counter and statistics widgets and it works well for small to medium-sized websites.
This commercial plugin is available through the Craft plugin store.
Craft 5/Craft 4.13 or higher.
- Count site visits, visitors, and online users.
- Count page visits.
- Support GraphQL and Twig for fetching site and page statistics.
- Provide statistics widgets for user dashboards.
- Go to the plugin settings page
admin/counter/settings/general. - Change the
Register counter automaticallysetting to enable.- Enabling this setting will add a JavaScript script to the end of all site pages if the counter is enabled for the site, which sends a request to the backend on every visit.
- If you want to support outdated browsers like IE11 in your site's front end, enable
Support outdated browsersin plugin settings. Disabling this item makes requests faster and lighter on the front end.
- In the plugin settings, enable
Site Counterfor the sites on which you want to count visits.
- If your site is statically cached, add this to the end of your frontend pages:
{% do view.registerAssetBundle("vnali\\counter\\assets\\CounterAsset") %}
- If your site is not statically cached, you can call the count service directly in twig:
{% set fullUrl = craft.app.request.getAbsoluteUrl() %}
{% do craft.counter.count(fullUrl) %}
- If you count visits by calling the count service directly in Twig, you can enable
Prevent access to counter via HTTP requestin plugin settings. In this case, requests to the backend counter via HTTP requests are ignored. - When
Register counter automaticallyis enabled, we disable counting by calling thecount()service in Twig to prevent counting a visit twice. - Please ensure that you set the site's timezone in the general settings and the
Start of the weekin the plugin settings before using this plugin. Changing these settings may affect some pre-calculated statistics (for example, new visitors, maximum online users, page visit statistics, etc.).
-
Visits- It shows the number of visits in a given date range.
- You can enable
show visitorsto display visitors' data on the chart; this data will only appear if the chart interval is set to daily or less. - There is an option called Ignore visits interval. By selecting this option, the total number of visits will include those that are ignored by the visit interval setting.
-
Visitors- It shows the number of unique visitors in a given date range.
- This plugin calculates visitors on a daily basis, so the number of visitors is limited to the previous hour, the current hour, today, yesterday, or a specific day.
- The anonymized value of the visitor's IP is kept in the database by default. You can disable this option by setting the
anonymizeIpoption to false in the plugin config file.
-
Average Daily Visitors- It shows the average number of unique visitors in a given date range.
-
Online Visitors- It shows the number of online visitors based on the time you consider a visitor online after a visit.
- You can specify the time you consider a visitor online via
Online threshold in secondsin the widget setting.- This number can not be higher than the
Online threshold in secondsset in the plugin settings.
- This number can not be higher than the
- When you specify the site as
allin the widget setting, it counts a user who is online in site A and site B once.
-
Max Online- It shows the maximum number of online site visitors and when it happened in a given date range on a chart.
-
Top Pages- It shows a list of top visited pages in a given date range for a site.
-
Trending Pages- It shows a list of trending pages in a given date range and site.
- When the growth of the pages is displayed as a percentage, pages that did not receive any visits in the previous date ranges—such as yesterday, the past week, and the past month—are not shown.
-
Declining Pages- It displays a list of declining pages in terms of visits within a specified date range and site.
- If a page does not have a visit in the current date range -today, this week, this month, this year- it does not show up in the result.
- Obviously, this widget data can be more helpful when the current date range getting closer to the end -end of the day, end of the week, ....-
-
Not Visited Pages- You can see a list of pages that are not visited in a specified date range but visited earlier.
-
Pages Visits Statistics- By using this widget, you can view the latest statistics for every desired page of your site.
-
Next Visited Pages- This widget allows you to analyze which pages users visit after a specific page within a defined number of seconds.
- This widget requires a considerable amount of time to load, so it's best to apply it to shorter date ranges only on small to medium-sized websites.
- This widget allows you to analyze which pages users visit after a specific page within a defined number of seconds.
- The user preference for the
Week Start Dayonly affects thevisits,visitors,average daily visitors,online visitors, andmax onlinewidgets for thethisWeekdate range. It does not impact page-related widgets such asTop Pages,Trending Pages,Declining Pages,Not Visited Pages,Pages Visits Statistics, andNext Visited Pages. The calculations for this week and the previous week for page-related widgets are always based on the selectedstart of the weekspecified in the plugin settings.
You can fetch site and page statistics on your site via Twig or GraphQL.
- To be able to run GraphQL queries, your token needs to have the appropriate permission for the queried item, site, and timeframe.
- Make sure that you only enable needed items for a token.
- For example, if you want today's site visits counter for the front page of your primary site, you should limit the schema to querying
Site Visits,Todaydate range, andmainsite. -because the used token and schema are exposed to the end users in this case-.
- For example, if you want today's site visits counter for the front page of your primary site, you should limit the schema to querying
- We always pass a
targument with a unique value to prevent caching results. - We can pass a
debugMessagefield in test environments to obtain useful information from GraphQL results. You should allowquerying the debug messagein the schema.
{% set visits = craft.counter.siteVisits(dateRange, startDate, endDate, siteId, ignoreVisitsInterval) %}
- The supported date ranges are:
custom,thisHour,previousHour,today,yesterday,thisWeek,thisMonth,thisYear,past7Days,past30Days,past90Days,pastYear. - The
startDateandendDateonly should be set when the date range is set ascustom. - By passing
Ignore visits intervalas true, the output includes also visits that are ignored by the visit interval setting. The Default isfalse.- You can use this option to see how many visits happen in the
ignoreVisitsIntervalthreshold.
- You can use this option to see how many visits happen in the
{% set visits = craft.counter.siteVisits('today') %}
{% set visits = craft.counter.siteVisits('today', null, null, '*') %} // today site's visits for all sites
{% set visits = craft.counter.siteVisits('yesterday', null, null, '2') %}
{% set visits = craft.counter.siteVisits('thisHour') %}
{% set visits = craft.counter.siteVisits('past90Days', null, null, '5', true) %} // past 90 days site's visits for site id of 5 without ignoring visits interval
{% set visits = craft.counter.siteVisits('thisWeek') %}
{% set visits = craft.counter.siteVisits('cutsom', '2024-01-10', '2024-09-01', '*') %} // 2024-01-10 to 2024-09-01 site's visits for all sites
// visits for today for all sites. we pass a `t` argument with a unique value to prevent caching results.
{
counter(dateRange:"today", siteId: "*", ignoreVisitsInterval: true, t:timestamp) {
visits
debugMessage
}
}
// Visits for a custom range for the site with an ID of 2.
{
counter(dateRange:"custom", startDate:"2024-01-10", endDate:"2024-09-01", siteId: "2", t: timestamp) {
visits
debugMessage
}
}
{% set visitors = craft.counter.siteVisitors(dateRange, startDate, endDate, siteId) %}
- supported date ranges are:
thisHour,previousHour,today,yesterday, andcustom(only for one day). - startDate and endDate only should be set when the date range is set as
custom.
{% set visitors = craft.counter.siteVisitors('today') %}
{% set visitors = craft.counter.siteVisitors('today', null, null, '*') %}
{% set visitors = craft.counter.siteVisitors('yesterday', null, null, '2') %}
{% set visitors = craft.counter.siteVisitors('thisHour') %}
{% set visitors = craft.counter.siteVisitors('cutsom', '2024-01-10', '2024-01-10') %} // when custom is passed, only one day can be passed
- You can pass
debugMessageto get more information about errors.
//
{
counter(dateRange:"today", siteId: "*", t: timestamp) {
visitors
debugMessage
}
}
{
counter(dateRange:"custom", startDate:"2024-01-10", endDate:"2024-01-10", siteId: "2", t: timestamp) {
visitors
debugMessage
}
}
// Combine visits and visitors query:
{
counter(dateRange:"today", siteId: "*", t: timestamp) {
visits
visitors
debugMessage
}
}
// We can also combine visitors with visits since the custom date range is limited to one day (the visitors query does not support ranges longer than one day).
{
counter(dateRange:"custom", startDate:"2024-01-10", endDate:"2024-01-10", siteId: "2", t: timestamp) {
visits
visitors
debugMessage
}
}
- The supported date ranges are:
All,today,thisWeek,thisMonth,thisYear,past7Days,past30Days,past90Days,pastYear,custom.
{
counter(dateRange: "past7Days", siteId: "*") {
averageVisitors,
debugMessage
}
}
{% set online = craft.counter.onlineVisitors($siteId, $onlineThreshold) %}
$siteId: if siteId is not passed, primary siteId is used. If*is passed, unique online visitors across all sites are returned.$onlineThreshold: Specifies the duration for which a user is considered online.
{
counter(siteId: "*", onlineThreshold: 100, t: unique) {
onlineVisitors
debugMessage
}
}
{% set maxOnline = craft.counter.maxOnline(dateRange, startDate, endDate, siteId) %}.
- The result is an array. The first item represents the number of maximum online users, and the second item indicates the date when the maximum number of online users occurred.
- The supported date ranges are:
custom,thisHour,previousHour,today,yesterday,thisWeek,thisMonth,thisYear,past7Days,past30Days,past90Days,pastYear.
{
counter(dateRange: "custom", startDate: "2024-08-25", endDate: "2024-08-30", siteId:"*", t: timestamp) {
maxOnline
maxOnlineDate
debugMessage
}
}
{% set pageVisits = craft.counter.pageVisits($pageUrl, $siteId, $attributes) %}.
$pageUrl: the page URL is required.$siteId: if it is not passed, the primary siteId is used. If the site of the page is not important, pass*.$attributes: an array of attributes which you want in return. The default is['all', 'allIgnoreInterval', 'today', 'yesterday', 'thisWeek', 'previousWeek', 'thisMonth', 'previousMonth', 'thisYear', 'previousYear', 'lastVisit'].
- If the
siteIdof the requested page is not passed, the primary site is sent.- You can pass
*to get the first matched page without filtering the site.
- You can pass
- You can use
@dateConvertdirective with calendar, format, locale and timezone parameters to return the last visit in the intended format
{
pageVisits(page: "https://xyz.test/page1", siteId: "*", t: uniqueParam) {
all
allIgnoreInterval
thisYear
thisMonth
thisWeek
today
previousYear
previousMonth
previousWeek
yesterday
lastVisit@dateConvert(calendar: "gregorian", format: "yyyy-MM-dd HH:mm:ss EEEE", locale: "en_US", timezone: "UTC")
debugMessage
}
}
{% set topPages = craft.counter.topPages(dataRange, siteId, limit) %}
- The dataRange can be
all,allIgnoreInterval,today,thisWeek,thisMonth,thisYear,yesterday. - If
siteIdis not passed, the primary site is used. If*is passed, the top pages for all sites will be returned.
{
topPages(dateRange: "all", siteId: "*", limit: 10, t: timestamp) {
page
visits
}
}
{% set trendingPages = craft.counter.trendingPages(dataRange, siteId, growthType, ignoreNewPages, limit) %}
- The
dataRangecan betoday,thisWeek,thisMonth, orthisYear. - If
siteIdis not passed, the primary site is used. If*is passed, the top pages for all sites are passed. - The
growthTypecan be passed ascountorpercentage. Ifpercentageis selected the new pages are ignored. Ifcountis selected, you can filter new pages via theignoreNewPagesargument. - If
ignoreNewPagesis set to true, pages without visits in the previous date range are not shown. The default value is false.
{
trendingPages(dateRange: "thisMonth", growthType: "count", ignoreNewPages: true, limit: 1) {
page
current
previous
growth
debugMessage
}
}
- You should pass a new random value for the
targument like the current timestamp to prevent cache. - You can skip passing siteId, by default primary siteId is used.
{
counter(dateRange: "today", t: timestamp) {
visits
visitors
}
}
- By passing custom as dateRange, you can pass "2024-08-26" as the start date and end date.
- You can get current online visitors by passing
onlineVisitorstoo. - By passing the
debugMessage, you can view a debug message if the result is ok or not. - You can't pass visitors in this query because the selected
dateRangeis more than one day.
{
counter(dateRange: "custom", startDate: "2024-08-26", endDate: "2024-08-26", siteId: "2", t: timestamp) {
visits
averageVisitors,
onlineVisitors
maxOnline
maxOnlineDate
debugMessage
}
}
- By passing
onlineVisitorsas a field, we get current online visitors with this query - You can pass the
onlineThresholdargument in seconds, the default value is the value set in plugin settings
{
counter(dateRange: "yesterday", siteId: "*", onlineThreshold: 30, t: randomString) {
visits
visitors
onlineVisitors
}
}
By copying the counter.php file to the config folder of your project, you can customize some plugin configurations.
Config items are:
anonymizeIp: The default value is true, meaning the IP address is anonymized before processing and stored in the database.ipInEvent: The default value is false, indicating that the IP address is not included in the event.anonymizedIpInEvent: The default value is false, which means that the anonymized IP address is not included in the event.cacheWidgetsSeconds: default is 0. when this is not set or is 0, widgets use default caching system so cached results are used as long as the cached data is valid
- Counter widgets utilize caching to improve performance.
- Default caching is enabled automatically, requiring no additional setup.
- Users will still see the latest data when default caching is in effect. Cached data is used only if there are no new visits for the site/page within the widget's date range.
- For high-traffic sites where visits occur frequently, a new configuration option called cacheWidgetsSeconds has been introduced. This allows for caching widget data for a specified number of seconds, provided that cacheWidgetsSeconds is set to a value greater than 0.
- When cacheWidgetsSeconds is enabled, the default caching system is bypassed.
- Note that cacheWidgetsSeconds does not apply to widgets with date ranges set to "Previous Hour", "Yesterday", or "Today (prior to this hour)" where the default caching system will still be used.
Users can manually invalidate data caches for the counter plugin via the cache utility page.
The plugin will automatically expire (invalidate) the cached data for a widget whenever the widget is resaved. This ensures that any updates made to the widget are reflected immediately.
The plugin will also automatically expire cached data for a widget if the server's current day or hour differs from the day or hour when the widget was cached.
For example:
If a widget is set to display data for "today" or "yesterday" the cache will be invalidated when the server's day changes.
If a widget is set to display data for "this hour" or "previous hour" the cache will be invalidated when the server's hour changes.
-
EVENT_BEFORE_COUNT
use vnali\counter\services\counterService; use vnali\counter\events\CountEvent; use yii\base\Event; Event::on( CounterService::class, CounterService::EVENT_BEFORE_COUNT, function(CountEvent $event) { // to prevent counting // $event->isValid = false; } ); -
EVENT_AFTER_COUNT
use vnali\counter\services\counterService; use vnali\counter\events\CountEvent; use yii\base\Event; Event::on( CounterService::class, CounterService::EVENT_AFTER_COUNT, function(CountEvent $event) { // } ); -
Passed event items:
public string $page; // The requested page, trimmed to 2048 characters or 3072 bytes public string $untrimmedPage; // The untrimmed requested page public int $siteId; // The siteId that page belong to public ?int $userId; // The user who requested the page public ?string $ip; // IP of the user. It is passed only if `ipInEvent` config is set to true. public ?string $anonymizedIp; // The anonymized version of IP. It is passed only if `anonymizedIpInEvent` config is set to true. public string $hashedIp; // The hashed version of IP public ?string $userAgent; // The user agent who requested the page public DateTime $time; // The time when page is requested in UTC
| Label | Permission | Description |
|---|---|---|
| Manage plugin settings | counter-manageSettings | Can manage plugin settings |
| Access plugin widgets | counter-accessWidgets | Can view counter widgets |
- Test on the Craft Cloud.
- Provide a caching mechanism for faster widget loads.
- Implement Ajax load for widgets for better user experience.
Feel free to contact me by email at [email protected] or direct message me via 'vnali' on Craft CMS Discord channel.