Download the PHP package netipar/laravel-consent without Composer

On this page you can find all versions of the php package netipar/laravel-consent. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package laravel-consent

Consent for Laravel

Latest Version on Packagist Tests Total Downloads

Headless cookie consent for Laravel: a versioned consent cookie, a server-side API and a Blade directive, a framework-free JavaScript core with Google Consent Mode v2 (Basic), and unstyled Alpine/Livewire and Vue 3 adapters. The package ships behaviour and accessibility wiring — your application owns all markup and styling (Tailwind, shadcn-vue, plain CSS).

Coming from netipar/cookie-consent 1.x? See UPGRADE.md.

Contents

Why Consent

Requirements

5-minute quickstart (Blade and Livewire)

1. Install.

2. Place the config and the banner in your layout:

3. Register the adapter before Alpine starts (resources/js/app.js):

4. Let visitors change their mind with a footer control:

5. Style it. The default markup has no classes. Pass class="…" to <x-consent::banner> and use the settings slot to restyle the dialog; see the cookbook.

Inertia and Vue 3 quickstart

Share the client config (it is resolved per request, so the locale is right):

Install the plugin in the app setup shared by the client and server entries. It is SSR-safe: on the server (no document) the plugin provides an inert manager — undecided, prompt open, no-op actions — so <ConsentBanner> renders without throwing; the consent UI is effectively client-side (the browser creates the real manager and hides the prompt on mount for visitors who already decided):

Use the renderless <ConsentBanner> with your own components. Slot props are raw refs, so read them with .value:

A complete shadcn-vue dialog is in the integration guide.

The cookie contract

Cookie Value Notes
__cookie_consent the granted categories, comma-separated and URL-encoded: necessary%2Canalytics Required categories are always present once a decision exists.
__cookie_consent_v the config version, verbatim A decision is valid only when both cookies exist and the version equals the config.
__cookie_consent_id a UUID Only when the consent log is enabled. A necessary cookie, never part of the value cookie.

All are written with Path=/, Max-Age = lifetime_days × 86400, SameSite=Lax and Secure on HTTPS (all configurable). Names, lifetime and attributes live in config/consent.php; CONSENT_LIFETIME_DAYS overrides the lifetime (default 365).

Complete withdrawal

Withdrawing consent must also remove what was set under it, so list the cookies of every category:

An entry is an exact name or a prefix pattern ending in * (or a rich array, see business.safety.google/adscookies; verify them against the tags you use.

When cookies are deleted. On every transition that removes a previously granted category — saving fewer categories, "Reject all", withdrawing — and, for all optional categories, when a stale version is detected. A change made in another tab is handled the same way on the next sync(), which runs on livewire:navigated, when the tab becomes visible again (visibilitychange) and on a bfcache restore (pageshow with persisted); disable the last two with syncOnVisibility: false. Required categories are never purged.

Where. Each cookie is deleted without a Domain attribute, then with the current host, then with each parent domain (www.example.com → example.com), because Google Analytics sets _ga* on the registrable domain.

Reload. Scripts that already ran cannot be unloaded: gtag.js would simply recreate _ga on its next hit. When a revoked category had something loaded for it (a gated script ran, a Google tag was configured) and reload_on_revoke is true (the default), the page reloads once after the listeners have finished. Set it to false if you handle this yourself.

Housekeeping on every page load. Once a visitor has decided, the JS core removes the configured cookies (categories.*.cookies, exact names and prefix* patterns, on the host and every parent domain) of every optional category that is not granted — on the initial page load and on every re-sync (Livewire navigation, tab becoming visible, bfcache restore), not only at the moment of a revocation. So a tag that loaded before the decision, or a second tab that kept writing _ga*, cannot leave residue behind. Visitors who have not decided yet are never touched (nothing is purged before a decision), required categories are never purged, and this cleanup never triggers a page reload, because nothing was loaded for a category that is not granted.

What the package does not do. It does not expire third-party cookies from the server. A response can only guess the Domain and Path of a cookie the server merely saw by name, the two transitions that matter (reject and withdraw) happen in the browser with no round trip, and a response-mutating middleware is exactly what this package avoids. If an audit finds residue, add a terminating middleware in your application.

Consent log

An optional proof of consent (GDPR Art. 7(1)): who decided what, under which policy version, and when. Off by default.

Enable.

Set the variable before the first migrate: the package loads its migration only while the log is enabled. A published copy keeps the same file name, so it never runs twice. Pick log.table (at most 36 characters) and log.connection before migrating and do not change them afterwards. migrate:rollback runs down(), which drops the table and the proof in it.

What is recorded — one append-only row per decision in consent_logs, with the columns the shipped migration defines:

Column Type Notes
id bigint, primary key
event_id uuid, unique Generated by the browser per transition; makes the POST idempotent. Stored lowercase.
consent_id uuid From the __cookie_consent_id cookie; groups one visitor's decisions. Stored lowercase. Indexed together with created_at.
action string(16) accept_all, reject_all, save or withdraw.
granted json Granted category keys in config order; required categories always included.
version string(64) The config version the visitor saw, as sent.
url string(2048), nullable The pathname only — query strings and fragments are dropped.
ip_hash string(64), nullable HMAC-SHA256 with the app key over the /24 (IPv4) or /48 (IPv6) prefix. The raw IP is never stored. log.store_ip switches it off.
user_agent string(255), nullable Raw, scrubbed to valid UTF-8 and truncated to log.user_agent_max_length. log.store_user_agent switches it off.
user_id string(64), nullable, indexed Auth::id() when a user is authenticated; an identifier longer than 64 characters is stored as null. log.store_user_id switches it off.
created_at datetime, indexed Server time. There is no updated_at.

The IP is both truncated and keyed: truncation alone leaves 24 bits of the real address, and a keyed hash of a full IPv4 address can be brute-forced by anyone holding the key. Records from the same /24 still correlate within one application; rotating APP_KEY breaks that correlation for older rows. The user agent stays readable so a data subject can recognise "their browser" in a dispute.

The endpoint. After every actual change of the decision the JS core sends POST /consent/log (the prefix comes from log.route.prefix) with credentials: 'same-origin' and keepalive: true, and never waits for the answer:

The route runs in the web middleware group (session and CSRF — the core sends the XSRF-TOKEN cookie value as X-XSRF-TOKEN and, without that cookie, the csrf-token meta tag value as X-CSRF-TOKEN) plus throttle:consent-log (log.route.throttle requests per minute per IP). Answers: 204 stored, 422 invalid payload, 419 CSRF failure, 429 throttled. Replaying an event_id is a 204 no-op. With the log disabled the route does not exist (404). Prefix, middleware and throttle are fixed at boot.

Failure semantics. A failed log call dispatches a consent:log-failed event on document (detail: { action, error }) and is not retried: the cookies are authoritative, so a lost entry is a lost proof, not a broken consent. Repeating an identical decision is not logged — the original record already proves it.

Lookup.

history() throws NETipar\Consent\Exceptions\LogDisabledException while the log is disabled and InvalidArgumentException for a non-UUID.

Retention. log.retention_days (default 365, never less than cookie.lifetime_days) drives ConsentLog::prunable(). Package models are not discovered by model:prune, so schedule it explicitly in routes/console.php:

Describe the log, its purpose and its retention in your privacy notice.

Cookie table

Declare cookies with metadata and render them on your policy page, so the page cannot drift from what the site really sets:

Plain strings are purge-only; rich arrays are purged and listed. purpose is a text, a lang key or a [locale => text] map; lifetime is 'session' or an ISO 8601 duration, rendered in the unit as written and in the current locale (P90D → "90 days" / "90 nap").

Consent::cookieTable() returns the same data for your own markup (a slot replaces the default table). In Vue, pass Consent::cookieTable() and __('consent::consent.cookies.headings') as Inertia props to <ConsentCookieTable :categories="…" :headings="…" />. Details: integration guide.

Texts and languages

The package ships de, en, es, fr and hu (formal register). Every string — banner copy, category labels and descriptions, table headings — comes from lang/<locale>/consent.php, and Consent::clientConfig() resolves them for the request locale, so the Blade view, Alpine and Vue render exactly the same text.

Override only the keys you want to change. Props and slots always win over the lang texts. policy_url accepts a URL, a [locale => url] map or a route name, resolved per request.

Known limitation: the strings are resolved on the server per request and captured when the JS manager is created. A client-side-only locale switch without a page render keeps the previous strings until a reload or re-initialisation.

Server-side API

allows() throws UnknownCategoryException for a key that is not configured. Pass a Request as the last argument to evaluate a specific request.

In Blade:

@unlessconsent('x') and @elseconsent('x') (always with an argument) exist as well. Never write a bare @elseconsent — it fails at run time; use @else. Keep whitespace before @else and @endconsent. @consentConfig prints <script type="application/json" id="consent-config">…</script>.

Gating third-party scripts

Change type to text/plain and name the category:

This works with src, async/defer, data-consent-type="module" and several categories (data-consent="analytics marketing" — all must be granted). The nonce is copied. Scripts are re-scanned after every decision, on livewire:navigated and on manager.sync(root). See the integration guide.

Google Consent Mode v2

Basic mode: Google tags do not load at all without consent. After the first grant of a tag's category the core sends consent default with the four signals (ad_storage, analytics_storage, ad_user_data, ad_personalization), loads gtag.js and configures the granted tags. A later grant sends consent update plus config for the tags that just became granted. A revoke sends consent update with the signals denied, purges the cookies and — since loaded tags cannot be unloaded — reloads (see Complete withdrawal). ads_data_redaction and url_passthrough are gtag('set', …) calls made before the loader. Remove any hand-written gtag.js snippet from your layout.

Accessibility

The default markup (Blade and Vue) follows one contract, and replacement markup should too:

Interop with laravel-office-leads

netipar/laravel-office-leads reads the same cookie by default (__cookie_consent, category marketing), so attribution tracking honours the banner with no configuration. To bind it explicitly:

Testing in your application

Send the two cookies as a raw header:

Use the version from your config. A request without the cookies has no decision, so optional categories are denied.

Configuration

config/consent.php is validated at boot into a typed ConsentConfig; a bad value fails fast with the offending key. Full reference in magyarul).


Licensed under the MIT license.


All versions of laravel-consent with dependencies

PHP Build Version
Package Version
Requires php Version ^8.3
illuminate/cache Version ^12.0||^13.0
illuminate/contracts Version ^12.0||^13.0
illuminate/database Version ^12.0||^13.0
illuminate/http Version ^12.0||^13.0
illuminate/routing Version ^12.0||^13.0
illuminate/support Version ^12.0||^13.0
illuminate/translation Version ^12.0||^13.0
illuminate/validation Version ^12.0||^13.0
illuminate/view Version ^12.0||^13.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package netipar/laravel-consent contains the following files

Loading the files please wait ...