Skip to main content

Installation

Requirements

Supported
PHP8.4, 8.5
Laravel13.x
DatabasesSQLite · PostgreSQL · MySQL 8.4 LTS

Every database-touching path is tested against a real PostgreSQL and a real MySQL 8.4 — not just SQLite — so it runs on Laravel Cloud (serverless Postgres plus MySQL 8.4 LTS) out of the box. PostgreSQL is primary, but the append-only and one-active-version guarantees are enforced portably in the app layer on every engine.

Your users may be keyed however you key them

The ledger records the subject by subject_type + subject_id, and subject_id is a 64-character string column. An auto-increment id, a UUID and a ULID all fit, so the package imposes no key type on the model you pass it — HasUuids needs no adapter and no config.

The proof is unaffected by which you use: the tamper-evidence chain hashes the string cast of every field, precisely so a value one driver returns as 2 and another as '2' produces the same hash. Upgrading from a version before 0.18 widens the column in place and rewrites no row.

MariaDB is not supported. It speaks the MySQL wire protocol, but Laravel carries mariadb as its own driver name, so a MariaDB connection never travels the MySQL code path — and the proof-column trigger this package's evidentiary weight rests on is written per engine. The migration stops with a named exception rather than leaving frozen rows editable.

Install

composer require pushery/legal-consent-for-laravel

The service provider is registered automatically.

Publish and migrate

Publish everything at once, or only the group you need, then run the migrations:

php artisan vendor:publish --tag=legal-consent # config + migrations + views + lang
php artisan vendor:publish --tag=legal-consent-config # or just one group
php artisan migrate

Have WireKit installed? Take the group-by-group route instead — the umbrella tag publishes views, and that changes which ones are served.

What the umbrella tag deliberately leaves out

The umbrella tag covers the standard set only. Two groups stay separate because publishing them unasked would be destructive:

  • legal-consent-users-cache — drops a column from your users table.
  • legal-consent-backfill — backfills historical rows.

legal-consent-wirekit is separate for a different reason: it overwrites the plain stubs with their WireKit twins, and that override only ever happens when you ask for it.

With WireKit installed, publish the config and the language files — not the views

legal-consent.ui.variant defaults to auto and serves the WireKit-native views whenever WireKit ≥ 2.26.0 is installed, so nothing has to be published to get the WireKit look — see User interface.

Publishing views is what takes it away again. The umbrella tag — and legal-consent-views — copies the whole resources/views tree into resources/views/vendor/legal-consent/, and the top level of that tree is the plain stubs. A published view is resolved before either of the package's own sets, by design, so those plain copies now win over the WireKit views auto would have served. The WireKit twins arrive in the same copy, one directory down under views/vendor/legal-consent/wirekit/, where nothing looks for them. Nothing errors and legal-consent:doctor reports nothing — the screens simply render unstyled.

So on a WireKit application, publish the two groups you actually edit:

php artisan vendor:publish --tag=legal-consent-config
php artisan vendor:publish --tag=legal-consent-lang
php artisan migrate

If you do publish views — to customize them, which is the only reason to — pull the WireKit twins over the published plain copies afterwards:

php artisan vendor:publish --tag=legal-consent-wirekit --force

--force is not optional there: the files already exist from the view publish, and without it the command leaves them exactly as they are. Pinning legal-consent.ui.variant to plain or wirekit decides the question outright instead of leaving it to what happens to be published.

The individual tags are legal-consent-config, legal-consent-migrations, legal-consent-views, legal-consent-lang, and legal-consent-mail — the last one takes the change-notice mail shell and its theme without the consent screens, for an app that only wants to brand the notice. See User interface for what the view and language tags contain.

Publish your texts — the step that has no error message

After migrate, legal_documents is empty. Consent::published() returns null for every document, and the read path does not fall back to your source files on purpose (a page shows the text of the locale it claims, or nothing). So every legal page you build renders empty: no error, no log, no warning. The install looks finished, the configuration is correct, the Markdown is sitting there — and the pages are shells.

Freeze the whole configured matrix once:

php artisan legal-consent:publish --all --editorial

--editorial is the right classification for a first publish: nobody has agreed to anything yet, so there is nothing to notify about. It is idempotent — text that is already the active version is left untouched. That matters most if your app is cloned: every fresh CI database and every new staging box starts from the empty state, and the failure only shows when someone opens a legal page.

In a deploy script, add --only-missing

php artisan legal-consent:publish --all --only-missing --editorial

The idempotence above holds for unchanged sources. Once you edit a legal text and bump its version, the next --all --editorial is not a no-op — it publishes, and it files the change as editorial, the one mode that notifies nobody. That is a classification your deploy script has no business making.

--only-missing fills gaps and never reads a combination that already has an active version, so it cannot classify a change at all. Keep the bare --all for a person who has looked at the diff and chosen the mode on purpose. Use --dry-run with either to see what a run would do before it does it.

A registered document with no source is reported, not skipped, and the command exits non-zero — and that holds under --only-missing too, for every source except one waiting on an author. A missing markdown file is a deployment missing a file, not a text somebody still has to write. legal-consent:doctor names the same gap at any time, without changing anything.

Next

Continue with the quick start: write a text, publish a version, give a model a ledger, and switch the gate on.