›
byrcsc/laravel-whitelabel · 1.x
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.
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.
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.