dragon-code/contracts
Dragon Code Contracts provides a lightweight set of PHP interfaces (contracts) you can reuse across any project to standardize key behaviors, improve type-safety, and keep implementations decoupled. Ideal as a shared dependency for packages and applications.
Installation:
composer require dragon-code/contracts
Add to composer.json under require-dev if only for testing:
"dragon-code/contracts": "^2.25"
First Use Case:
Implement a Dtoable contract for a simple data transfer object:
use DragonCode\Contracts\Dto\Dtoable;
class UserDto implements Dtoable
{
public function __construct(
public string $name,
public int $age
) {}
// No additional methods required for basic usage
}
Where to Look First:
vendor/dragon-code/contracts/src/Contracts/.Cache\Store (caching operations)Queue\ShouldQueue (job queueing)Dto\Dtoable (data transfer objects)Http\Builder (HTTP request building)use DragonCode\Contracts\Dto\Dtoable;
class CreateUserRequest implements Dtoable
{
public function __construct(
public string $name,
public string $email,
public string $password
) {}
}
use DragonCode\Helpers\Dto\DtoHelper;
$dto = DtoHelper::fromArray($request->all(), CreateUserRequest::class);
use DragonCode\Contracts\Queue\ShouldQueue;
use DragonCode\Contracts\Queue\ShouldBeUnique;
class SendWelcomeEmail implements ShouldQueue, ShouldBeUnique
{
public function handle(): void
{
// Job logic
}
public function uniqueKey(): string
{
return 'email_' . $this->userId;
}
}
use DragonCode\Contracts\Cache\Store;
use DragonCode\Contracts\Cache\Ttl;
class UserCacheService
{
public function __construct(private Store $cache) {}
public function getUser(int $id): ?array
{
return $this->cache->get("user_{$id}");
}
public function rememberUser(int $id, callable $callback, Ttl $ttl): array
{
return $this->cache->remember("user_{$id}", $ttl->value(), $callback);
}
}
use DragonCode\Contracts\Http\Builder;
class ApiClient
{
public function __construct(private Builder $builder) {}
public function getBaseUrl(): string
{
return $this->builder->getBaseDomain();
}
public function buildUrl(string $endpoint): string
{
return $this->builder->url($endpoint);
}
}
use DragonCode\Contracts\Database\Migrations\ShouldMigrate;
class CreateUsersTable implements ShouldMigrate
{
public function up(): void
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('users');
}
}
Bind contracts to Laravel’s container in AppServiceProvider:
use DragonCode\Contracts\Cache\Store;
use Illuminate\Support\Facades\Cache;
public function register(): void
{
$this->app->bind(Store::class, function ($app) {
return new class($app['cache']) implements Store {
public function __construct(private \Illuminate\Contracts\Cache\Store $cache) {}
// Delegate methods to Laravel's cache store
};
});
}
Use contracts for dependency injection in tests:
use DragonCode\Contracts\Cache\Store;
public function testCacheOperations()
{
$mockCache = $this->createMock(Store::class);
$mockCache->method('get')->willReturn(['name' => 'John']);
$service = new UserCacheService($mockCache);
$this->assertEquals(['name' => 'John'], $service->getUser(1));
}
Leverage traits or interfaces to add contracts to existing classes:
use DragonCode\Contracts\Queue\ShouldQueue;
class ExistingJob
{
// Existing implementation
}
class QueueableJob extends ExistingJob implements ShouldQueue
{
public function handle(): void
{
// Existing logic
}
}
Laravel Version Mismatches:
ShouldQueue) may not work as expected.^2.20) if targeting Laravel 10.Over-Engineering for Simple Projects:
Cache Contract Inconsistencies:
Cache\Store contract mirrors Laravel’s cache store but may miss newer methods (e.g., pull, forget).$this->cache->store()->forget($key); // If not in contract
Queue Contracts and Laravel Jobs:
ShouldQueue is not a drop-in replacement for Laravel’s ShouldQueue interface. It’s designed for custom queueable logic.Illuminate\Contracts\Queue\ShouldQueue for full compatibility:
use DragonCode\Contracts\Queue\ShouldQueue as DragonShouldQueue;
use Illuminate\Contracts\Queue\ShouldQueue as LaravelShouldQueue;
class MyJob implements LaravelShouldQueue, DragonShouldQueue
{
// ...
}
DTO Serialization Quirks:
Dtoable alone does not enforce serialization. Pair with DragonCode\Helpers\Dto\DtoHelper for full functionality.class UserDto implements Dtoable
{
public function __construct(
public string $name,
public AddressDto $address // Must also implement Dtoable
) {}
}
Interface Not Found Errors:
composer dump-autoload).vendor/composer/autoload_psr4.php includes:
'DragonCode\\Contracts\\' => [__DIR__ . '/../dragon-code/contracts/src/Contracts'],
Method Not Implemented:
Cache Contract Conflicts:
$this->app->bind(Store::class, function ($app) {
return $app['cache']->store('redis');
});
Custom TTL Contracts:
Extend Cache\Ttl for domain-specific timeouts:
use DragonCode\Contracts\Cache\Ttl;
class SessionTtl implements Ttl
{
public function value(): int
{
return 60 * 30; // 30 minutes
}
}
Queue Deduplication:
Implement ShouldBeUnique for idempotent jobs:
use DragonCode\Contracts\Queue\ShouldBeUnique;
class ProcessPayment implements ShouldBeUnique
{
public function uniqueKey(): string
{
return 'payment_' . $this->paymentId;
}
}
HTTP Domain Logic:
Extend Http\Builder for multi-tenant domains:
use DragonCode\
How can I help you explore Laravel packages today?