draw/contracts
Lightweight PHP contracts for Draw packages. Provides shared interfaces and abstractions to standardize implementations across components, improving interoperability, testing, and decoupling in Laravel or framework-agnostic projects.
Installation Add the package via Composer:
composer require draw/contracts
No publisher or service provider is required—this is a pure abstraction layer.
First Use Case: Defining a Contract
Use the Contract trait to define a formal interface for your domain logic:
use Draw\Contracts\Contract;
class UserContract implements Contract
{
public function validate(array $data): bool
{
// Validation logic
}
}
Where to Look First
src/Contract.php: Core trait defining the Contract interface.src/Exceptions/: Custom exceptions for contract violations.tests/: Example implementations and edge cases.Define Contracts
Extend Contract to enforce rules on data, services, or behaviors:
class PaymentContract implements Contract
{
public function validate(array $data): bool
{
return !empty($data['amount']) && is_numeric($data['amount']);
}
}
Integrate with Laravel Use contracts in service providers, form requests, or domain services:
class PaymentService
{
public function process(PaymentContract $contract, array $data)
{
if (!$contract->validate($data)) {
throw new \InvalidArgumentException('Invalid payment data');
}
// Process payment...
}
}
Dependency Injection
Bind contracts to interfaces in AppServiceProvider:
$this->app->bind(
PaymentContract::class,
function ($app) {
return new PaymentContract();
}
);
Validation Layer Combine with Laravel’s validation:
use Illuminate\Support\Facades\Validator;
$validator = Validator::make($request->all(), [
'amount' => 'required|numeric',
]);
if ($validator->fails()) {
throw new \Draw\Contracts\Exceptions\ContractValidationException(
$validator->errors()
);
}
No Built-in Enforcement The package provides abstractions only—you must manually validate contracts. Example:
// ❌ Won't auto-validate
$service->process(new PaymentContract(), $data);
Overhead for Simple Cases Avoid over-engineering for trivial validations. Use Laravel’s built-in validation when possible.
Exception Handling
Custom exceptions (ContractValidationException) are thrown but require manual catching:
try {
$service->process($contract, $data);
} catch (\Draw\Contracts\Exceptions\ContractValidationException $e) {
// Handle gracefully
}
Compose Contracts Chain multiple contracts for complex logic:
class CompositeContract implements Contract
{
private $contracts;
public function __construct(Contract ...$contracts)
{
$this->contracts = $contracts;
}
public function validate(array $data): bool
{
return collect($this->contracts)
->every(fn ($contract) => $contract->validate($data));
}
}
Testing Strategies Mock contracts in unit tests:
$mockContract = $this->createMock(Contract::class);
$mockContract->method('validate')->willReturn(true);
Extending the Package
\Draw\Contracts\Exceptions\ContractException.Contract methods in child classes.Performance Note For high-throughput systems, cache contract validation results if data is static:
private $cache = [];
public function validate(array $data): bool
{
$key = md5(serialize($data));
return $this->cache[$key] ??= parent::validate($data);
}
How can I help you explore Laravel packages today?