gcego/data
gcego/data is a lightweight Laravel/PHP package for working with structured data. It offers a fluent API for transforming, validating, and organizing data, helping developers keep application logic clean and maintainable, with sensible defaults and straightforward integration into existing projects.
Build vs. buy for the data access layer. This release bundles, in one dependency, the capabilities most teams end up hand-rolling across a dozen sprints: filtering/sorting/search/pagination with typed operators, relation inclusion with depth limits, relationship aggregates as subqueries, a mutation pipeline (validate → authorize → before-save → scoped write → post-commit events), transaction management with deadlock retries and savepoints, result caching with stampede locks, field-level security, and query profiling with N+1 detection. If your roadmap has any of these as "someday," this shifts the conversation from whether to build to what you build on top of it.
Multi-tenancy as a launch gate, not a later hardening pass. TenantScoped is fail-closed (no tenant → 403), tenant stamping is not input-settable, related records are tenant-scoped on includes/eager loads/relation aggregates/relation filters/relation search, and the result cache is always keyed by tenant even under Shared isolation. That is a security posture you can put in front of a customer's security review rather than promising in a roadmap.
Packaging and tiering. Field-level security (read/write rules via booleans, Gates, or closures — unreadable fields cannot be filtered, sorted, searched, or aggregated), audit read events (SourceQueried), and the write event set (RecordCreated/Updated/Deleted/Restored after commit) are the raw material for an enterprise or compliance tier. These are features buyers ask for by name and are expensive to retrofit.
Embedded analytics and reporting features. DataManager::aggregate() with grouping and time buckets (day/week/month/year), plus scopedQuery() returning a builder that already has authorization, mandatory constraints, tenancy, and allowlisted filters applied, means analytics surfaces can aggregate in the database without re-implementing your security model — and without an export-to-warehouse detour. This is the unlock for dashboards, usage reporting, and self-serve BI.
A single contract for a heterogeneous data layer. Source::define() with typed definitions plus Contracts\SourceProvider lets Eloquent models, database tables, builders, and in-memory collections be addressed identically. Concretely: you can put a stable query/write contract in front of an API before you know whether a given domain is a table, a model, or a third-party API, and change your mind later without touching consumers.
Roadmap sequencing signal. The pre-1.0 release explicitly removed the Blueprints/Registry integration and the source renderer: this is now a domain-agnostic engine other packages build on, not a UI or admin-panel product. Plan for the presentation/admin layer to remain your own work.
Adopt when several of these are true:
Look elsewhere (or stay with plain Eloquent) when:
illuminate/validation ^13. This is the single hardest gate. Laravel 13 is a very new major; if your platform is on 11 or 12, or your upgrade cadence is conservative, this package is unavailable to you regardless of merit. Validate this before any design discussion.docs/migration.md, and assume at least one more migration before the API settles. The hard dependency on gcego/core also means an ecosystem-level version lock.Adoption criteria before committing: run a one-sprint spike against a single high-risk source (one tenant-scoped model with a relation, one field-security rule, one filterable endpoint); confirm RelationScope, cache invalidation, and the write rollback-on-scope-escape behavior match your threat model; confirm your error-handling layer copes with the new generic 404/403/503 responses, which deliberately move detail into context() for logs only.
For executives: We can buy, rather than build, the security-sensitive plumbing that sits under every screen and endpoint in the product: tenant isolation, field-level permissions, a governed write path with audit events, caching, and query performance monitoring. Three arguments matter commercially. First, risk: tenancy is fail-closed — the default behavior is to deny, not to leak — and unreadable fields are excluded from filtering, sorting, searching, and aggregation, so they cannot be inferred sideways. That is defensible in a security questionnaire. Second, speed: filtering, search, pagination, relation loading, aggregates, and transactions are delivered as configuration instead of bespoke code, which compresses the delivery of list/report/dashboard features. Third, packaging: field security, audit events, and tenant isolation are credible enterprise-tier differentiators. The cost is a hard platform requirement (PHP 8.3+, Laravel 13) and an immature dependency at 1.0, so we treat it as a pinned dependency with a budgeted upgrade path, not a fire-and-forget install.
For engineering: This replaces hand-rolled query and write plumbing with one declared contract. Source::define() gives you typed, immutable definitions; the Data facade and manager give you query, aggregate, scopedQuery, create/update/delete/restore, and transaction. Writes are allowlisted, validated, policy-authorized, locked inside the source's scope, run through BeforeSave, rolled back if they would escape the scope, and emit events only after commit. Caching is version-token invalidated on writes and model events with stampede locks, and authorization, field rules, and transforms still run per caller — cache does not become a permission bypass. QueryContext::profiled() gives per-request statement profiles, N+1 detection, slow-query logging, and statement timeouts (MySQL/MariaDB, PostgreSQL), plus lazy-loading prevention on returned models.
What you must plan for: the pre-1.0 to 1.0 transition is a genuine rewrite of the declaration API — new Source(...), Source::model/table/builder/collection(), Property, Paginate, the FilterOperator/ValueType enums, and CustomOperator are all gone in favor of typed definition objects and Contracts\Operator/Contracts\FilterValueType. If we have any prototype code, it does not carry over. Custom providers must add the fifth $aggregates parameter to ResolvesRecords::find(). And we own the presentation layer: there is no renderer, no admin panel, and no Blueprint integration in this package. Treat the source declaration as our internal data contract and keep controllers/resources thin on top of it.
How can I help you explore Laravel packages today?