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

Troba Laravel Package

scandio/troba

Troba is a lightweight Laravel package from Scandio for adding database-backed search to Eloquent models. It provides a simple searchable trait and query helpers so you can find records without an external search service, keeping setup minimal and search logic close to your models.

Deep Wiki
Context7

Getting Started

Troba is a standalone PDO wrapper with annotation-mapped entities and a fluent query builder — it is not a Laravel package and has no service provider, facade, or Eloquent integration. Treat it as a library you bolt on, not a dependency you require casually.

Before anything else, check the hard constraints:

  • composer.json declares PHP ~5.5. That means no PHP 7.x/8.x support: no scalar type hints in its internals, no Throwable handling, and almost certainly no PHP 8 compatibility (dynamic properties, PDO::ATTR_* deprecations, curly brace string offsets). If your app is Laravel 8+ (PHP 7.3+ minimum, realistically 8.1+), you cannot run this in-process without a PHP 5.5/5.6 container.
  • The repo is archived and has ~11 stars. There are no security patches coming. Pin an explicit commit hash in composer.json, never dev-master, and vendor-lock it.

Where to look first, in order:

  1. README.md — for the bootstrap/configuration contract (connection setup and entity base class).
  2. The test suite (tests/) — this is the real documentation for an 11-star archived library. Look for entity fixtures and query-builder assertions; they show exact annotation syntax and expected SQL.
  3. The entity base class and the annotation/metadata parser — this tells you which docblock tags are actually honoured (table, column, id, relation tags) versus what the README claims.
  4. The query builder and its SQL-generation tests — this is where you learn how it quotes identifiers and handles NULL/IN.

Minimal bootstrap: give troba a live PDO handle (reuse Laravel's so transactions are shared), point it at a config object or static config, then map one existing table to a plain entity class with annotations on properties. Do a single read first:

// conceptual shape — confirm exact class/method names against the repo
$pdo = DB::connection()->getPdo();

// configure troba's entity manager / config with $pdo
$user = $em->find(User::class, 42);      // or via the static facade
$active = $em->findBy(User::class, ['status' => 'active']);

First use case to pick: read-only access to a legacy table you don't want to model in Eloquent — reporting, an admin lookup, an import source. Do not start with writes; persistence/flush semantics in an archived, undocumented library are where you'll lose days.

Implementation Patterns

Wrap it behind repositories, immediately. The single highest-value pattern: never let EntityManager/EQM calls leak into controllers or jobs. One repository class per aggregate, returning your own DTOs or Eloquent models. This is also your migration escape hatch — when you replace troba with Eloquent or Doctrine, you rewrite the repository bodies only.

final class LegacyOrderRepository
{
    public function __construct(private EntityManager $em) {}

    public function findOpen(int $customerId): array
    {
        return array_map(
            fn ($row) => OrderDto::fromRow($row),
            $this->em->findBy(Order::class, ['customer_id' => $customerId, 'status' => 'open'])
        );
    }
}

Bind the manager as a singleton. Construct it once in a service provider with the shared PDO handle, and inject it. Avoid the static facade in application code — statics make tests order-dependent and parallel test runs flaky (global config + global connection).

$this->app->singleton(EntityManager::class, fn () => new EntityManager(
    DB::connection()->getPdo(),
    config('troba')
));

Share Laravel's PDO instance. If troba opens its own connection, DB::transaction() will not roll back troba's writes, and you'll get phantom rows in tests and half-committed data in production. Pass DB::connection()->getPdo() and use DB::transaction() as the single transaction boundary. If troba wraps its own transactions, keep them strictly inside the Laravel one and rely on savepoints.

Read path: builder → query → hydration. The idiomatic flow is a fluent builder producing a query object, executing against PDO, and hydrating into entities (or a raw array result if you want to skip hydration overhead). For reporting, prefer the array/row result — entity hydration is the expensive part and buys you nothing if you immediately serialise to JSON.

Relations: go eager by hand. Annotation-declared relations are typically lazy, and an archived library will not have a convincing N+1 solution. In list endpoints, issue a batched query for the related IDs (WHERE id IN (...)) and stitch in PHP. Budget for this explicitly; it's the most common performance trap.

Map legacy schemas explicitly. Use the column annotation to bridge snake_case DB columns to camelCase properties rather than renaming columns or adding accessors. Keep one entity class per table, no inheritance cleverness — the metadata parser is the least-tested part of the codebase.

Test against SQLite in-memory with schema fixtures. Troba's SQL generation is driver-sensitive (identifier quoting, LIMIT syntax). Run the bulk of your tests on SQLite for speed, plus a small suite on MySQL/Postgres for the driver-specific paths. Wrap each test in a transaction and rollBack() in tearDown.

Migration strategy: introduce the repository boundary and DTOs on day one, then replace repository internals with Eloquent one aggregate at a time. Because troba entities are dumb data holders, DTO construction is cheap and the swap stays mechanical.

Gotchas and Tips

  • The PHP version is the real blocker. Nothing else matters if you're on PHP 8. Options: a separate PHP 5.6 service (HTTP or CLI) that owns the troba code, or a rewrite. Don't try to patch the library forward — you'd own a fork with 11 stars and no upstream.
  • Annotation parsing is silent. A malformed docblock, a typo in a column name, or a missing use in the annotation string typically yields a default-mapped entity or an empty result set, not an exception. When a query returns nothing, dump the resolved metadata (table name, column map, primary key) before debugging the SQL.
  • Annotations + opcache. If troba reads docblocks at runtime, opcache settings that strip comments (opcache.save_comments=0) break entity metadata in production only. Verify this setting and, if there's a metadata cache, warm it during deploy.
  • PDO returns strings. int, bool, and DateTime columns come back as strings unless you cast. Cast in the DTO/from-row step, not in the entity, so the mapping stays obvious.
  • IN () with an empty array is a classic builder bug in small libraries — it emits invalid SQL or matches everything. Guard the call site.
  • IS NULL vs = NULL. Fluent builders frequently translate ['col' => null] into col = NULL, which never matches. Use the explicit isNull/notNull method if it exists, or raw SQL fragments.
  • Identifier quoting is driver-dependent. Reserved-word column names (order, key, group) may work on MySQL and fail on Postgres or vice versa. Check what the builder emits for your production driver, not your test driver.
  • Don't dual-write with Eloquent. If an Eloquent model and a troba entity touch the same table, you have two identity maps, two timestamp conventions, and no cache coherence. Pick one writer per table; route the other side through the same repository.
  • Static config and parallel tests. Global configuration plus a global connection means phpunit --parallel will interleave state. Run these tests serially, or refactor config into the injected manager instance.
  • Debugging: enable whatever query logging hook exists; if there is none, wrap the PDO handle in a decorator that logs $sql and $params before delegating. That five-line class is the single best investment you'll make in this dependency.
  • Extension points worth knowing: custom relation classes, a custom entity base class, and (if present) hydration callbacks for post-load fixups. Extend via subclassing rather than forking the library — a fork in an archived repo is unmaintainable.
  • Fork hygiene if you must patch: keep patches in a thin layer (a subclass, a decorator, a wrapper) with a single PATCHES.md, and pin the upstream commit you diffed against. Upstream will never accept a PR.
  • Overall recommendation for a Laravel shop: if this is greenfield, do not adopt troba — Eloquent, Doctrine, or plain DB::select() with DTO mapping covers every use case here with maintained code and PHP 8 support. Troba is justified only for maintaining an existing PHP 5.5 codebase that already depends on it, and even then the correct roadmap is extraction, not expansion.
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.
encore2/laravel-admin2
cboxdk/laravel-console-kit
shapecode/doctrine-dbal-datetimeutc
gilads-otiannoh254/inertia-protocol
dakiquang/laravel-oauth-refresh-lock
woda/laravel-worktrees
dalefranks/ramasite
faghy/laracashbook
roundly-consulting/google-places-for-laravel
roundly-consulting/kubernetes-api-for-laravel
roundly-consulting/plausible-for-laravel
gvp/gvp-laravel-template
metasyncsite/laravel-metasync-client
roundly-consulting/onboarding-for-laravel
adachsoft/cli-process-tester
adachsoft/changelog-linter
adachsoft/collection
adachsoft/php-code-style
adachsoft/api-version-bundle
cgv/localized-resource