A Php >=7.2 Package for Laravel.
This Package is a combination of two visit counter packages :
- shetabit/visitor
- 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.
Speed | |
---|---|
Database | Low |
Cache:file | Medium |
Cache:redis | High |
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
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
$ 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.
-
Here you can define the name of your visits table.
'table_name' => 'eye_visits',
-
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, ],
-
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 ],
-
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',
-
You can decide to store crawlers visits or not. The Package will use jaybizzle/crawler-detect to detect crawlers.
'ignore_bots' => true,
-
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,
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();
});
//To set visitable model
$eye = eye($visitable);
//OR
$eye = eye()->setVisitable();
//And there are other ways
----------------------------
//to Change Visitor Model
//It is set to auth()->user() on default
$eye = eye()->setVisitor($userModel);
//And there are other ways
----------------------------
//To set Collection
$eye = eye()->setCollection($string);
----------------------------
//To change the parser value in config
$eye = eye()->byParser($string);
----------------------------
eye()->request(); // == request()->all()
eye()->ip(); // == request()->ip()
eye()->url(); // == request()->fullUrl()
eye()->referer(); // == $_SERVER['HTTP_REFERER'] ?? null
eye()->method(); // == request()->getMethod()
eye()->httpHeaders(); // == request()->headers->all()
eye()->userAgent(); // == request->userAgent() ?? ''
eye()->device(); //example: Webkit, ...
eye()->platform(); //example: Windows, Mac, ...
eye()->browser(); //example: Edge, Chrome, ...
eye()->languages(); //example: ["en-us","en","fa"]
eye()->uniqueId(); // == Str::random for cookie
eye()->getCurrentVisit(); // All the above as Visit Model
eye()->getVisitorModel(); // example: User Model
eye()->getVisitableModel(); //example: Article Model
NOTE: If you don't use these methods, the package automatically uses all of storages
$eye = $eye->viaCache(); // meaning : only using cache
$eye = $eye->viaDatabase(); // meaning : only using database
You Can Also Combine the result of storages that you choose:
eye()->via('cache' , 'database'); // meaning : only using database
// or simply
eye()
These Methods Work on viaDatabase
and viaCache
and any storing method that this package provides.
You can read all of Period Documentation in this Link: Cryildewit/Period
//basic example
//The Date Should be at least Y-m
$period = Period::create('2017-04-20', '2023-07-08');
$eye = $eye->period($period);
This chain method will assist to get Unique values base on the column you need.
- By default, it is set for unique_id .
//example
$eye = $eye->unique('browser');
This chain method can help to get Visits or set collection for Visit
Model.
//example
$eye = $eye->collection('articles');
This chain method will replace visitor fields in Visit
Model.
- It is useful for getting the User's activity.
By default
$whereMode
in true which means : Everytime you are fetching data it will use->where('visitor_type' , $type)->where('visitor_id' , $id)
methods to get them.
//example
$user = User::findOrFail(1);
$eye = $eye->visitor($user);
//or
$eye = $eye->visitor($user , false); // to disable where() query methods
This chain method has two usages based on Argument:
- Model or Null : It will Replace value of
visitable_type
andvisitable_id
inVisit
Model. For Fetching visits it will use->where('visitable_type' , $type)->where('visitable_id' , $id)
- Bool False : IF ONLY false was given it will disable
where()
methods
//example
$post = Article::findOrFail(1);
$eye = $eye->visitable($post);
//or
$eye = $eye->visitable(false); // to disable where() query methods
NOTE: If where() is enabled and visitable is null , the packages works based on url.
It will Fetch Visits from database or cache with the help of the queries you specified (like above).
//example
$eye = $eye->get();
It does the exact thing as get()
does. but returns Integer.
//example
$eye = $eye->count();
NOTE: If you don't specify one storing method ,
get()
andcount()
will fetch data from multiple storages and combines them.example: If
eye()->viaCache()->count()
returns 6 andeye()->viaDatabase()->count()
returns 50,eye()->count()
will return 56.
This method will check if the visitor has a record from before, it will not record another one until the user's cookie expires.
Note: for
viaDatabase()
it only works withcount()
and notget()
//example
$eye = $eye->once();
It will record the visit ONLY via the storing method you chose. you can also:
- replace
once()
with its argument - replace visitable methods with its argument
- replace visitor methods with its argument
//simple example
$eye->record();
//or
$post = Post::findOrFail(1);
$user = User::findOrFail(1);
$eye->record(
once : true, // == once()
visitable : $post, // == ->setVisitable($post)
visitor : $user, // == ->setVisitor($user)
);
removes all visits in storage
$eye->truncate();
removes all visits in storage
$eye->delete();
eye()->viaCache()->forget();
//equals to this
Cache::forget(config('eye.cache.key'));
You can push caches to database anytime you want
eye()->viaCache()->pushCacheToDatabase();
// it uses Visit::insert($cachedVisits)
This package automatically binds methods to Collection class
$collection = Cache::get(config('eye.cache.key'));
$collection->period(Period $period);
$collection->whereUrl(string $url);
$collection->whereVisitor(?Model $user);
$collection->whereVisitable(?Model $post);
$collection->whereCollection(string $name);
$collection->whereVisitHappened(Visit $visit);
You can write any query you want.
eye()->viaDatabase()->queryInit();
//equals to this
Visit::query();
Note: Recording Methods are applied for all storing methods, Therefore you can replace
viaCache
with other ones.The only Method that does not have General Usage is
Record()
. It just didn't make sense to make one. Feel free comment if you needed.
If you don't set visitable you can work with url
//only Individual Usage
eye()->viaCache()->record();
$post = Post::findOrFail(1);
//All of these lines return the same outcome.
//Individual Usage
eye($post)->viaCache()->record(); //Recommended
//or
eye()->setVisitable($post)->viaCache()->record();
//or
eye()->viaCache()->visitable($post)->record();
//or
eye()->viaCache()->record(visitable: $post);
It will replace auth()->user()
$user = User::findOrFail(1);
//All of these lines return the same outcome.
eye()->setVisitor($user)->viaCache()->record();
//or
eye()->viaCache()->visitor($user)->record(); //recommended
//or
eye()->viaCache()->record(visitor: $user);
It will fill collection column
$name = "name of collection";
//All of these lines return the same outcome.
eye()->setCollection($name)->viaCache()->record();
//or
eye()->viaCache()->collection($name)->record(); //Recommended
//All of these lines return the same outcome.
eye()->viaCache()->once()->record(); //Recommended
//or
eye()->viaCache()->record(true);
//General Usage
eye()->get();
-------------
eye()->count();
//Individual Usage
eye()->viaCache()->get();
-------------
eye()->viaCache()->count();
$post = Post::findOrFail(1);
//General Usage
eye($post)->get(); // Recommended
//or
eye()->visitable($post)->get();
//or
eye()->setVisitable($post)->get();
-------------
eye($post)->count(); // Recommended
//or
eye()->visitable($post)->count();
//or
eye()->setVisitable($post)->get();
//Individual Usage
eye($post)->viaCache()->get(); // Recommended
//or
eye()->viaCache()->visitable($post)->get();
//or
eye()->setVisitable($post)->viaCache()->get();
-------------
eye($post)->viaCache()->count(); // Recommended
//or
eye()->viaCache()->visitable($post)->count();
//or
eye()->setVisitable($post)->viaCache()->get();
$user = User::findOrFail(1);// Null works too
//General Usage
eye()->visitor($user)->get();
-------------
eye()->visitor($user)->count();
//Individual Usage
eye()->viaCache()->visitor($user)->get();
-------------
eye()->viaCache()->visitor($user)->count();
$user = User::findOrFail(1);// Null works too
//General Usage
eye()->visitor($user, false)->get();
-------------
eye()->visitor($user, false)->count();
//Individual Usage
eye()->viaCache()->visitor($user, false)->get();
-------------
eye()->viaCache()->visitor($user, false)->count();
//General Usage
eye()->collection('name of collection')->get();
-------------
eye()->collection('name of collection')->count();
//Individual Usage
eye()->viaCache()->collection('name of collection')->get();
-------------
eye()->viaCache()->collection('name of collection')->count();
You can read all of Period Documentation in this Link: Cryildewit/Period
$period = Period::create('2011-10' , '2023-10-17 12:00:00');// Null works too
//General Usage
eye()->period($period)->get();
-------------
eye()->period($period)->count();
//Individual Usage
eye()->viaCache()->period($period)->get();
-------------
eye()->viaCache()->period($period)->count();
NOTE: This method is the only method that does not work for database
get()
It is best to usecount()
$field = "column name";// Null works too
//eye()->unique($field)->get(); //DOES NOT WORK properly
//eye()->viaDatabase()->unique($field)->get() //DOES NOT WORK
eye()->viaCache()->unique($field)->get() //WORKS
-------------
eye()->unique($field)->count();// recommended
eye()->viaDatabase()->unique($field)->count();// WORKS
eye()->viaCache()->unique($field)->count();// WORKS
Removes Everything, Everywhere, All At Once
//General Usage
eye()->truncate();
//Individual Usage
eye()->viaCache()->truncate();
Select the Visits by methods above then delete. examples:
//General Usage
eye()->collection($name)->visitable($post)->delete();
//Individual Usage
eye()->viaCache()->collection($name)->visitable($post)->delete();
You can also get visits data through you morphed models.
Add EyeVisitable
to your visitable model.
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();
}
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
Add EyeVisitor
to your visitor model. and the rest is similar to visitable trait.
public function deleted(Model $visitor)
{
if ($visitor->isForceDeleting())
eye()->visitor($visitor)->visitable(false)->delete();
}
public function visits()
{
return $this->morphMany(Visit::class, 'visitor');
}
public function cachedVisits(?string $unique = null , ?string $collection = null , $visitable = false): Collection