myerscode/utilities-bags
Immutable “Bag” utilities for fluent manipulation of collection/array data in PHP. Provides a helper class with chainable methods for reading, transforming, and managing data safely without mutation; includes usage and method docs, plus tests and coverage.
Installation:
composer require myerscode/utilities-bags
Basic Usage:
use Myerscode\Utilities\Bag;
// From array
$bag = Bag::from([1, 2, 3]);
// From Laravel Collection
$bag = collect([1, 2, 3])->toBag();
// Global helper (added in 2025.1.0)
$bag = bag([1, 2, 3]);
First Use Case: Filter and transform an array immutably:
$result = Bag::from([1, 2, 3, 4, 5])
->filter(fn($item) => $item % 2 === 0)
->map(fn($item) => $item * 2)
->toArray(); // [4, 8]
Illuminate\Support\Collection compatibility (e.g., toBag() method).// Process API response immutably
$response = Bag::from($apiData)
->pluck('user.id')
->filter(fn($id) => $id > 100)
->groupBy(fn($id) => (int)($id / 10))
->toArray();
// Replace collect() for immutable ops
$users = User::where('active', true)->get()->toBag()
->map(fn($user) => $user->only(['id', 'name']))
->toArray();
// Chain with Eloquent
$stats = Order::query()
->whereYear('created_at', now()->year)
->get()
->toBag()
->sum('amount')
->toFloat();
// Encapsulate business logic
class OrderBag extends Bag {
public function applyDiscount(float $discount): static {
return $this->map(fn($order) => [
...$order,
'discounted_price' => $order['price'] * (1 - $discount),
]);
}
}
// Usage
$orders = OrderBag::from($orderData)
->applyDiscount(0.1)
->filter(fn($order) => $order['discounted_price'] > 100);
// Mock immutable data for tests
$testData = Bag::make()
->merge(['user' => ['id' => 1, 'name' => 'Test']])
->merge(['roles' => ['admin']])
->toArray();
$this->assertEquals(['admin'], $testData['roles']);
AppServiceProvider:
Bag::macro('customMethod', function() {
return $this->filter(fn($item) => /* ... */);
});
toJson() for direct HTTP responses:
return response()->json(
Bag::from($data)->only(['id', 'name'])->toJson()
);
$lines = Bag::from(file($filePath))
->filter(fn($line) => str_contains($line, 'search'))
->map('trim')
->toArray();
Immutability Overhead:
Bag instance. Avoid chaining excessively in performance-critical paths.toArray() early to break immutability if needed.Laravel Collection Confusion:
toBag() converts to Bag, but Bag methods do not mirror Collection (e.g., no first() until 2026.0.0).Bag for immutable ops; use collect() for Laravel-specific features.PHP 8.5 Dependency:
Edge Cases:
isEmpty()/isNotEmpty() return booleans, not Bag instances (unlike other methods).reduce() requires a non-null initial value (unlike JS/PHP’s array_reduce).static (self). Example:
// Debug: Check return type
dd(get_class($bag->filter(...))); // Should be Myerscode\Utilities\Bag
Xdebug if chaining >5 methods. Consider intermediate toArray() calls.Bag::macro('avg', function() {
return $this->sum() / $this->count();
});
Bag::setDefaultDriver('array'); // Force array-based storage
bag() helper in composer.json autoload:
"extra": {
"laravel": {
"providers": ["Myerscode\\Utilities\\BagServiceProvider"]
}
}
pipe() for Complex Logic:
$result = Bag::from($data)
->pipe(fn($bag) => $bag->filter(...)->map(...))
->pipe(fn($bag) => $bag->groupBy(...));
only()/except() for API Responses:
return $this->bag($user)->only(['id', 'name', 'email'])->toJson();
Bag for request data:
$validated = Bag::from($request->all())
->only(['email', 'name'])
->toArray();
collect() for Eloquent relationships:
$user->posts->toBag()->pluck('title');
How can I help you explore Laravel packages today?