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

Lurkerlite Laravel Package

totten/lurkerlite

LurkerLite is a lightweight Laravel package for logging and inspecting HTTP requests. It captures incoming traffic for debugging and monitoring with minimal setup and overhead, making it a handy alternative when full debugging suites are overkill. Review payloads, headers, and responses during devel...

View on GitHub
Deep Wiki
Context7

Getting Started

Lurker is not a Laravel package — it is a framework-agnostic resource-tracking library. It introspects a SQL schema, derives the foreign-key graph between tables, and lets you walk that graph from a root record (find every row that refers to it, and recursively everything that refers to those). The lite build is the dependency-light variant (plain PDO rather than a full ORM/DBAL stack), which is why it shows up inside larger CMS projects rather than standalone apps.

Minimal setup:

composer require totten/lurkerlite

First three things to read, in order — the docs are thin, so the code is the documentation:

  1. composer.json → the autoload block, to confirm the exact namespace (Lurker\... vs. a global Lurker class). Guessing this is the #1 reason a first attempt fatals.
  2. README.md → the canonical short example of constructing the tracker and passing it a connection.
  3. tests/ → the real API contract. Copy a test that builds a small two-table fixture; it will show you how schema discovery is triggered and what the traversal returns.

Smoke test outside Laravel first:

$pdo = new PDO('sqlite::memory:');
$pdo->exec('CREATE TABLE team (id INTEGER PRIMARY KEY)');
$pdo->exec('CREATE TABLE player (id INTEGER PRIMARY KEY, team_id INTEGER REFERENCES team(id))');

$lurker = new Lurker(/* connection or schema, per README */);

// 1. Prove the graph is visible before you trust it
print_r($lurker->getReferrers(['team', 'id' => 1]));   // expect: player.team_id

Then wire it into Laravel in a service provider and bind it as a singleton (see below). First real use case: "give me everything that would break / everything I'd have to delete if I removed row X" — impact analysis and GDPR-style data export are the two things this library is genuinely good at, and they are painful to hand-roll.

Implementation Patterns

1. Bind the raw PDO, not the Eloquent connection. Lurker wants a PDO handle and does its own introspection. Laravel's connection wrapper will confuse it.

// app/Providers/LurkerServiceProvider.php
public function register(): void
{
    $this->app->singleton(Lurker::class, function ($app) {
        return new Lurker($app['db']->connection()->getPdo());
    });
}

Make it a singleton so schema introspection is paid once per request, and forgetInstance() in tests when you rebuild the database.

2. Cascade delete by walking referrers, children first. The traversal gives you the dependency order; reverse it for deletes.

DB::transaction(function () use ($lurker, $root) {
    $graph = $lurker->getReferrers($root);   // table => [ids], dependency-ordered
    foreach (array_reverse($graph, true) as $table => $ids) {
        DB::table($table)->whereIn('id', $ids)->delete();
    }
    DB::table($root['table'])->where('id', $root['id'])->delete();
});

Keep the whole thing inside one transaction and delete in reverse topological order — doing it the other way round trips FK constraints halfway through and leaves a half-deleted graph.

3. Data export / subject-access requests. Same graph, different verb: serialize table => rows to JSON instead of deleting. This is the pattern where Lurker earns its keep, because the alternative is a hand-maintained list of forty tables that goes stale the moment someone adds a migration.

4. Listeners/callbacks for auditing. Rather than pulling the whole graph into memory, register a listener that fires per discovered record and writes to an audit log or emits an event. This keeps memory flat on wide graphs and gives you a natural place to enforce per-table authorization:

$lurker->addListener('*', function ($record) use ($user) {
    abort_unless($user->can('view', $record), 403);
});

5. Queue the heavy traversals. A graph walk is N queries per table per level. For anything beyond a handful of hops, dispatch a job and stream results to a file/table rather than trying to return it from a controller.

6. Guard it behind your own interface. Because the package is tiny and unmaintained-looking, define App\Contracts\ResourceGraph with referrers() / dependents() and adapt Lurker behind it. Swapping to a hand-written recursive-CTE implementation later then costs you one class, not a codebase-wide refactor.

7. Test fixtures. Build the schema in sqlite::memory: in a test setUp() with real REFERENCES clauses — this both tests your integration and documents which edges Lurker is expected to infer.

Gotchas and Tips

  • It only knows about declared foreign keys. Laravel makes it trivially easy to write user_id columns with no ->constrained(). Lurker will silently skip those edges, and a "deep delete" will leave orphans that no error message warns you about. Audit information_schema for FK coverage before you trust any destructive use. The same blind spot applies to polymorphic (commentable_id/commentable_type) and pivot tables with composite keys.
  • Prove the graph before acting on it. Always dump getReferrers() for a known row in a scratch database first. "The library deleted the wrong thing" is almost always "the library never saw that edge."
  • Schema caching. Introspection results are held on the instance; run migrations in the same process (or long-lived Octane/Swoole workers) and you'll be traversing a stale graph. Re-instantiate after migrate, or bind it as scoped rather than singleton under Octane.
  • ON DELETE CASCADE double-deletes. If your schema already cascades, Lurker's explicit child deletes are redundant but harmless — except when listeners/audit hooks fire, in which case you log rows that the DB was about to remove anyway. Decide which layer owns deletion and disable the other.
  • Soft deletes are invisible to it. Lurker sees rows, not deleted_at. A "cascade" through soft-deleted children will hard-delete them. Translate to update(['deleted_at' => now()]) explicitly if that's your model.
  • Index every FK column. The traversal is a WHERE fk IN (...) per level; unindexed FKs turn a 3-hop walk into a table-scan storm. This is the single biggest performance lever.
  • Buffered queries and iteration. On MySQL, unbuffered result sets plus a second query on the same connection will error. If you're iterating results while writing audit rows, use a separate connection or fetchAll() first.
  • Naming assumptions. It was written in the CiviCRM ecosystem: single-column integer primary keys, foo_id conventions, snake_case tables. UUID/ULID keys and nonstandard PK names are where you should expect to hand-hold it or override.
  • Vendor risk is real. Two stars, zero dependents, no Laravel integration, no docs beyond a README. Pin an exact version in composer.json, vendor the tests you rely on, and don't put it on the critical path of anything you can't afford to replace. It is a good reference implementation of schema-graph traversal even if you end up not shipping it — reading its introspection code is a fast way to learn how to do this yourself with information_schema and a recursive CTE.
  • Extension point: listeners are the intended seam for customization. Anything Lurker gets wrong about your schema (missing edge, custom join table), patch it by registering a listener that pushes the extra edges you know about rather than forking the library.
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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource