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

Laravel Report Url Laravel Package

jeffersongoncalves/laravel-report-url

A lightweight Laravel package for adding URL reporting to your application. It provides configuration, routes, and controller scaffolding to collect, store, and manage reports associated with URLs, making it easy to integrate into existing Laravel projects.

View on GitHub
Deep Wiki
Context7

Getting Started

  1. Install and scaffold:
composer require <vendor>/report-url
php artisan vendor:publish --tag="report-url-config"
php artisan vendor:publish --tag="report-url-migrations"
php artisan migrate
  1. Look here first: config/report-url.php. Four keys drive almost all behaviour — disk (must be a private disk), ttl (default 7 days), middleware (applied to the report-url.download route, alongside signed), and restrict_to_owner. Confirm your disk before writing any code; pointing this at public defeats the entire purpose of the package.

  2. First use case — stop emailing large report attachments:

use Vendor\ReportUrl\Facades\ReportUrl;

$link = ReportUrl::store($pathToGeneratedFile, ttl: now()->addDays(3));

return (new ReportReady($link->url(), $link->expires_at))
    ->to($user->email);
  1. Schedule the cleanup immediately, otherwise links and files accumulate forever:
// routes/console.php
Schedule::command('report-url:prune')->daily()->onOneServer();

Implementation Patterns

Two entry points, pick by what you already have. storeFile() when the report generator hands you an UploadedFile/File-like object; store() when you have a path on disk or raw contents from a PDF/CSV writer. Both return the same object exposing ->url() and ->expires_at.

Per-call TTL for tiered retention. Keep the config default sensible (7 days) and override at the call site for reports with different sensitivity:

ReportUrl::store($path);                             // config default
ReportUrl::store($path, ttl: now()->addHours(24));   // short-lived
ReportUrl::store($path, ttl: now()->addDays(30));    // monthly statements

Email the link, keep the file private. The whole workflow is: generate → store() → put ->url() in a Mailable/Notification → let the recipient click through the signed route. Render ->expires_at in the email body so users don't discover expiry by hitting a 410.

Audit and analytics via the event. ReportUrlDownloaded is the hook for "who actually opened their report" — listen for it and persist to an activity log or fire an internal webhook. The built-in download counter and last_downloaded_at cover the simple case; the event is where you add IP, user agent, or per-recipient attribution.

Owner restriction for multi-tenant apps. With restrict_to_owner enabled, treat the link as user-scoped: generate it for a specific user and ensure guests/anonymous links are handled deliberately. This is the difference between "signed link" (anyone holding it) and "signed link for this user" (session-bound), and it materially changes what you can safely put behind it.

Scheduled-report integration. With laravel-saved-filter 1.9+ installed, the package binds HostsReportFiles automatically — scheduled reports too large to attach get emailed as download links with no code change on your side. Pin the constraint in composer.json so the binding doesn't silently disappear on a downgrade or a lockfile rebuild.

Testing. Storage::fake() for the disk, Event::fake() to assert ReportUrlDownloaded, and $this->get($link->url()) to hit the real signed route. Use $this->travel(8)->days() to prove the 410 path rather than mocking clocks by hand.

Gotchas and Tips

  • The signature covers the expiry. Changing ttl in config has no effect on links already issued; there is no "extend this link" operation. If you need sliding expiry, issue a new link.
  • Know your status codes. Tampered/unsigned URL → signature failure (403-ish). Correct signature but past expiry → 410 Gone. Tests and front-end error handling should distinguish them; a 403 usually means a broken URL in an email template, not an expired report.
  • restrict_to_owner is the sharp edge. Enabling it changes access semantics for every existing link style, and behaviour for guests and queued notifications is easy to get wrong. Verify with a second user account before shipping, and check what happens when the owning user is deleted or the session is on another device.
  • The download counter is telemetry, not security. It increments on hit; it does not rate-limit, revoke, or detect sharing. Don't market it as access control.
  • Prune carefully. Run report-url:prune --dry-run in production first and read the count. Keep onOneServer() and consider withoutOverlapping() — concurrent prunes or a prune racing an in-flight download will produce confusing 404/500s for a user who was legitimately mid-download.
  • Middleware and the web group. If you put session/auth middleware on the route, the web group must be present or auth silently never resolves a user. The signed middleware stays regardless — don't remove it while adding your own.
  • Route naming. report-url.download is a stable public identifier; if you override the route or its prefix, update any hardcoded route() calls and any links already sitting in users' inboxes (they won't heal).
  • Storage growth is your responsibility. The TTL controls link lifetime; pruning controls disk. A short TTL with a broken scheduler leaves orphaned files indefinitely — alert on prune failures.
  • Extension points: publish the config, swap the disk, add middleware, and listen to the event. Those four cover most customisation; avoid forking the route or the model unless you must.
  • Integration is version-gated. The laravel-saved-filter binding requires 1.9+; below that, oversized scheduled reports fall back to attachment attempts and can fail at the mail layer rather than in an obvious place.
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.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
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