Skip to content

Add a database driver for reports - #700

Open
jure-knezovic wants to merge 7 commits into
statamic:7.xfrom
jure-knezovic:feature/reports-database
Open

jure-knezovic wants to merge 7 commits into
statamic:7.xfrom
jure-knezovic:feature/reports-database

Conversation

@jure-knezovic

Copy link
Copy Markdown

Closes #698

On hosts whose filesystem is not shared between instances or does not survive a deploy, a report written by one process is never seen by the next request: the create action lands on a 404, chunks end up on the queue worker's disk, and finished reports vanish on deploy. Redirects and errors can already live in the database. This gives reports the same option.

What's new

  • reports.driver config key, file by default. Setting it to database binds an Eloquent ReportRepository that stores reports, their pages and their pending chunks in three tables.
  • reports.directory config key for the file driver, defaulting to the existing storage/statamic/seopro/reports. A config published before the key existed keeps working.
  • php please seo-pro:database-reports publishes the migration, runs it and offers to import existing file reports with their pages, mirroring database-redirects and database-errors.
  • Documentation for both, in a Storage section under Reports.

How it works

The storage concerns of Report, Chunk and Page moved behind a ReportRepository contract. The existing YAML layout is the file driver, so nothing changes for current installations. The Eloquent driver saves each page with a single upsert, keeps dates in the app timezone, and deletes a report's rows in one transaction.

Two small fixes surfaced along the way and are in their own commits: a reports section without queue_chunk_size made chunk() return nothing and left the report pending forever, so the key is now read with its default; and a report saved several times during generation got a fresh date on each save, so the date is now fixed on the first save.

Breaking change

Report lost six public methods that only made sense for the YAML layout: parentFolder(), chunksFolder(), path(), exists(), load() and the static preparePath(). load() became hydrate(array $raw). Nothing in the addon used them; a site that called them directly will need to go through the repository instead.

Tests

Repository tests for both drivers, generation and the control panel on the database driver, and the command including re-runs and the empty-import case. The full suite passes, Pint is clean.

🤖 Generated with Claude Code

jure-knezovic and others added 7 commits October 20, 2026 19:02
Report, Chunk and Page wrote YAML straight to the storage directory. The
storage concerns now live behind a ReportRepository contract, with the
existing YAML layout as the file driver, so a second driver can be bound
without touching the reporting logic.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The file driver reads its directory from reports.directory, like the
redirects and errors drivers do. A config published before the key
existed keeps working: the repository falls back to the old storage path,
since mergeConfigFrom() only fills in missing top-level sections.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A reports section without queue_chunk_size, as a partially published
config or a nested test override produces, made chunk() return nothing
and left the report pending forever. Read the key with the shipped
default instead, as the length rules already do.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A new report is saved several times while it generates. The date was
resolved to now() on each save without being stored on the instance, so
the in-memory report drifted from what was persisted.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Where the filesystem is not shared between processes or does not survive
a deploy, a report written by one process is never seen by the next
request. Setting reports.driver to database binds an Eloquent repository
that stores reports, their pages and their pending chunks in three tables.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Publishes the reports migration, runs it and offers to import the
existing file reports with their pages, mirroring database-redirects and
database-errors. Re-running it is safe: the migration is published once
and imported reports keep their ids.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Report::latest() has been public since the v3 restructure and loaded every
report to pick the first. It now delegates to the repository like all()
and find(), so the database driver answers it with a single query.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ability to store reports in the database

1 participant