PHP code example of climactic / laravel-credits

1. Go to this page and download the library: Download climactic/laravel-credits library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

climactic / laravel-credits example snippets


return [
    // Allow negative balances
    'allow_negative_balance' => false,

    // Table name for credit transactions (change if you've updated the migration table name)
    'table_name' => 'credits',
];

use Climactic\Credits\Traits\HasCredits;

class User extends Model
{
    use HasCredits;
}

// Add credits
$user->creditAdd(100.00, 'Subscription Activated');

// Deduct credits
$user->creditDeduct(50.00, 'Purchase Made');

// Get current balance
$balance = $user->creditBalance();

// Check if user has enough credits
if ($user->hasCredits(30.00)) {
    // Proceed with transaction
}

$sender->creditTransfer($recipient, 100.00, 'Paying to user for their service');

// Get last 10 transactions
$history = $user->creditHistory();

// Get last 20 transactions in ascending order
$history = $user->creditHistory(20, 'asc');

$date = new DateTime('2023-01-01');
$balanceAsOf = $user->creditBalanceAt($date);

$metadata = [
    'order_id' => 123,
    'product' => 'Premium Subscription',
    'user_id' => 456,
    'tags' => ['premium', 'featured']
];

$user->creditAdd(100.00, 'Purchase', $metadata);

// ✅ Valid
$user->credits()->whereMetadata('source', 'purchase')->get();
$user->credits()->whereMetadata('user.id', 123)->get();
$user->credits()->whereMetadata('  source  ', 'test')->get(); // Trimmed automatically (not recommended)

// ❌ Invalid - will throw InvalidArgumentException
$user->credits()->whereMetadata('', 'value')->get();              // Empty key
$user->credits()->whereMetadata('data->key', 'value')->get();     // Arrow operator
$user->credits()->whereMetadata('data"key', 'value')->get();      // Contains quotes

// Query by simple metadata value
$purchases = $user->credits()
    ->whereMetadata('source', 'purchase')
    ->get();

// Query with comparison operators
$highValue = $user->credits()
    ->whereMetadata('order_value', '>', 100)
    ->get();

// Query by nested metadata (using dot notation)
$specificUser = $user->credits()
    ->whereMetadata('user.id', 123)
    ->get();

// Deeply nested keys (use raw where for very deep nesting)
$results = $user->credits()
    ->where('metadata->data->level1->level2->value', 'found')
    ->get();

// Check if metadata array contains a value
$premium = $user->credits()
    ->whereMetadataContains('tags', 'premium')
    ->get();

// Check if metadata key exists
$withOrderId = $user->credits()
    ->whereMetadataHas('order_id')
    ->get();

// Check if metadata key is null or doesn't exist
$withoutTags = $user->credits()
    ->whereMetadataNull('tags')
    ->get();

// Query by metadata array length
$multipleTags = $user->credits()
    ->whereMetadataLength('tags', '>', 1)
    ->get();

$filtered = $user->credits()
    ->whereMetadata('source', 'purchase')
    ->whereMetadata('category', 'electronics')
    ->whereMetadataContains('tags', 'featured')
    ->where('amount', '>', 50)
    ->get();

// Get credits filtered by metadata (with limit and ordering)
$purchases = $user->creditsByMetadata('source', 'purchase', limit: 20, order: 'desc');

// With comparison operators
$highValue = $user->creditsByMetadata('order_value', '>=', 100, limit: 10);

// Multiple metadata filters
$filtered = $user->creditHistoryWithMetadata([
    ['key' => 'source', 'value' => 'purchase'],
    ['key' => 'category', 'value' => 'electronics'],
    ['key' => 'tags', 'value' => 'premium', 'method' => 'contains'],
    ['key' => 'order_value', 'operator' => '>', 'value' => 50],
], limit: 25);

// Query with null values (explicitly 

use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;

return new class extends Migration
{
    public function up()
    {
        $tableName = config('credits.table_name', 'credits');

        // Add virtual columns for frequently queried metadata keys
        DB::statement("
            ALTER TABLE {$tableName}
            ADD COLUMN metadata_source VARCHAR(255)
            GENERATED ALWAYS AS (JSON_UNQUOTE(JSON_EXTRACT(metadata, '$.source'))) VIRTUAL
        ");

        DB::statement("
            ALTER TABLE {$tableName}
            ADD COLUMN metadata_order_id BIGINT
            GENERATED ALWAYS AS (JSON_EXTRACT(metadata, '$.order_id')) VIRTUAL
        ");

        // Add indexes on virtual columns
        Schema::table($tableName, function (Blueprint $table) {
            $table->index('metadata_source');
            $table->index('metadata_order_id');
        });

        // Optional: Composite indexes for common query combinations
        Schema::table($tableName, function (Blueprint $table) {
            $table->index(['metadata_source', 'created_at']);
            $table->index(['creditable_id', 'creditable_type', 'metadata_order_id']);
        });
    }

    public function down()
    {
        $tableName = config('credits.table_name', 'credits');

        Schema::table($tableName, function (Blueprint $table) {
            $table->dropIndex(['metadata_source', 'created_at']);
            $table->dropIndex(['creditable_id', 'creditable_type', 'metadata_order_id']);
            $table->dropIndex(['metadata_source']);
            $table->dropIndex(['metadata_order_id']);
        });

        DB::statement("ALTER TABLE {$tableName} DROP COLUMN metadata_source");
        DB::statement("ALTER TABLE {$tableName} DROP COLUMN metadata_order_id");
    }
};

use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;

return new class extends Migration
{
    public function up()
    {
        $tableName = config('credits.table_name', 'credits');

        // GIN index supports all JSON operators (@>, ?, ?&, ?|, etc.)
        DB::statement("
            CREATE INDEX {$tableName}_metadata_gin_idx
            ON {$tableName} USING GIN (metadata)
        ");

        // Optional: Add specific path indexes for even faster queries
        DB::statement("
            CREATE INDEX {$tableName}_metadata_source_idx
            ON {$tableName} ((metadata->>'source'))
        ");

        DB::statement("
            CREATE INDEX {$tableName}_metadata_order_id_idx
            ON {$tableName} ((metadata->'order_id'))
        ");
    }

    public function down()
    {
        $tableName = config('credits.table_name', 'credits');

        DB::statement("DROP INDEX IF EXISTS {$tableName}_metadata_gin_idx");
        DB::statement("DROP INDEX IF EXISTS {$tableName}_metadata_source_idx");
        DB::statement("DROP INDEX IF EXISTS {$tableName}_metadata_order_id_idx");
    }
};

// If you often query by source
$user->credits()->whereMetadata('source', 'purchase')->get();
// → Index: metadata_source

// If you filter by order_id
$user->credits()->whereMetadata('order_id', 12345)->get();
// → Index: metadata_order_id

// If you query by tags array
$user->credits()->whereMetadataContains('tags', 'premium')->get();
// → MySQL: Index on virtual column for tags
// → PostgreSQL: GIN index handles this automatically
bash
php artisan vendor:publish --tag="credits-migrations"
php artisan migrate
bash
php artisan vendor:publish --tag="credits-config"