Download the PHP package goldnead/statamic-brand-context without Composer

On this page you can find all versions of the php package goldnead/statamic-brand-context. 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 statamic-brand-context

Brand Context

Optional multi-brand (multi-tenant) foundation for Statamic addons.

Single-brand by default. Most Statamic installs use the dependent addons exactly as before — one brand, no switcher, no visible machinery. The global scope is a no-op and every record belongs to a single default brand.

Multi-brand behind a flag. Flip brand-context.multi_brand on (optionally gated behind a license via license_check, so multi-brand can ship as a premium feature) to get hard brand isolation: the global scope filters every branded model by the current brand, new records are stamped with it, and the Control-Panel brand switcher appears.

The database schema is identical in both modes (brand_id everywhere, backfilled to the default brand), so enabling multi-brand later needs no migration.

Requirements

PHP 8.2 or newer
Laravel 12.40 or newer, or 13 — the range statamic/cms ^6.0 itself allows
Statamic 6.0 or newer
Database Any Laravel-supported driver. The migrations are verified against MySQL 8 in CI; SQLite has no InnoDB key-length limit and is not a substitute for that run.

The Control-Panel surface (brand switcher, addon settings screen, the brand field on the user form) needs Statamic. The rest of the package boots in a plain Laravel application without it.

Installation

That is the whole install for single-brand mode. The default brand is created by the migration and the global scope is a no-op, so nothing changes visibly.

To enable multi-brand, publish the config and flip the flag:

The flag is read once at boot. Changing it takes effect on the next request, and on a deployed site you need php artisan config:clear if the config is cached.

The Control-Panel assets are published automatically by Statamic. If you have disabled that, or the switcher does not appear after an upgrade:

Publish tags

Tag What it publishes
brand-context-config config/brand-context.php
brand-context-migrations The brands and brand_user migrations, if you want to edit them
brand-context-cp The built Control-Panel bundle
brand-context-translations lang/vendor/brand-context/{en,de}

Building the Control-Panel assets

Only needed when working on this package itself. @statamic/cms resolves from the installed vendor directory, so Composer must run first:

Concepts

Isolation guarantees (multi-brand mode)

Usage

Brand members (which CP users belong to a brand)

The global scope isolates Eloquent models. A Statamic user is not one — with the file users repository it is not a database row at all — so "the users of this brand" cannot be expressed by scoping and gets its own answer:

Membership is brand affiliation, never authorisation. Consumers combine it with their own permission check:

Write access is attach() / detach(), both idempotent and both taking the same brand argument.

In the Control Panel it is a Brands field on Statamic's own user form — a multi-select of every brand, shown only when multi-brand is on and there is more than one brand to choose from. The value is never stored on the user: a Statamic user may be a yaml file rather than a database row, so the field is read from and written back to brand_user around the save. There is nothing to publish, configure or grant for it.

The rule that will surprise you

A user with no membership at all counts as a member of every brand.

Every install upgrading into this feature starts with an empty brand_user table. Strict filtering would empty every assignee dropdown, every team notification and every approval list on the day of the upgrade — and it would look exactly like a permissions bug. So nothing changes until somebody deliberately assigns a user. The first assignment is what narrows that user down, and it narrows them everywhere at once: from then on they belong only to the brands listed for them. Removing their last assignment puts them back into every brand; there is deliberately no way to express "member of nothing", which is what revoking a permission is for.

includes(), usersOf(), filter() and brandsOf() apply the rule. assignedUserIdsOf() and assignedBrandIdsOf() return the raw rows and do not — they are for rendering and auditing the assignments themselves, never for deciding who may be offered, notified or assigned.

Notes for consumers

Public routes

Links in an e-mail are opened without a session, so no brand is current and the fail-closed scope hides the record the link points at. The brand comes from the token instead:

The three arguments are the model, the column to look the value up in, and the route parameter (or input field) carrying it.

The column must carry a unique index across all brands. One token, one record, one brand — that is the whole safety argument. If two records answer, brandForUnique() throws AmbiguousBrandRecord rather than guessing, because guessing means serving one brand's record to another brand's visitor. Never pass a column that is unique only per brand.

Nothing is aborted by the middleware: an unknown value sets no brand, the scope stays closed, and the controller produces the response it always produced. And the brand is set explicitly on every request — never inherited from the last one, which matters the moment the app runs in a long-lived process.

Queued jobs

A worker has no request behind it, so nothing resolves a brand — and under multi-brand that is not "unscoped", it is fail-closed. Every branded query in the job returns zero rows, the job finishes successfully, and nothing anywhere says so: no exception, no failed_jobs row, no log line.

Since 1.9.0 the brand that was current when the job was pushed is written into the queue payload and set again while the job runs. Nothing has to be done for it; there is no trait and no base class.

Three properties are worth knowing:

An explicit BrandContext::runFor() inside a job still wins, and still makes sense wherever the job knows better than its dispatcher did — a digest command looping over every brand, for instance.

Sender identity (who a brand's mail goes out as)

A mail belongs to a brand, so the address it comes from and the transport it leaves through belong to the brand too. Both live in brands.settings.mail:

Why the transport and not just the From. A relay that verifies sending domains per account (Scaleway TEM, Postmark, SES) refuses — or silently replaces — a From it does not own. Sending brand A's mail through the account that only knows brand B is how a reader ends up with brand A's newsletter under brand B's name. The two values have to be chosen together, which is why they live in one place and are resolved in one place. The SMTP credentials stay in the environment and never reach the database, a backup or a CP export.

Sending goes through BrandMailer, which puts the identity on the message and never into the config:

Four rules, and the reasons they are rules:

A mailable that travels this way must not overwrite a From that is already there:

A host that keeps sender identities somewhere else rebinds one contract:

Addons that send mail (statamic-marketing, -notifications, -preference-center, -automations) extend that interface in their own namespace and bind their own default, so a host can answer the question for marketing post alone without touching transactional post. Rebinding the contract above changes it for everything that has not been rebound individually.

Testing

CI runs all of it on every push, plus a job that rebuilds resources/dist and fails if the committed bundle has drifted from its sources.

Support

Only the latest version of this addon is supported, against the Statamic major it targets. Bugs and questions go to GitHub issues; bugs in Statamic itself belong in statamic/cms.

Security reports do not go into a public issue — see SECURITY.md.

Changelog and license

LICENSE.md.


All versions of statamic-brand-context with dependencies

PHP Build Version
Package Version
Requires php Version ^8.2
inertiajs/inertia-laravel Version ^2.0 || ^3.0
laravel/framework Version ^12.40|^13.0
statamic/cms Version ^6.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 goldnead/statamic-brand-context contains the following files

Loading the files please wait ...