Skip to content

Commit

Permalink
feat(resolvers): Introduced resolution hook
Browse files Browse the repository at this point in the history
Identity resolvers can now report whether they can resolve for the hook, and tenancies now track the hook where the tenancy was resolved
  • Loading branch information
ollieread committed Sep 10, 2024
1 parent ff67251 commit 9aaacba
Show file tree
Hide file tree
Showing 10 changed files with 149 additions and 30 deletions.
17 changes: 17 additions & 0 deletions src/Contracts/IdentityResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Illuminate\Http\Request;
use Illuminate\Routing\Router;
use Illuminate\Routing\RouteRegistrar;
use Sprout\Support\ResolutionHook;

/**
* Identity Resolver Contract
Expand Down Expand Up @@ -73,4 +74,20 @@ public function routes(Router $router, Closure $groupRoutes, Tenancy $tenancy):
* @return void
*/
public function setup(Tenancy $tenancy, ?Tenant $tenant): void;

/**
* Can the resolver run on the request
*
* This method allows a resolver to prevent resolution with the request in
* its current state, whether that means it's too early, or too late.
*
* @template TenantClass of \Sprout\Contracts\Tenant
*
* @param \Illuminate\Http\Request $request
* @param \Sprout\Contracts\Tenancy<TenantClass> $tenancy
* @param \Sprout\Support\ResolutionHook $hook
*
* @return bool
*/
public function canResolve(Request $request, Tenancy $tenancy, ResolutionHook $hook): bool;
}
18 changes: 18 additions & 0 deletions src/Contracts/Tenancy.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

namespace Sprout\Contracts;

use Sprout\Support\ResolutionHook;

/**
* Tenancy Contract
*
Expand Down Expand Up @@ -108,13 +110,29 @@ public function provider(): TenantProvider;
*/
public function resolvedVia(IdentityResolver $resolver): static;

/**
* Set the hook where the tenant was resolved
*
* @param \Sprout\Support\ResolutionHook $hook
*
* @return $this
*/
public function resolvedAt(ResolutionHook $hook): static;

/**
* Get the used identity resolver
*
* @return \Sprout\Contracts\IdentityResolver|null
*/
public function resolver(): ?IdentityResolver;

/**
* Get the hook where the tenant was resolved
*
* @return \Sprout\Support\ResolutionHook|null
*/
public function hook(): ?ResolutionHook;

/**
* Check if the current tenant was resolved
*
Expand Down
11 changes: 6 additions & 5 deletions src/Http/Resolvers/CookieIdentityResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,14 @@ final class CookieIdentityResolver extends BaseIdentityResolver implements Ident
private array $options;

/**
* @param string $name
* @param string|null $cookie
* @param array<string, mixed> $options
* @param string $name
* @param string|null $cookie
* @param array<string, mixed> $options
* @param array<\Sprout\Support\ResolutionHook> $hooks
*/
public function __construct(string $name, ?string $cookie = null, array $options = [])
public function __construct(string $name, ?string $cookie = null, array $options = [], array $hooks = [])
{
parent::__construct($name);
parent::__construct($name, $hooks);

$this->cookie = $cookie ?? '{Tenancy}-Identifier';
$this->options = $options;
Expand Down
4 changes: 2 additions & 2 deletions src/Http/Resolvers/HeaderIdentityResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ class HeaderIdentityResolver extends BaseIdentityResolver
{
private string $header;

public function __construct(string $name, ?string $header = null)
public function __construct(string $name, ?string $header = null, array $hooks = [])
{
parent::__construct($name);
parent::__construct($name, $hooks);

$this->header = $header ?? '{Tenancy}-Identifier';
}
Expand Down
4 changes: 2 additions & 2 deletions src/Http/Resolvers/PathIdentityResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ final class PathIdentityResolver extends BaseIdentityResolver implements Identit

private int $segment = 1;

public function __construct(string $name, ?int $segment = null, ?string $pattern = null, ?string $parameter = null)
public function __construct(string $name, ?int $segment = null, ?string $pattern = null, ?string $parameter = null, array $hooks = [])
{
parent::__construct($name);
parent::__construct($name, $hooks);

if ($segment !== null) {
$this->segment = max(1, $segment);
Expand Down
4 changes: 2 additions & 2 deletions src/Http/Resolvers/SubdomainIdentityResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ final class SubdomainIdentityResolver extends BaseIdentityResolver implements Id

private string $domain;

public function __construct(string $name, string $domain, ?string $pattern = null, ?string $parameter = null)
public function __construct(string $name, string $domain, ?string $pattern = null, ?string $parameter = null, array $hooks = [])
{
parent::__construct($name);
parent::__construct($name, $hooks);

$this->domain = $domain;

Expand Down
37 changes: 21 additions & 16 deletions src/Managers/IdentityResolverManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Sprout\Http\Resolvers\CookieIdentityResolver;
use Sprout\Http\Resolvers\HeaderIdentityResolver;
use Sprout\Http\Resolvers\PathIdentityResolver;
use Sprout\Http\Resolvers\SessionIdentityResolver;
use Sprout\Http\Resolvers\SubdomainIdentityResolver;
use Sprout\Support\BaseFactory;

Expand Down Expand Up @@ -40,10 +41,10 @@ protected function getConfigKey(string $name): string
/**
* Create the subdomain identity resolver
*
* @param array<string, mixed> $config
* @param string $name
* @param array<string, mixed> $config
* @param string $name
*
* @phpstan-param array{domain?: string, pattern?: string|null, parameter?: string|null} $config
* @phpstan-param array{domain?: string, pattern?: string|null, parameter?: string|null, hooks?: array<\Sprout\Support\ResolutionHook>} $config
*
* @return \Sprout\Http\Resolvers\SubdomainIdentityResolver
*/
Expand All @@ -59,17 +60,18 @@ protected function createSubdomainResolver(array $config, string $name): Subdoma
$name,
$config['domain'],
$config['pattern'] ?? null,
$config['parameter'] ?? null
$config['parameter'] ?? null,
$config['hooks'] ?? []
);
}

/**
* Create the path identity resolver
*
* @param array<string, mixed> $config
* @param string $name
* @param array<string, mixed> $config
* @param string $name
*
* @phpstan-param array{segment?: int|null, pattern?: string|null, parameter?: string|null} $config
* @phpstan-param array{segment?: int|null, pattern?: string|null, parameter?: string|null, hooks?: array<\Sprout\Support\ResolutionHook>} $config
*
* @return \Sprout\Http\Resolvers\PathIdentityResolver
*/
Expand All @@ -87,35 +89,37 @@ protected function createPathResolver(array $config, string $name): PathIdentity
$name,
$segment,
$config['pattern'] ?? null,
$config['parameter'] ?? null
$config['parameter'] ?? null,
$config['hooks'] ?? []
);
}

/**
* Create the header identity resolver
*
* @param array<string, mixed> $config
* @param string $name
* @param array<string, mixed> $config
* @param string $name
*
* @phpstan-param array{header?: string|null} $config
* @phpstan-param array{header?: string|null, hooks?: array<\Sprout\Support\ResolutionHook>} $config
*
* @return \Sprout\Http\Resolvers\HeaderIdentityResolver
*/
protected function createHeaderResolver(array $config, string $name): HeaderIdentityResolver
{
return new HeaderIdentityResolver(
$name,
$config['header'] ?? null
$config['header'] ?? null,
$config['hooks'] ?? []
);
}

/**
* Create the cookie identity resolver
*
* @param array<string, mixed> $config
* @param string $name
* @param array<string, mixed> $config
* @param string $name
*
* @phpstan-param array{cookie?: string|null, options?: array<string, mixed>|null} $config
* @phpstan-param array{cookie?: string|null, options?: array<string, mixed>|null, hooks?: array<\Sprout\Support\ResolutionHook>} $config
*
* @return \Sprout\Http\Resolvers\CookieIdentityResolver
*/
Expand All @@ -124,7 +128,8 @@ protected function createCookieResolver(array $config, string $name): CookieIden
return new CookieIdentityResolver(
$name,
$config['cookie'] ?? null,
$config['options'] ?? []
$config['options'] ?? [],
$config['hooks'] ?? []
);
}
}
34 changes: 32 additions & 2 deletions src/Support/BaseIdentityResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

namespace Sprout\Support;

use Illuminate\Http\Request;
use Sprout\Contracts\IdentityResolver;
use Sprout\Contracts\Tenancy;
use Sprout\Contracts\Tenant;
Expand All @@ -14,9 +15,19 @@ abstract class BaseIdentityResolver implements IdentityResolver
*/
private string $name;

public function __construct(string $name)
/**
* @var array<\Sprout\Support\ResolutionHook>
*/
private array $hooks;

/**
* @param string $name
* @param array<\Sprout\Support\ResolutionHook> $hooks
*/
public function __construct(string $name, array $hooks = [])
{
$this->name = $name;
$this->name = $name;
$this->hooks = empty($hooks) ? [ResolutionHook::Routing] : $hooks;
}

/**
Expand Down Expand Up @@ -50,4 +61,23 @@ public function setup(Tenancy $tenancy, ?Tenant $tenant): void
{
// This is intentionally empty
}

/**
* Can the resolver run on the request
*
* This method allows a resolver to prevent resolution with the request in
* its current state, whether that means it's too early, or too late.
*
* @template TenantClass of \Sprout\Contracts\Tenant
*
* @param \Illuminate\Http\Request $request
* @param \Sprout\Contracts\Tenancy<TenantClass> $tenancy
* @param \Sprout\Support\ResolutionHook $hook
*
* @return bool
*/
public function canResolve(Request $request, Tenancy $tenancy, ResolutionHook $hook): bool
{
return ! $tenancy->wasResolved() && in_array($hook, $this->hooks, true);
}
}
35 changes: 34 additions & 1 deletion src/Support/DefaultTenancy.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ final class DefaultTenancy implements Tenancy
*/
private array $options;

/**
* @var \Sprout\Support\ResolutionHook|null
*/
private ?ResolutionHook $hook = null;

/**
* @param string $name
* @param \Sprout\Contracts\TenantProvider<TenantClass> $provider
Expand Down Expand Up @@ -139,6 +144,7 @@ public function identify(string $identifier): bool

if ($tenant === null) {
$this->resolver = null;
$this->hook = null;

return false;
}
Expand All @@ -164,12 +170,15 @@ public function load(int|string $key): bool
$tenant = $this->provider()->retrieveByKey($key);

if ($tenant === null) {
$this->resolver = null;
$this->hook = null;

return false;
}

$this->setTenant($tenant);

event( new TenantLoaded($tenant, $this));
event(new TenantLoaded($tenant, $this));

return true;
}
Expand Down Expand Up @@ -264,4 +273,28 @@ public function option(string $key, mixed $default = null): mixed
{
return $this->options()[$key] ?? $default;
}

/**
* Set the hook where the tenant was resolved
*
* @param \Sprout\Support\ResolutionHook $hook
*
* @return $this
*/
public function resolvedAt(ResolutionHook $hook): static
{
$this->hook = $hook;

return $this;
}

/**
* Get the hook where the tenant was resolved
*
* @return \Sprout\Support\ResolutionHook|null
*/
public function hook(): ?ResolutionHook
{
return $this->hook;
}
}
15 changes: 15 additions & 0 deletions src/Support/ResolutionHook.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php
declare(strict_types=1);

namespace Sprout\Support;

enum ResolutionHook
{
case Bootstrapping;

case Booting;

case Routing;

case Middleware;
}

0 comments on commit 9aaacba

Please sign in to comment.