LaravelPackages.net
Acme Inc.
Toggle sidebar
turahe/laravel-counters

management for counters in laravel system

450
2
3.0.0
About turahe/laravel-counters

turahe/laravel-counters is a Laravel package for management for counters in laravel system. It currently has 2 GitHub stars and 450 downloads on Packagist (latest version 3.0.0). Install it with composer require turahe/laravel-counters. Discover more Laravel packages by turahe or browse all Laravel packages to compare alternatives.

Last updated

Laravel Counters

PHP Tests Codecov Latest Release Latest Version PHP Version License

A modern, optimized counter management package for Laravel 11/12/13 with PHP 8.4 and 8.5 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.

πŸš€ Features

  • βœ… PHP 8.4 & 8.5: Readonly properties, constructor property promotion, match expressions, and strict typing
  • βœ… Laravel 11/12/13 Compatible: Modern service provider patterns and dependency injection
  • βœ… High Performance: Built-in caching, bulk operations, and optimized database queries
  • βœ… Type Safe: Full type declarations throughout
  • βœ… Comprehensive Testing: Full test suite with Codecov coverage reporting
  • 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

πŸ“‹ Table of Contents

Installation

Requirements

  • PHP: 8.4 or higher (8.5 supported)
  • Laravel: 11.x, 12.x, or 13.x

Step-by-Step Installation

  1. Install the package via Composer:
composer require turahe/laravel-counters
  1. 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
  1. Run the migrations:
php artisan migrate

This creates the counters and counterables tables in your database.

Quick Start

1. Create a Counter

use Turahe\Counters\Models\Counter;

// Create a counter for page views
Counter::create([
    'key' => 'page_views',
    'name' => 'Page Views',
    'initial_value' => 0,
    'step' => 1
]);

2. Use with Models

use Turahe\Counters\Traits\HasCounter;

class Post extends Model
{
    use HasCounter;
    
    // Your model code...
}

3. Track Views

// In your controller
public function show(Post $post)
{
    $post->incrementCounter('page_views');
    
    return view('posts.show', compact('post'));
}

4. Global Counters

use Turahe\Counters\Facades\Counters;

// Track total downloads
Counters::increment('total_downloads');

Usage

Model Counters

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...
}

Available Methods

// 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
}

Global Counters

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');

API Reference

Model Methods (HasCounter Trait)

| 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 |

Global Counter Methods (Counters Facade)

| 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 |

Configuration

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'),
];

Customizing Table Names

You can customize the table names in the configuration:

'tables' => [
    'table_name' => 'my_counters',
    'table_pivot_name' => 'my_counterables',
],

Artisan Commands

Create Counter

Create a counter via command line:

php artisan make:counter page_views "Page Views" 0 1

Parameters:

  • page_views: Counter key
  • "Page Views": Display name
  • 0: Initial value
  • 1: Step value

Testing

The package includes a comprehensive test suite covering model counters, global counters, cookie-based tracking, exceptions, and configuration.

Local

composer install
composer test

Generate an HTML coverage report locally:

composer test-coverage

Docker

Run tests in a container (default: PHP 8.5 + Laravel 13):

docker compose run --rm test

Makefile

Run the full CI matrix (PHP 8.4/8.5 Γ— Laravel 11/12/13):

make test-all
make test-php84-laravel12
make help

Coverage

Coverage is uploaded to Codecov on every push and pull request via the Codecov workflow.

What's tested

  • βœ… Model counter operations
  • βœ… Global counter operations
  • βœ… Cookie-based tracking
  • βœ… Exception handling
  • βœ… Database operations
  • βœ… Configuration flexibility

Common Usage Patterns

βœ… Best Practices

  1. 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');
  1. Use meaningful counter keys:
// Good
$post->incrementCounter('article_views');
$user->incrementCounter('login_count');

// Avoid generic names
$post->incrementCounter('count');
  1. Handle counter existence gracefully:
if ($post->hasCounter('views')) {
    $post->incrementCounter('views');
} else {
    $post->addCounter('views');
    $post->incrementCounter('views');
}

❌ Common Mistakes

  1. Don't forget to run migrations:
php artisan migrate
  1. 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');

Performance Optimizations

Caching

The package includes built-in caching for counter lookups, significantly improving performance for frequently accessed counters.

Bulk Operations

Use bulk operations to update multiple counters efficiently:

// Bulk increment
$results = Counters::bulkIncrement(['counter1', 'counter2'], 5);

// Bulk decrement
$results = Counters::bulkDecrement(['counter1', 'counter2'], 3);

Database Indexes

The migration includes optimized indexes for better query performance.

PHP 8.4+ Features Used

  • 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

Laravel 11/12/13 Features Used

  • 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

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
        ]);
    }
}

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

This package is open-sourced software licensed under the MIT license.

Support


Made with ❀️ by Nur Wachid

Comments