boson-php/uri-factory-contracts
Lightweight PHP 8.4+ contracts for URI factory implementations used in the Boson ecosystem. Install via Composer and implement the interfaces to standardize URI creation across packages and applications.
Install the Package
composer require boson-php/uri-factory-contracts
No additional configuration is required for contracts alone.
First Use Case: Define a URI Factory Contract
Create a custom contract extending the base interface (e.g., app/Contracts/UriFactory.php):
use Boson\UriFactoryContracts\Contracts\UriFactory as BaseUriFactory;
interface UriFactory extends BaseUriFactory {
public function createApiUri(string $path, array $params = []): string;
}
Implement the Contract with Laravel’s Url
Create a concrete implementation (e.g., app/Services/LaravelUriFactory.php):
use Boson\UriFactoryContracts\Contracts\UriFactory;
use Illuminate\Support\Facades\Url;
class LaravelUriFactory implements UriFactory {
public function create(string $path, array $params = []): string {
return Url::to($path, $params);
}
public function createApiUri(string $path, array $params = []): string {
return $this->create("/api/v1/{$path}", $params);
}
}
Bind the Implementation
Register the factory in AppServiceProvider:
public function register() {
$this->app->singleton(
\App\Contracts\UriFactory::class,
\App\Services\LaravelUriFactory::class
);
}
Usage in Controllers/Jobs Inject the factory via constructor and use it:
use App\Contracts\UriFactory;
class UserController {
public function __construct(private UriFactory $uriFactory) {}
public function show(User $user) {
$userUri = $this->uriFactory->createApiUri("users/{$user->id}");
return response()->json(['uri' => $userUri]);
}
}
// Define contract
interface ApiUriFactory extends \Boson\UriFactoryContracts\Contracts\UriFactory {
public function createAdminUri(string $path, array $params = []): string;
}
// Implement with Laravel’s Route facade
class RouteApiUriFactory implements ApiUriFactory {
public function create(string $path, array $params = []): string {
return route('api.' . $path, $params);
}
public function createAdminUri(string $path, array $params = []): string {
return $this->create("admin.{$path}", $params);
}
}
// Bind in service provider
$this->app->bind(
\App\Contracts\ApiUriFactory::class,
\App\Services\RouteApiUriFactory::class
);
// Inject and use
class AdminController {
public function __construct(private ApiUriFactory $uriFactory) {}
public function index() {
$adminUri = $this->uriFactory->createAdminUri('dashboard');
// ...
}
}
/users/{id}).class DynamicUriFactory implements \Boson\UriFactoryContracts\Contracts\UriFactory {
public function create(string $path, array $params = []): string {
return route('api.' . $path, $params);
}
}
// Usage in a job
class SendWelcomeEmail implements ShouldQueue {
public function __construct(private DynamicUriFactory $uriFactory) {}
public function handle() {
$userUri = $this->uriFactory->create('users.profile', ['id' => 123]);
Mail::to('user@example.com')->send(new WelcomeEmail($userUri));
}
}
// Test
$mockFactory = Mockery::mock(\Boson\UriFactoryContracts\Contracts\UriFactory::class);
$mockFactory->shouldReceive('create')
->with('users.show', ['id' => 1])
->andReturn('/users/1');
$controller = new UserController($mockFactory);
$response = $controller->show(new User());
$this->assertEquals('/users/1', $response->original['uri']);
class LocalizedUriFactory implements \Boson\UriFactoryContracts\Contracts\UriFactory {
public function __construct(private \Boson\UriFactoryContracts\Contracts\UriFactory $decorated) {}
public function create(string $path, array $params = []): string {
$locale = app()->getLocale();
return $this->decorated->create("{$locale}/{$path}", $params);
}
}
// Bind in service provider
$this->app->bind(
\App\Contracts\UriFactory::class,
fn($app) => new LocalizedUriFactory($app->make(\Boson\UriFactoryContracts\Contracts\UriFactory::class))
);
class ServiceUriFactory implements \Boson\UriFactoryContracts\Contracts\UriFactory {
private string $baseUrl;
public function __construct() {
$this->baseUrl = config('services.auth.base_uri');
}
public function create(string $path, array $params = []): string {
return "{$this->baseUrl}/{$path}" . http_build_query($params);
}
}
// Usage in a service client
class AuthServiceClient {
public function __construct(private ServiceUriFactory $uriFactory) {}
public function getUserToken(string $email) {
$uri = $this->uriFactory->create('auth/login', ['email' => $email]);
return Http::post($uri, ['password' => '...']);
}
}
class QueryUriFactory implements \Boson\UriFactoryContracts\Contracts\UriFactory {
public function __construct(private \Boson\UriFactoryContracts\Contracts\UriFactory $decorated) {}
public function create(string $path, array $params = []): string {
$uri = $this->decorated->create($path);
parse_str(parse_url($uri, PHP_URL_QUERY) ?? '', $query);
$query = array_merge($query, $params);
return str_replace(
parse_url($uri, PHP_URL_QUERY),
http_build_query($query),
$uri
);
}
}
Over-Abstraction
Url::to() calls).Route Cache Invalidation
route() in the factory, changes to route definitions may not reflect until the route cache is cleared.php artisan route:clear
Circular Dependencies
PHP 8.4+ Requirement
Missing URI Validation
public function create(string $path, array $params = []): string {
$path = ltrim($path, '/');
return Url::to("/{$path}", $params);
}
Performance Overhead
Log URI Generation Add logging to trace URI creation:
public function create(string $path, array $params = []): string {
\Log::debug('URI created', ['path' => $path, 'params' => $params]);
return Url::to($path, $params);
}
Use dd() for Inspection
Temporarily dump factory outputs to debug:
How can I help you explore Laravel packages today?