## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require digitalrevolution/utils
Add the namespace to your composer.json autoload or use it directly via use statements:
use DigitalRevolution\Utils\Arrays;
use DigitalRevolution\Utils\Assert;
use DigitalRevolution\Utils\Stringify;
use DigitalRevolution\Utils\Closures;
First Use Case:
Replace native array operations with safer, more expressive methods. For example, replace reset($array) with:
$firstItem = Arrays::first($array); // Throws exception if empty
$firstItemOrNull = Arrays::firstOrNull($array); // Returns null if empty
Where to Look First:
flatten, groupBy, fetchByPath).Assert::notNull($value)->isInstanceOf(MyClass::class)).Stringify::value($complexObject)).Closures::unfold($closure)).Flattening Nested Arrays:
$flatArray = Arrays::flatten($nestedArray);
Useful for API responses or nested configurations.
Path-Based Access:
$value = Arrays::fetchByPath($array, ['user', 'profile', 'name']);
Arrays::assignByPath($array, ['user', 'profile', 'age'], 30);
Ideal for deeply nested Laravel config or request data.
Grouping and Mapping:
$grouped = Arrays::groupBy($users, fn($user) => $user['department']);
$mapped = Arrays::map($users, fn($user, $key) => strtoupper($user['name']));
Replace collect()->groupBy() or array_map() for type safety.
Safe Removal:
$filtered = Arrays::remove($items, fn($item) => $item->isArchived());
$filtered = Arrays::removeTypes($items, [null, 'string']);
Cleaner than manual array_filter with custom logic.
Fluent Chaining:
Assert::notNull($user)->isInstanceOf(User::class)->notFalse();
Replace if (!$user) throw new \RuntimeException(...) with self-documenting code.
Custom Error Messages:
Assert::greaterThan($age, 18, 'User must be at least 18 years old');
Useful in Laravel form requests or API validation.
Type-Specific Assertions:
Assert::isArray($config)->nonEmptyArray();
Assert::fileExists($path)->readable();
Validate Laravel config, file uploads, or environment variables.
Logging Complex Data:
\Log::debug('User data:', ['data' => Stringify::value($user)]);
Replace var_export or json_encode for human-readable output.
Template Rendering:
$template = "User: {user}, Status: {status}";
$rendered = str_replace(
['{user}', '{status}'],
[Stringify::value($user), Stringify::value($user->status)]
);
Service Initialization:
$service = fn() => new ExpensiveService();
$instance = Closures::unfold($service); // Executes once
Useful for Laravel service providers or dependency injection.
Memoization:
$cache = [];
$getCached = fn($key) => $cache[$key] ?? ($cache[$key] = fetchExpensiveData($key));
$value = Closures::unfold($getCached('key'));
Form Request Validation:
public function rules(): array {
return [
'age' => ['integer', 'min:18'],
// Custom validation via Assert:
'user' => ['required', 'array', 'min:1'],
];
}
public function withValidator($validator) {
$validator->after(function ($validator) {
Assert::notNull($this->user['id']);
Assert::greaterThan($this->user['age'], 0);
});
}
Service Providers:
public function register() {
$this->app->singleton(ExpensiveService::class, function () {
return Closures::unfold(fn() => new ExpensiveService());
});
}
API Responses:
return response()->json([
'data' => Arrays::map($users, fn($user) => [
'id' => $user['id'],
'name' => Stringify::value($user['name']),
]),
]);
first/last vs firstOrNull/lastOrNull:
Always prefer *OrNull in Laravel controllers to avoid UndefinedArrayKeyException in views/blades.
$firstItem = Arrays::firstOrNull($array); // Safe for Blade
fetchByPath and assignByPath:
\InvalidArgumentException if path is invalid.Arrays::wrap($path) if path might be a scalar:
Arrays::fetchByPath($array, Arrays::wrap('user.profile'));
remove Performance:
Arrays::removeNull() is optimized (uses array_filter internally).array_filter + array_values if order matters.equals vs ==:
Arrays::equals($a, $b) ignores order and supports ComparableInterface.Exception Messages:
Assert::notNull($value, 'Custom error message');
Assert::greaterThan($age, 18, 'Age must be > {0}')).isList vs isArray:
isList checks for sequential arrays (no gaps in keys).Assert::isList($response['data']);
File Assertions:
fileExists and readable/writable are Laravel-friendly for storage paths:
Assert::fileExists(storage_path('app/logs/error.log'))->readable();
Nested Assertions:
Assert::notNull($user)->isInstanceOf(User::class)) require explicit message passing:
Assert::notNull($user, 'User not found')->isInstanceOf(User::class, 'Invalid user type');
Edge Cases:
Stringify::value(null) returns 'null'.Stringify::value([]) returns 'empty-array'.Stringify::value(new DateTime()) returns '2023-10-01T00:00:00+00:00'.JsonSerializable, uses json_encode($value, JSON_THROW_ON_ERROR).Debugging:
app/Exceptions/Handler.php for exception debugging:
report(new \RuntimeException(Stringify::value($context)));
unfold Behavior:
$service = fn() => new DatabaseConnection();
$connection = Closures::unfold($service); // Executes once
$service = fn() => new Service();
$instance1 = Closures::unfold($service); // Executes
$service = fn() => new AnotherService(); // Resets!
$instance2 = Closures::unfold($service); // Executes again
Assert methods. Enable in phpstan.neon:
includes:
-
How can I help you explore Laravel packages today?