appique/support

Useful utilities and helpers for Laravel applications

v1.0.0 2025-08-16 18:06 UTC

This package is auto-updated.

Last update: 2025-08-16 20:55:31 UTC


README

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

Introduction

A collection of utilities, helpers, and extensions for Laravel applications.

Requirements

  • PHP 8.3 or higher
  • Laravel 11.x or 12.x

Installation

You can install the package via composer:

composer require appique/support

Features

🔧 Helpers

ArrayHelper

Array manipulation with dot notation support:

use Appique\Support\Helpers\ArrayHelper;

// Set values with dot notation
$data = [];
ArrayHelper::set($data, 'user.profile.name', 'John Doe');
// Result: ['user' => ['profile' => ['name' => 'John Doe']]]

// Set with closure evaluation
ArrayHelper::set($data, 'timestamp', fn() => now());

// Set multiple values using wildcards
$data = [
    'users' => [
        ['name' => 'John'], 
        ['name' => 'Jane']
    ]
];
ArrayHelper::set($data, 'users.*.active', true);
// All users now have 'active' => true

StringHelper

String utilities with Unicode support and data masking:

use Appique\Support\Helpers\StringHelper;

// Remove non-ASCII characters
StringHelper::removeNonAscii('Héllö Wörld!'); // 'Hello World!'

// Check if value is integer-like
StringHelper::isIntegerish('123');  // true
StringHelper::isIntegerish('1.23'); // false

// Mask specific keys in arrays
$data = [
    'password' => 'secret123', 
    'username' => 'john'
];
$masked = StringHelper::maskKeys(['password'], $data);
// Result: ['password' => 's*******3', 'username' => 'john']

// Mask sensitive data
StringHelper::mask('4532015112830366');    // '453**********366'
StringHelper::mask('secret', '*', 1);       // 's****t'

CacheHelper

Encrypted caching with automatic cleanup of corrupted entries:

use Appique\Support\Helpers\CacheHelper;

// Store sensitive data encrypted
CacheHelper::encryptAndPut('api.token', $sensitiveToken, 3600);

// Retrieve and decrypt (automatically removes corrupted entries)
$token = CacheHelper::getAndDecrypt('api.token');
  • Stores all cached values encrypted
  • Automatically removes corrupted/tampered cache entries when detected
  • Returns null for missing or invalid data
  • No try-catch blocks needed

🔌 Collection Methods

Additional methods for Laravel Collections. You can use them in two ways:

Option 1: Global Mixins Register the mixin once (e.g., in AppServiceProvider) to make methods available on all Laravel Collections:

// In AppServiceProvider::boot()
use Illuminate\Support\Collection;
use Appique\Support\Mixins\CollectionMixin;

Collection::mixin(new CollectionMixin);

// Then use anywhere in your application
$admin = User::where('role', 'admin')
    ->get()
    ->oneOrThrow('Expected exactly one admin');
    
$product = Product::where('sku', $sku)
    ->get()
    ->oneOrNull('Multiple products with same SKU');

Option 2: Using the CollectionExtension Trait
For custom collection classes:

use Appique\Support\Traits\CollectionExtension;

class CustomCollection extends \Illuminate\Support\Collection
{
    use CollectionExtension;
}

$collection = new CustomCollection($users);
$admin = $collection
    ->where('role', 'admin')
    ->oneOrThrow();

Available Methods

// oneOrThrow() - Get exactly one item or throw exception
$winner = collect(['Alice'])->oneOrThrow('No single winner found');  // Returns 'Alice'
collect(['Alice', 'Bob'])->oneOrThrow('Multiple winners found');     // Throws exception

// oneOrNull() - Get one item or null (allows 0 or 1 item)
$single = collect(['item'])->oneOrNull('Multiple items found');      // Returns 'item'
$empty = collect([])->oneOrNull();                                   // Returns null
collect([1, 2, 3])->oneOrNull('Too many results');                   // Throws exception

// firstOrThrow() - Get first item or throw exception if empty
$first = collect(['a', 'b', 'c'])->firstOrThrow('No items');         // Returns 'a'
collect([])->firstOrThrow('Collection is empty');                    // Throws exception

// throwIfEmpty() - Ensure collection has at least one item
collect(['data'])->throwIfEmpty('No data available');                // Continues normally
collect([])->throwIfEmpty('No data available');                      // Throws exception

// throwIfNotEmpty() - Ensure collection is empty
collect([])->throwIfNotEmpty('Collection should be empty');          // Continues normally
collect(['item'])->throwIfNotEmpty('Collection should be empty');    // Throws exception

// copy() - Create a shallow copy of the collection
$original = new CustomCollection([1, 2, 3]);
$backup = $original->copy();  // New CustomCollection instance with same items

🎯 HasMethodMemoization Trait

Memoization with parameter-based caching and reset capability. Unlike Laravel's once() helper, this trait supports caching different results for different parameters:

use Appique\Support\Traits\HasMethodMemoization;

class DataProcessor
{
    use HasMethodMemoization;
    
    public function process(string $input, int $complexity = 1): string
    {
        // Cache keys are optional - use them to cache different results for different parameters
        return $this->memoize(
            fn() => $this->someExpensiveOperation($input, $complexity),
            $input,      // Optional: First cache key
            $complexity  // Optional: Second cache key
        );
    }
    
    public function processGlobal(): array
    {
        // Without cache keys, the result is cached once per instance
        return $this->memoize(
            fn() => $this->someExpensiveGlobalOperation()
            // No cache keys = same result for all calls
        );
    }
    
    public function clearCache(): void
    {
        // Reset all memoized values
        $this->resetMemoization();
    }
}

// Usage
$processor = new DataProcessor();

// Each call with different parameters is cached separately
$result1 = $processor->process('hello', 1);  // Runs expensive operation
$result1 = $processor->process('hello', 1);  // Returns cached result
$result2 = $processor->process('hello', 2);  // Different complexity = new cache entry
$result3 = $processor->process('world', 1);  // Different input = new cache entry

// Clear cache when needed
$processor->clearCache();
$result1 = $processor->process('hello', 1);  // Runs expensive operation again

🛠️ Console Tools

ClassGenerator

Generate classes from stub templates:

use Appique\Support\Console\ClassGenerator;

// Generates app/Services/UserService.php
ClassGenerator::make('UserService', 'service.stub')
    ->inAppNamespace('Services')
    ->placeholders(['model' => 'User'])
    ->create();

// Generates tests/Unit/Services/UserServiceTest.php
ClassGenerator::make('UserServiceTest', 'test.stub')
    ->inTestNamespace('Unit', 'Services')
    ->create();

// Generates database/factories/UserFactory.php
ClassGenerator::make('UserFactory', 'factory.stub')
    ->inFactoriesNamespace()
    ->create();

// Generates app/Models/User.php
ClassGenerator::make('App\\Models\\User', 'model.stub')
    ->create(); 

Stubs use simple placeholders: {{ class }}, {{ namespace }}, {{ model }} etc.

Testing

Run the test suite:

composer test

Run tests with coverage:

composer test-coverage

Run static analysis:

composer analyse

Fix code style:

composer format

Run all checks in one go:

composer check

Changelog

Please see CHANGELOG for more information on recent changes.

Contributing

Contributions are welcome! Please see CONTRIBUTING for details.

Development Setup

  1. Fork the repository
  2. Clone your fork
  3. Install dependencies: composer install
  4. Create a feature branch
  5. Make your changes
  6. Run checks: composer check (formats code, runs static analysis, and tests)
  7. Submit a pull request

Security

Please review our security policy on how to report security vulnerabilities.

Credits

License

The MIT License (MIT). Please see License File for more information.

Acknowledgments

This package was inspired by the Laravel community, particularly: