›
›
›
  1. docs
  2. ›
  3. byrcsc/laravel-whitelabel
1.x
Browse documentationOpenClose

Getting started

  • Introduction
  • Installation and setup
  • Quick start

Core concepts

  • Brand definitions
  • Fallback and clearing
  • Brand resolution
  • Brand repositories

Using brands

  • Blade components
  • Mail and notifications
  • Queues
  • Spatie Multitenancy

Operations

  • Manage database brands
  • Custom drivers and resolvers
  • Events and listeners
  • Cache management

Reference

  • Configuration
  • Public API
  • Console commands
  • Testing
  • Troubleshooting

Getting started

  • Introduction
  • Installation and setup
  • Quick start

Core concepts

  • Brand definitions
  • Fallback and clearing
  • Brand resolution
  • Brand repositories

Using brands

  • Blade components
  • Mail and notifications
  • Queues
  • Spatie Multitenancy

Operations

  • Manage database brands
  • Custom drivers and resolvers
  • Events and listeners
  • Cache management

Reference

  • Configuration
  • Public API
  • Console commands
  • Testing
  • Troubleshooting

byrcsc/laravel-whitelabel · 1.x

Custom drivers and resolvers.

Load brands from another data source or select them with a custom resolver.

Use a custom driver when definitions live outside config and the included table. Use a custom resolver when a request header or another application value decides which existing brand is active.

Create a repository driver

Implement every method on BrandRepository. This in-memory driver shows the complete contract. Replace its array storage with calls to your data source:

namespace App\Whitelabel;

use Byrcsc\Whitelabel\Brand;
use Byrcsc\Whitelabel\Contracts\BrandRepository;
use Byrcsc\Whitelabel\Exceptions\UnknownBrand;

final class ArrayBrandRepository implements BrandRepository
{
    /** @var array<string, array<array-key, mixed>> */
    private array $definitions = [];

    /** @return array<string, Brand> */
    public function all(): array
    {
        $brands = [];

        foreach ($this->definitions as $id => $definition) {
            $brands[$id] = new Brand(
                $id,
                $definition,
                $this->defaultBrand($id),
            );
        }

        return $brands;
    }

    public function find(string $id): ?Brand
    {
        $definition = $this->definitions[$id] ?? null;

        return $definition === null
            ? null
            : new Brand($id, $definition, $this->defaultBrand($id));
    }

    public function findByDomain(string $domain): ?Brand
    {
        foreach ($this->all() as $brand) {
            if ($brand->domain() === mb_strtolower($domain)) {
                return $brand;
            }
        }

        return null;
    }

    public function has(string $id): bool
    {
        return array_key_exists($id, $this->definitions);
    }

    public function create(string $id, array $definition): Brand
    {
        $this->definitions[$id] = (new Brand($id, $definition))->definition();

        return new Brand($id, $this->definitions[$id], $this->defaultBrand($id));
    }

    public function update(string $id, array $definition): Brand
    {
        if (! $this->has($id)) {
            throw UnknownBrand::named($id);
        }

        return $this->create($id, $definition);
    }

    public function delete(string $id): bool
    {
        if (! $this->has($id)) {
            return false;
        }

        unset($this->definitions[$id]);

        return true;
    }

    public function flush(): void
    {
        // Clear any read cache here. This driver has none.
    }

    private function defaultBrand(string $id): ?Brand
    {
        $defaultId = (string) config('whitelabel.default');

        return $id === $defaultId ? null : $this->find($defaultId);
    }
}

Hydrate non-default brands with the current default as their fallback. Validate definitions before storing them so custom drivers keep the same schema and failure behavior.

Register the driver during application boot:

use App\Whitelabel\ArrayBrandRepository;
use Byrcsc\Whitelabel\BrandRepositoryManager;

app(BrandRepositoryManager::class)->extend(
    'array',
    fn (): ArrayBrandRepository => new ArrayBrandRepository(),
);

Then configure it:

'driver' => 'array',

Custom drivers are wrapped in CachedBrandRepository when whitelabel.cache.enabled is true. Set it to false if the driver already owns its own cache.

Create a resolver

A resolver returns a brand or null:

namespace App\Whitelabel;

use Byrcsc\Whitelabel\Brand;
use Byrcsc\Whitelabel\Contracts\BrandResolver;
use Byrcsc\Whitelabel\Whitelabel;
use Illuminate\Http\Request;

final class HeaderBrandResolver implements BrandResolver
{
    public function __construct(
        private Request $request,
        private Whitelabel $whitelabel,
    ) {}

    public function resolve(): ?Brand
    {
        $id = $this->request->header('X-Brand');

        return is_string($id) ? $this->whitelabel->find($id) : null;
    }
}

Insert it in whitelabel.resolvers according to when it should run:

'resolvers' => [
    Byrcsc\Whitelabel\Resolvers\OverrideResolver::class,
    App\Whitelabel\HeaderBrandResolver::class,
    Byrcsc\Whitelabel\Resolvers\TenantResolver::class,
    Byrcsc\Whitelabel\Resolvers\DomainResolver::class,
    Byrcsc\Whitelabel\Resolvers\DefaultResolver::class,
],

Laravel constructs a resolver only when the chain reaches it. Return null when the resolver has no value to check or cannot find a matching brand. Throw an exception only when an invalid value should stop the remaining resolvers.

What to read next

  • Brand repositories for the included driver behavior to preserve.
  • Brand resolution for resolver construction and order.
  • Public API for complete contract signatures.
PreviousManage database brandsNextEvents and listeners
View source

On this page

  1. Create a repository driver
  2. Create a resolver
  3. What to read next