Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Typed Collections Laravel Package

vistik/typed-collections

A Laravel/PHP package for type-safe collections: enforce element types at runtime, improve IDE and static-analysis support, and keep familiar Collection ergonomics. Define typed collections for models, DTOs, scalars, and custom classes.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Install with composer require vistik/typed-collections. Note the package is archived and last tagged in 2017, so it targets Laravel 5.x-era Illuminate\Support\Collection and older PHP versions. Test installs against your actual Laravel/PHP matrix before relying on it.
  • Read these first:
    1. README.md for the intended usage example.
    2. src/ for the base class (typically a TypedCollection extending Illuminate\Support\Collection).
    3. composer.json for PHP and illuminate/support constraints.
  • Minimal first use case: a collection that only accepts one model or DTO class.
use Vistik\TypedCollections\TypedCollection;

class UserCollection extends TypedCollection
{
    protected $type = \App\Models\User::class;
}

$users = new UserCollection([$userA, $userB]);
$users->push($userC); // allowed
$users->push(new stdClass()); // should throw
  • First practical integration: return the typed collection from a repository or service instead of a plain collect() result, so callers get immediate type enforcement at the boundary.

Implementation Patterns

  • Subclass per domain type. Create one small collection class per aggregate or DTO, e.g. UserCollection, InvoiceLineCollection, ProductCollection. Keep the $type property as the single source of truth and avoid scattering instanceof checks through callers.
  • Repository/service return types. Use typed collections as return values from query objects and services. This makes the contract executable: callers know they receive UserCollection, not an arbitrary Collection that might contain arrays or nulls.
class EloquentUserRepository
{
    public function all(): UserCollection
    {
        return new UserCollection(User::query()->get()->all());
    }
}
  • Hydration from Eloquent. Eloquent returns a base Collection. Wrap it explicitly with new UserCollection($models->all()) or a named constructor. Avoid assuming User::all() returns your subclass.
  • DTO/value-object pipelines. Typed collections work best when the element type is a stable, locally owned class. Use them at application boundaries where data crosses from HTTP, queues, or persistence into domain code.
  • Composition over inheritance for transformations. Because the class extends Laravel’s Collection, most collection methods are available. Be deliberate about whether a map() result should remain typed. If the mapping changes the element type, create a new typed collection for the new type instead of reusing the old one.
  • Validation at construction and mutation. The intended pattern is that both the constructor and mutating methods (push, prepend, offset assignment) enforce the type. Use the typed collection in tests to assert that invalid elements fail fast rather than leaking into later code.
  • Service container binding. If a typed collection represents a domain concept, you can bind an interface to a factory that returns the correct subclass. Keep this light; typed collections are value containers, not services.

Gotchas and Tips

  • Archived and unmaintained. There will be no fixes for newer Laravel collection internals, PHP 8 deprecations, or security issues. For new code, prefer a small in-house subclass with explicit validation, or static analysis generics via PHPStan/Psalm annotations. If you inherit this package, plan a migration rather than expanding its surface area.
  • Laravel version drift. Illuminate\Support\Collection has changed significantly since 2017. Methods such as map, filter, and when may return base collections or behave differently with subclassing. Verify the return type of every transformation you rely on.
  • Subclass return types are not guaranteed. A method that returns static in one Laravel version may return Collection in another. Do not assume chained calls preserve your typed collection; re-wrap when the type must be preserved.
  • Element validation edge cases. Check how the package handles null, associative array keys, and nested collections. If null is allowed, confirm it is intentional. If the type check uses is_a() versus instanceof, behavior with interfaces and parent classes may differ from expectations.
  • Eloquent collections are not your subclass. Model::all() and relationship properties return Laravel’s own Collection. Wrapping is explicit; there is no automatic conversion.
  • Debugging type failures. When an invalid element is rejected, inspect the exception message and the collection’s configured $type. Add a small test that pushes a wrong-type value and asserts the failure, so future Laravel upgrades do not silently weaken enforcement.
  • Extension points are limited. The main customization point is the type declaration. If you need multiple allowed types, nullable elements, or custom error messages, read the source first; you may need to override validation methods or wrap the collection yourself.
  • Performance is not the concern. The overhead is negligible for typical request workloads. The real cost is maintenance risk from the archived dependency.
  • Migration tip. Replace the package with a local base class:
class TypedCollection extends \Illuminate\Support\Collection
{
    protected string $type;

    public function __construct($items = [])
    {
        parent::__construct($items);
        $this->assertValid($this->all());
    }

    public function push(...$values)
    {
        $this->assertValid($values);
        return parent::push(...$values);
    }

    protected function assertValid(array $values): void
    {
        foreach ($values as $value) {
            if (! $value instanceof $this->type) {
                throw new \InvalidArgumentException(
                    sprintf('Expected [%s], got [%s].', $this->type, get_debug_type($value))
                );
            }
        }
    }
}
  • Use static analysis instead where possible. For collections that are only used internally, @var Collection<int, User> docblocks plus PHPStan/Psalm often provide better day-to-day safety than a runtime subclass, without an abandoned dependency.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin