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.

Addon settings (one screen for every addon)

An addon that wants its values editable in the Control Panel implements Goldnead\BrandContext\Contracts\ProvidesSettings and registers the class with SettingsRegistry::register(). The screen, form, validation, storage (per brand, overrides only) and the config() values at runtime come from here; the addon declares the field list and nothing else. Required methods: settingsNamespace(), settingsConfigPath(), settingsPermission(), settingsGroups().

Four optional methods, read with method_exists() so an addon written against an older version keeps working unchanged:

Method Default What it sets
settingsTitle(): string the addon's name from its package the sidebar entry and the tab (since 1.15)
settingsOrder(): int 100, low sorts first the tab's position
settingsIcon(): string sliders-horizontal the sidebar icon
settingsNavParent(): array none [section, name] of the addon's own CP nav entry, spelled as in its Nav::extend() (since 1.16)

Where the sidebar link goes. An addon with a Control Panel navigation of its own names that entry in settingsNavParent() (['Tools', __('Inbox')]) and gets a "Settings" child under it, which opens a page for that addon alone (no tab bar, the addon's name as title). Every other addon is a tab on one general page, "Addon Suite", with a single entry in the Settings section. If the parent entry is not found at runtime, or the user may not see it, the addon falls back to the general page, so no addon becomes unreachable. The nav is read at boot: the parent must be registered with Nav::extend() in the addon's own provider.

settingsTitle() is for a host that registers settings of its own: without it the title is derived from the namespace, and an app namespace like choirlive-mail shows up as "Choirlive Mail".

Map objects (what a map can point at)

An addon that wants its objects to appear on the funnels map implements Goldnead\BrandContext\Contracts\ProvidesMapObjects and registers it in boot():

A provider answers four questions: which objects exist (objects()), what one object is (find(): label, Control Panel link, on/off/no switch), and its single number over a period (metric(), or null when the addon has none). The map stores only type and id and asks the owning addon every time. MapObjectRegistry::lookup() returns found, missing (the reference is dead) or unavailable (no provider, or it failed). A provider that throws costs its own card, never the screen.

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 ...