A modern, optimized counter management package for Laravel 11/12 with PHP 8.4 support.
A flexible and powerful counter management system for Laravel applications. Easily track and manage various types of counters like page views, downloads, user actions, and more without cluttering your database schema.
- ✅ PHP 8.4 Optimized: Uses readonly properties, constructor property promotion, match expressions, and improved type declarations
- ✅ Laravel 11/12 Compatible: Modern service provider patterns and dependency injection
- ✅ High Performance: Built-in caching, bulk operations, and optimized database queries
- ✅ Type Safe: Full type declarations and strict typing throughout
- ✅ Modern Patterns: Uses modern PHP and Laravel patterns and best practices
- ✅ Comprehensive Testing: Full test coverage with modern testing practices
- Model-specific counters: Associate counters with any Eloquent model
- Global counters: System-wide counters for general statistics
- Cookie-based tracking: Prevent duplicate increments from the same user
- Flexible configuration: Customizable table names and settings
- Artisan commands: Create counters via command line
- Laravel 10-12 support: Compatible with modern Laravel versions
- PHP 8.2+ support: Built for modern PHP applications
- PHP: 8.4 or higher
- Laravel: 11.x or 12.x
- Install the package via Composer:
composer require turahe/laravel-counters
- Publish the configuration and migrations:
php artisan vendor:publish --tag=counters-config
This will publish:
- Configuration file:
config/counter.php
- Migration file:
database/migrations/xxxx_xx_xx_xxxxxx_create_counters_tables.php
- Run the migrations:
php artisan migrate
This creates the counters
and counterables
tables in your database.
use Turahe\Counters\Models\Counter;
// Create a counter for page views
Counter::create([
'key' => 'page_views',
'name' => 'Page Views',
'initial_value' => 0,
'step' => 1
]);
use Turahe\Counters\Traits\HasCounter;
class Post extends Model
{
use HasCounter;
// Your model code...
}
// In your controller
public function show(Post $post)
{
$post->incrementCounter('page_views');
return view('posts.show', compact('post'));
}
use Turahe\Counters\Facades\Counters;
// Track total downloads
Counters::increment('total_downloads');
Add the HasCounter
trait to any model you want to track:
use Turahe\Counters\Traits\HasCounter;
class Post extends Model
{
use HasCounter;
// Your model code...
}
// Add a counter to a model
$post->addCounter('views');
// Get counter value
$views = $post->getCounterValue('views');
// Increment counter
$post->incrementCounter('views');
// Decrement counter
$post->decrementCounter('views', 2); // Decrement by 2
// Reset counter to initial value
$post->resetCounter('views');
// Remove counter from model
$post->removeCounter('views');
// Check if model has counter
if ($post->hasCounter('views')) {
// Do something
}
Use the Counters
facade for system-wide counters:
use Turahe\Counters\Facades\Counters;
// Create a counter
Counters::create('total_downloads', 'Total Downloads', 0, 1);
// Get counter value
$downloads = Counters::getValue('total_downloads');
// Increment counter
Counters::increment('total_downloads');
// Decrement counter
Counters::decrement('total_downloads', 2);
// Set specific value
Counters::setValue('total_downloads', 100);
// Reset to initial value
Counters::reset('total_downloads');
Prevent duplicate increments from the same user:
// Only increment if user doesn't have cookie
Counters::incrementIfNotHasCookies('daily_visitors');
Counters::decrementIfNotHasCookies('available_slots');
Method | Description | Parameters |
---|---|---|
addCounter($key, $initialValue = null) |
Add counter to model | $key : Counter key, $initialValue : Optional initial value |
getCounter($key) |
Get counter object | $key : Counter key |
getCounterValue($key) |
Get counter value | $key : Counter key |
hasCounter($key) |
Check if model has counter | $key : Counter key |
incrementCounter($key, $step = null) |
Increment counter | $key : Counter key, $step : Optional step value |
decrementCounter($key, $step = null) |
Decrement counter | $key : Counter key, $step : Optional step value |
resetCounter($key, $initialValue = null) |
Reset counter | $key : Counter key, $initialValue : Optional reset value |
removeCounter($key) |
Remove counter from model | $key : Counter key |
Method | Description | Parameters |
---|---|---|
create($key, $name, $initialValue = 0, $step = 1) |
Create a counter | $key : Counter key, $name : Display name, $initialValue : Initial value, $step : Step value |
get($key) |
Get counter object | $key : Counter key |
getValue($key, $default = null) |
Get counter value | $key : Counter key, $default : Default value if not found |
setValue($key, $value) |
Set counter value | $key : Counter key, $value : New value |
setStep($key, $step) |
Set counter step | $key : Counter key, $step : Step value |
increment($key, $step = null) |
Increment counter | $key : Counter key, $step : Optional step value |
decrement($key, $step = null) |
Decrement counter | $key : Counter key, $step : Optional step value |
reset($key) |
Reset counter | $key : Counter key |
incrementIfNotHasCookies($key) |
Increment if no cookie | $key : Counter key |
decrementIfNotHasCookies($key) |
Decrement if no cookie | $key : Counter key |
The package configuration is located at config/counter.php
:
return [
'models' => [
'counter' => Turahe\Counters\Models\Counter::class,
],
'tables' => [
'table_name' => 'counters',
'table_pivot_name' => 'counterables',
],
'database_connection' => env('COUNTER_DB_CONNECTION'),
];
You can customize the table names in the configuration:
'tables' => [
'table_name' => 'my_counters',
'table_pivot_name' => 'my_counterables',
],
Create a counter via command line:
php artisan make:counter page_views "Page Views" 0 1
Parameters:
page_views
: Counter key"Page Views"
: Display name0
: Initial value1
: Step value
The package includes comprehensive tests. Run them with:
./vendor/bin/phpunit
- ✅ Model counter operations
- ✅ Global counter operations
- ✅ Cookie-based tracking
- ✅ Exception handling
- ✅ Database operations
- ✅ Configuration flexibility
- Always create counters before using them:
// Create the counter first
Counter::create([
'key' => 'page_views',
'name' => 'Page Views',
'initial_value' => 0,
'step' => 1
]);
// Then use it
$post->incrementCounter('page_views');
- Use meaningful counter keys:
// Good
$post->incrementCounter('article_views');
$user->incrementCounter('login_count');
// Avoid generic names
$post->incrementCounter('count');
- Handle counter existence gracefully:
if ($post->hasCounter('views')) {
$post->incrementCounter('views');
} else {
$post->addCounter('views');
$post->incrementCounter('views');
}
- Don't forget to run migrations:
php artisan migrate
- Don't use counters without creating them first:
// This might fail if counter doesn't exist
$post->incrementCounter('undefined_counter');
// Better approach
$post->addCounter('new_counter');
$post->incrementCounter('new_counter');
The package includes built-in caching for counter lookups, significantly improving performance for frequently accessed counters.
Use bulk operations to update multiple counters efficiently:
// Bulk increment
$results = Counters::bulkIncrement(['counter1', 'counter2'], 5);
// Bulk decrement
$results = Counters::bulkDecrement(['counter1', 'counter2'], 3);
The migration includes optimized indexes for better query performance.
Run the test suite:
composer test
- Readonly Properties: Immutable data structures
- Constructor Property Promotion: Cleaner class definitions
- Match Expressions: Modern control flow
- Named Arguments: Self-documenting function calls
- Improved Type Declarations: Better type safety
- Strict Types: Enforced type checking
- Modern Service Providers: Deferrable providers for better performance
- Improved Dependency Injection: Constructor injection and type hints
- Enhanced Model Features: Better relationships and scopes
- Modern Command Structure: Improved Artisan commands
Example seeder for creating counters:
use Illuminate\Database\Seeder;
use Turahe\Counters\Models\Counter;
class CounterSeeder extends Seeder
{
public function run()
{
// Create global counters
Counter::create([
'key' => 'total_downloads',
'name' => 'Total Downloads',
'initial_value' => 0,
'step' => 1
]);
Counter::create([
'key' => 'daily_visitors',
'name' => 'Daily Visitors',
'initial_value' => 0,
'step' => 1
]);
// Create model-specific counters
Counter::create([
'key' => 'page_views',
'name' => 'Page Views',
'initial_value' => 0,
'step' => 1
]);
}
}
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add some amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
This package is open-sourced software licensed under the MIT license.
- Issues: GitHub Issues
- Discussions: GitHub Discussions
Made with ❤️ by Nur Wachid