PHPackages                             ami-hp/laravel-eye - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Templating &amp; Views](/categories/templating)
4. /
5. ami-hp/laravel-eye

ActivePackage[Templating &amp; Views](/categories/templating)

ami-hp/laravel-eye
==================

Recording Site Views with Cache and CronJob

1.1.4(2y ago)4491PHPPHP &gt;=7.2

Since Oct 5Pushed 2y ago1 watchersCompare

[ Source](https://github.com/ami-hp/laravel-eye)[ Packagist](https://packagist.org/packages/ami-hp/laravel-eye)[ RSS](/packages/ami-hp-laravel-eye/feed)WikiDiscussions v1.x Synced yesterday

READMEChangelog (3)Dependencies (9)Versions (5)Used By (0)

Laravel-Eye
===========

[](#laravel-eye)

[![laravel-eye Logo](./docs/img/laravel-eye-banner.png)](./docs/img/laravel-eye-banner.png)

[![Total Downloads](https://camo.githubusercontent.com/8592a5d297afac209cc2677f5bb8db613165cee6fbc2480097f6675141f38f1a/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f616d692d68702f6c61726176656c2d657965)](https://packagist.org/packages/ami-hp/laravel-eye)[![Latest Stable Version](https://camo.githubusercontent.com/8e43ed2229aeeaf5f51b0c302d4b5c2b373d068d15ea19377d6c17d199304559/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f616d692d68702f6c61726176656c2d657965)](https://packagist.org/packages/ami-hp/laravel-eye)[![License](https://camo.githubusercontent.com/3c52b72799ec92ff109971b6a008c765b34ff3c2fa5671a7eeccfa0bee74fae9/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f616d692d68702f6c61726176656c2d657965)](https://packagist.org/packages/ami-hp/laravel-eye)

A Php &gt;=7.2 Package for Laravel.

This Package is a combination of two visit counter packages :

- [shetabit/visitor](https://github.com/shetabit/visitor)
- [cyrildewit/eloquent-viewable](https://github.com/cyrildewit/eloquent-viewable)
- and a little extra.

It Stores Each Visit based on user's **Cookie**. The Idea is being able to Cache Visits to reduce queries. Using Cache is better for Websites with a little higher than normal traffic.

Storing MethodSpeedDatabaseLowCache:fileMediumCache:redisHigh> **NOTE** : If you save a high amount of data in cache, memory WILL BE EXHAUSTED. The Limitation Depends on your memory but no more than 1 million is recommended to save in cache.

These paths are provided for you to store the Visits When User Comes to your Visitable Page:

 ```
graph LR
A{Client Side} -- record --> B(via Cache)
B  -- push --> D(via Database)
A  -- record --> D
```

      Loading And These paths are provided to **get** the Visits from your storage:

 ```
graph LR
A(via Database) --> D{Client Side}
B(via Cache)  --> D{Client Side}
A --> E((SUM))
B --> E((SUM))
E --> D
```

      Loading Install
=======

[](#install)

```
$ composer require ami-hp/laravel-eye
$ php artisan vendor:publish --provider="Ami\Eye\Providers\EyeServiceProvider"
$ php artisan migrate
```

> **NOTE** : It is recommended to migrate the default **jobs** and **failed\_jobs** tables that come with a fresh laravel project, too.

Config
======

[](#config)

#### After Publishing the package files, you will have an `eye.php` file in your config folder.

[](#after-publishing-the-package-files-you-will-have-an-eyephp-file-in-your-config-folder)

1. Here you can define the name of your visits table.

    ```
    'table_name' => 'eye_visits',
    ```
2. To Prevent getting Memory Errors, You can specify the maximum amount of Visits saved in cache. after reaching to the maximum, all the Visits will be inserted to database. Also, you can change your cache key, too.

    ```
    'cache' => [
        'key' => 'eye__records',
        'max_count' => 1000000,
    ],
    ```
3. A Cookie will be set for the user when arrives to your page. You can Change the key of your cookie anytime. The Expiration Time is set for 5 years, you can change that as well.

    ```
    'cookie' =>[
        'key' => 'eye__visitor',
        'expire_time' => 2628000, //in minutes aka 5 years
    ],
    ```
4. The Package uses two packages to parse user agents: **jenssegers/agent** and **ua-parser/uap-php** . On default it is set for **jenssegers**. You Can Change it to **UAParser**.

    ```
    'default_parser' => 'jenssegers',
    ```
5. You can decide to store crawlers visits or not. The Package will use **jaybizzle/crawler-detect** to detect crawlers.

    ```
    'ignore_bots' => true,
    ```
6. If you wanted to use Jobs to increase speed in your inserts the package will do it for you. Feel free to turn it to false any time.

    ```
    'queue' => true,
    ```

Migration
=========

[](#migration)

```
   Schema::create(config('eye.table_name'), function (Blueprint $table) {
            $table->bigIncrements('id');
            $table->mediumText('unique_id')->nullable();
            $table->string('method')->nullable();
            $table->mediumText('request')->nullable();
            $table->mediumText('url')->nullable();
            $table->mediumText('referer')->nullable();
            $table->text('languages')->nullable();
            $table->text('useragent')->nullable();
            $table->text('headers')->nullable();
            $table->text('device')->nullable();
            $table->text('platform')->nullable();
            $table->text('browser')->nullable();
            $table->ipAddress('ip')->nullable();
            $table->string('collection')->nullable();
            $table->nullableMorphs('visitable'); // object model
            $table->nullableMorphs('visitor'); // subject model
            $table->timestamp('viewed_at')->useCurrent();
        });
```

---

Usage
=====

[](#usage)

- ### [Complete Documentation](./docs/Docs.md)

    [](#complete-documentation)
- [How To Record A Visit](#how-to-record-a-visit)

    - [Step 1: Set Visit Data From Start](#step-1-set-visit-data-from-start)
        - [Set Visitable Model](#set-visitable-model)
        - [Set Visitor Model](#set-visitor-model)
        - [Set Collection](#set-collection)
    - [Step 2: Choose Your Storing Method](#step-2-choose-your-storing-method)
        - [Store Visits in Database](#store-visits-in-database)
        - [Store Visits in Cache](#store-visits-in-cache)
        - [Store Visits in Redis](#store-visits-in-redis)
    - [Step 3: Set Data via Storing Methods](#step-3-set-data-via-storing-methods)
        - [Set Visitable Model](#set-visitable-model-1)
        - [Set Visitor Model](#set-visitor-model-1)
        - [Set Collection](#set-collection-1)
    - [Step 4: Define Conditions](#step-4-define-conditions)
    - [Step 5: Record the Visit](#step-5-record-the-visit)
    - [Additional Examples](#additional-examples)
- [How To Retrieve From Each Storage](#how-to-retrieve-from-each-storage)

    - [Step 1: Selecting the Data You Need](#step-1-selecting-the-data-you-need)
        - [Selecting Visitable or Url](#selecting-visitable-or-url)
        - [Selecting Visitor](#selecting-visitor)
        - [Selecting Collection](#selecting-collection)
    - [Step 2: Add More Conditions](#step-2-add-more-conditions-1)
        - [Select Visits In a Period of time](#select-visits-in-a-period-of-time)
        - [Select Visits with a Unique Value](#select-visits-with-a-unique-value)
    - [Step 3: Fetch Data From Storage](#step-3-fetch-data-from-storage)
- [How To Retrieve From Multiple Storages](#how-to-retrieve-from-multiple-storages)

    - [Step 1: Select Storing Methods](#step-1-select-storing-methods)
    - [Step 2: Select Visit Models](#step-2-select-visit-models)
        - [Select Visitable Model](#select-visitable-model)
        - [Select Visitor Model](#select-visitor-model)
        - [Select collection](#select-collection)
    - [Step 2: Add More Conditions](#step-2-add-more-conditions-1)
        - [Select A Period of Time](#select-a-period-of-time)
        - [Select Unique Values](#select-unique-values)
    - [Step 3: Retrieve Visits](#step-3-retrieve-visits)
- [How To Delete Visits](#how-to-delete-visits)

    - [Delete All](#delete-all)
    - [Delete Selected Visits](#delete-selected-visits)
- [How To Use Traits](#how-to-use-traits)

    - [EyeVisitable Trait](#eyevisitable-trait)
    - [EyeVisitor Trait](#eyevisitor-trait)

---

How To Record A Visit
---------------------

[](#how-to-record-a-visit)

You can record a Visit in various ways but first, you have to set your data to form a Visit model to be recorded.

### Step 1: Set Visit Data From Start

[](#step-1-set-visit-data-from-start)

The Visit Model is connected to the [visit table](#migration) which is shown above It is possible to Set Data From Start To the end of the chained methods.

#### Example

[](#example)

```
$post = Post::first();
$user = User::first();
$name = 'name of collection';

//if you wanted to set data
$eye = eye($post)->setVisitor($user)->setCollection($name);

//also if you didn't want to set data you can
$eye = eye();
```

#### Set Visitable Model

[](#set-visitable-model)

If you don't use this method, by default visitable model is set to be `NULL`.

```
$post = Post::first();

eye($post)
//or
eye()->setVisitable($post);
```

#### Set Visitor Model

[](#set-visitor-model)

If you don't use this method, by default visitable model is set to be `auth()->user()`.

```
$user = User::first();

eye()->setVisitor($user);
```

#### Set Collection

[](#set-collection)

If you don't use this method, by default it is set for `NULL`.

```
$name = 'name of collection';

eye()->setCollection($name);
```

### Step 2: Choose Your Storing Method

[](#step-2-choose-your-storing-method)

You can choose your storage, which is where you want to store your visits in.

#### Example

[](#example-1)

```
eye()->viaDatabase();
//or
eye()->viaCache()
```

#### Store Visits in Database

[](#store-visits-in-database)

Storing visits in database is a common way to store data. By using this method everytime a user visits, a query will be made to be inserting your data to database. By default, this method uses queue to insert data but, you can always turn it off by changing the value of queue to `false` in the config file `eye.php`.

> Pros : Gaining More control over visits with Visit model being connected to database.
>
> Cons : Interacting with database takes time. And if your website simultaneously has a lot of visitors it will slow your website down.

```
eye($post)->viaDatabase();
```

#### Store Visits in Cache

[](#store-visits-in-cache)

Using Cache as storage works as a bypass for inserting data to database. But you can hold the data in cache as long as you want. There is a Limit to the number of visits you can save, and it is in your control by changing `cache.max_count` in config file `eye.php`. After Reaching to maximum, the package will automatically push visits to database. Key name of cache is also there to be changed if you needed.

```
eye($post)->viaCache();
```

#### Store Visits in Redis

[](#store-visits-in-redis)

The only thing you should do is to change `CACHE_DRIVER` to `redis` so the package will use Redis through Cache. The storing method is still `viaCache()`.

### Step 3: Set Data via Storing Methods

[](#step-3-set-data-via-storing-methods)

You can also set data from this stage instead of setting it before choosing your storing method. there is no difference.

> **NOTE:** These methods are included in an **interface** so, they work in all storing methods.

#### Example

[](#example-2)

```
$eye = eye()->viaDatabase(); //OR
$eye = eye()->viaCache();

$post = Post::first();
$user = User::first();
$name = 'name of collection';

$eye->collection($name)
    ->visitor($user)
    ->visitable($user);
```

#### Set Visitable Model

[](#set-visitable-model-1)

If you don't use this method, by default visitable model is set to be `NULL`.

```
$post = Post::first();

eye()->viaCache()->visitable($post);
//SAME AS
eye($post)->viaCache();
```

#### Set Visitor Model

[](#set-visitor-model-1)

If you don't use this method, by default visitable model is set to be `auth()->user()`.

```
$user = User::first();

eye()->viaCache()->visitor($user);
//SAME AS
eye()->setVisitor($user)->viaCache();
```

#### Set Collection

[](#set-collection-1)

If you don't use this method, by default it is set for `NULL`.

```
$name = 'name of collection';

eye()->viaCache()->collection($user);
//SAME AS
eye()->setCollection($name)->viaCache();
```

### Step 4: Define Conditions

[](#step-4-define-conditions)

#### Record only Once

[](#record-only-once)

By using the method package will check if the user has a record stored or not. If the user had already visited the page so a new visit WILL NOT be recorded.

It works with the cookie that package set for user when enters the page, and will be stored as `unique_id`.

> **NOTE:** You can change the key of cookie and expiration time in config file `eye.php`by changing `cookie.key` and `cookie.expire_time`.

```
$name = 'name of collection';

eye()->viaCache()->once();
```

### Step 5: Record the Visit

[](#step-5-record-the-visit)

In the end you can insert the Visit Model to database or Cache. The Structure is basically this:

```
Step1->Step2->Step3->Step4->record(bool $once = false, ?Model $visitable = null, ?Model $visitor = null);
```

It means that you can also set the visitable and visitor from this method.

```
$post = Post::first();
$user = User::first();

eye()->viaCache()->record(true , $post , $user);
//SAME AS
eye($post)->viaCache()->visitor($user)->once()->record();
```

#### Additional Examples

[](#additional-examples)

```
$post = Post::first();
$user = User::first();
$name = 'name of collection';

eye()->viaDatabase()->collection($name)->visitable($post)->record();
eye($post)->viaCache()->once()->record(); // Recommended
```

---

How To Retrieve Visits
======================

[](#how-to-retrieve-visits)

You might want to list the Visits with details or just count them. It is all possible. You can do it for each storage separately or all together combined.

The Steps are very similar to Recording Steps.

How To Retrieve From Each Storage
---------------------------------

[](#how-to-retrieve-from-each-storage)

If you needed to retrieve data from each storage individually, you just need to use their storing method.

```
eye()->viaCache()
// OR
eye()->viaDatabase()
```

> **NOTE:** The Following Steps apply to all storing methods.

### Step 1: Selecting the Data You Need

[](#step-1-selecting-the-data-you-need)

It works exactly like setting data.

#### Selecting Visitable or Url

[](#selecting-visitable-or-url)

If you pass a model to visitable methods, It will select the Visits with the visitable model. But if you don't, it will select the url that the route is in. It works for `NULL` too.

For Visitable:

```
$post = Post::first(); // or NULL

eye($post)->viaCache(); // Simplified
//or
eye()->setVisitable($post)->viaCache();
//or
eye()->viaCache()->visitable($post); // Humanized
```

Result:

```
->where('visitable_id'   , $post->id)
->where('visitable_type' , get_class($post))
```

For Url:

```
eye()->viaCache();
```

Result:

```
->where('url' , request()->fullUrl())
```

Sometimes you don't need to enable a where clause for your query. you can **disable** it by using `visitable(false)`.

```
eye()->viaCache()->visitable(false);
```

#### Selecting Visitor

[](#selecting-visitor)

It is possible if you wanted to fetch the Visits that a specific visitor made. And you can pass `NULL` as well.

> The difference between this method and the one in record steps is that you need to turn `$whereMode` argument to `true`. otherwise it just sets visitor.

```
$user = User::first(); // or NULL

eye()->viaCache()->visitor($user , true);
```

Result:

```
->where('visitor_id'   , $user->id)
->where('visitor_type' , get_class($user))
```

#### Selecting Collection

[](#selecting-collection)

```
$name = 'name of collection';

eye()->viaCache()->collection($name);
```

Result:

```
->where('collection' , $name)
```

### Step 2: Add More Conditions

[](#step-2-add-more-conditions)

#### Select Visits In a Period of time

[](#select-visits-in-a-period-of-time)

The `period()` method passes a `Period` class as argument.

> You can read all about this class in [Cryildewit/Period](https://github.com/cyrildewit/eloquent-viewable/blob/master/README.md#between-two-datetimes)

```
$startDateTime = '1997-01';
$endDateTime   = '2023-08-17 10:11:00';
$period = Period::create($startDateTime , $endDateTime); //example

eye()->viaCache()->period($period);
```

Result:

```
if ($startDateTime !== null && $endDateTime === null)
    ->where('viewed_at', '>=', $startDateTime);
elseif ($startDateTime === null && $endDateTime !== null)
    ->where('viewed_at', 'viaCache()->unique($column)->count();
```

Result:

```
->distinct()->count($column);
```

### Step 3: Fetch Data From Storage

[](#step-3-fetch-data-from-storage)

Finally, you can retrieve data only by using `count()` or `get()`

The Structure is basically this:

```
Step1->Step2->count(); // returns Int
Step1->Step2->get();   // returns collection of Visit models
```

Get creative with it.

#### Examples

[](#examples)

```
eye($post)->viaCache()
          ->unique()
          ->count();
// OR
eye()->viaDatabase()
     ->collection('name')
     ->visitor($user)
     ->get();
```

How To Retrieve From Multiple Storages
--------------------------------------

[](#how-to-retrieve-from-multiple-storages)

When using cache, after reaching the maximum amount package will push visits to database. or maybe you accidentally used multiple storages. The package provides you methods to use multiple storing methods all at once.

### Step 1: Select Storing Methods

[](#step-1-select-storing-methods)

#### Select All

[](#select-all)

It's very simple.

```
eye()
// or
eye()->via('database' , 'cache'); // means both of them
```

### Step 2: Select Visit Models

[](#step-2-select-visit-models)

Do it as if you're using storages individually. It works the same.

#### Select Visitable Model

[](#select-visitable-model)

```
$post = Post::first();

eye($post);
//or
eye()->visitable($post);
```

#### Select Visitor Model

[](#select-visitor-model)

```
$user = User::first();

eye($post);
//or
eye()->visitor($post);
```

#### Select collection

[](#select-collection)

```
$name = 'collection';

eye($post)->collection($name);
```

### Step 2: Add More Conditions

[](#step-2-add-more-conditions-1)

#### Select A Period of Time

[](#select-a-period-of-time)

```
$startDateTime = '1997-01';
$endDateTime   = '2023-08-17 10:11:00';
$period = Period::create($startDateTime , $endDateTime); //example

eye()->period($period);
```

#### Select Unique Values

[](#select-unique-values)

By default, it is set for `unique_id`.

```
$column = 'platform';

eye()->unique($column);
```

> As mentioned before, it only works on counting for database.

### Step 3: Retrieve Visits

[](#step-3-retrieve-visits)

Combine the previous methods and add `get()` or `count()`.

#### Examples

[](#examples-1)

```
eye($post)->collection($name)->unique()->count();
eye()->via('cache' , 'database')->visitor($user)->get();
```

How To Delete Visits
--------------------

[](#how-to-delete-visits)

Sometimes you need to delete some recorded visits from your storages. This package provides you some methods to do it individually or delete them all at once.

> **NOTE:** Unique method does not work here

### Delete All

[](#delete-all)

- Step 1 : Select Storing Methods
- Step 2 : Truncate

#### Examples

[](#examples-2)

```
eye()->truncate(); // Removes All Visits in Every Storage
eye()->viaCache()->truncate(); // Removes All Visits in a storage
eye()->via('database' , 'cache')->truncate(); // Removes All Visits in selected storages
```

### Delete Selected Visits

[](#delete-selected-visits)

Selecting visits work exactly like retrieving them.

- Step 1 : Select Storing Methods
- Step 2 : Select Visits
- Step 3 : Delete

#### Examples

[](#examples-3)

```
eye($post)->collection($name)->delete();
eye()->viaCache()->visitor($user , true)->delete();

eye()->via('cache')
     ->visitable(false)
     ->visitor($user , true)
     ->collection($name)
     ->delete();
```

---

How To Use Traits
-----------------

[](#how-to-use-traits)

You can also get visits data through you morphed models.

### EyeVisitable Trait

[](#eyevisitable-trait)

Add `EyeVisitable` to your visitable model.

#### Observer

[](#observer)

Visitable observer will monitor your model's activity so, when your model got (force) deleted visits gets deleted as well by this code:

```
public function deleted(Model $visitable)
{
    if ($visitable->isForceDeleting())
        eye($visitable)->delete();
}
```

#### Relations

[](#relations)

In order to use eager loading you will need database relationships.

```
public function visits()
{
    return $this->morphMany(Visit::class, 'visitable');
}
```

Usage examples:

```
$posts = Post::with('visits')->get();

foreach ($posts as $post){
    $post->visits; // collection of visits
}

//OR

$posts = Post::withCount('visits')->get();

foreach ($posts as $post){
    $post->visits_count; // int
}
```

As you know, morphMany relationships are related to database, so somehow we need to access cached visits as well. this method also has been added to trait to access cached visits more easily.

```
public function cachedVisits(?string $unique = null , ?string $collection = null , ?Model $visitor = null): Collection
```

Usage examples:

```
$visits = $post->cachedVisits(); //collection of visits
// OR
$visits = $post->cachedVisits('unique_id' , 'name of collection' , $user);

$visits->count(); //int
```

### EyeVisitor Trait

[](#eyevisitor-trait)

Add `EyeVisitor` to your visitor model. and the rest is similar to visitable trait.

#### Observer

[](#observer-1)

```
public function deleted(Model $visitor)
{
    if ($visitor->isForceDeleting())
        eye()->visitor($visitor)->visitable(false)->delete();
}
```

#### Relations

[](#relations-1)

```
public function visits()
{
    return $this->morphMany(Visit::class, 'visitor');
}
```

```
public function cachedVisits(?string $unique = null , ?string $collection = null , $visitable = false): Collection
```

###  Health Score

22

—

LowBetter than 22% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity13

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity41

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~8 days

Total

4

Last Release

925d ago

Major Versions

0.0.1-alpha → 1.1.32023-10-28

PHP version history (2 changes)0.0.1-alphaPHP ^7.2|^8.0

1.1.3PHP &gt;=7.2

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/83532676?v=4)[Ahmadreza Nozaeem](/maintainers/ami-hp)[@ami-hp](https://github.com/ami-hp)

---

Top Contributors

[![ami-hp](https://avatars.githubusercontent.com/u/83532676?v=4)](https://github.com/ami-hp "ami-hp (43 commits)")

---

Tags

cacheclickslaravellaravel-packagelaravel-visitspage-visitsredis-cachevisitvisitorvisitor-counterlaravelpackagecacheviewvisitamivisitorscronjobvisitor-countereyesiteViewsami-hplaravel-eyepage-view-counter

### Embed Badge

![Health badge](/badges/ami-hp-laravel-eye/health.svg)

```
[![Health](https://phpackages.com/badges/ami-hp-laravel-eye/health.svg)](https://phpackages.com/packages/ami-hp-laravel-eye)
```

###  Alternatives

[laravel/pulse

Laravel Pulse is a real-time application performance monitoring tool and dashboard for your Laravel application.

1.7k12.1M99](/packages/laravel-pulse)[larastan/larastan

Larastan - Discover bugs in your code without running it. A phpstan/phpstan extension for Laravel

6.4k43.5M5.2k](/packages/larastan-larastan)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9682.1M97](/packages/roots-acorn)[laravel/cashier

Laravel Cashier provides an expressive, fluent interface to Stripe's subscription billing services.

2.5k25.9M107](/packages/laravel-cashier)[spatie/laravel-responsecache

Speed up a Laravel application by caching the entire response

2.8k8.2M51](/packages/spatie-laravel-responsecache)[laravel/cashier-paddle

Cashier Paddle provides an expressive, fluent interface to Paddle's subscription billing services.

264778.4k3](/packages/laravel-cashier-paddle)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
