spiral/models
Spiral DataEntity models package. Provides lightweight, type-safe data models/entities for your application with strong static analysis support (Psalm) and test coverage. Part of the Spiral ecosystem; MIT licensed.
This package provides immutable, schema-aware data transfer objects (DTOs) with field mutators and reflection—ideal for enforcing strict data contracts in Laravel or standalone PHP apps. Though it originates from Spiral Framework (hence the spiral/models namespace), it’s framework-agnostic.
Start by installing via Composer:
composer require spiral/models
Define a model class extending Spiral\Models\Model and declare typed properties. Use the @fields annotation, native typed properties (PHP 8+), or the new schema commands (now decoupled from the entity) to define the schema. For v2.2.1+, schema definitions are managed separately via Spiral\Models\Commands\* for better modularity:
use Spiral\Models\Model;
/**
* @property-read int $id
* @property-read string $name
*/
class UserDTO extends Model
{
// Schema is now defined externally via commands or config
// Example: Use `Spiral\Models\Commands\DefineSchema` in a service provider
public function __construct(int $id, string $name)
{
$this->id = $id;
$this->name = $name;
}
}
The first practical use case: validating and encapsulating API responses or database rows with immutable structure.
__set() or dedicated mutator methods (e.g., withName()), leveraging immutability via clone and __clone(). Use newInstance() for updates:
$updated = $dto->newInstance(['name' => 'Updated']);
Spiral\Models\Commands\DefineSchema or via config. Example:
// In a service provider
DefineSchema::for(UserDTO::class)
->fields(['id', 'name'])
->types(['id' => 'int', 'name' => 'string']);
ModelReflection::from($dto) to inspect schema, generate JSON schema, or validate fields. Cache reflections for performance:
$reflection = ModelReflection::from(UserDTO::class);
$schema = $reflection->toJsonSchema();
map() or collect():
$users = UserDTO::fromArray($rawData)->map(fn ($dto) => $dto->withName(ucfirst($dto->name)));
Validator by implementing ShouldValidate:
class UserDTO extends Model implements ShouldValidate
{
public static function rules(): array
{
return ['name' => 'required|string'];
}
}
from(EloquentModel::class) to add behavior without modifying domain models.$dto->name = 'new') fails. Use withName() or newInstance():
$dto->newInstance(['name' => 'new']); // Correct
ModelReflection.strict_types=1 to catch type mismatches early. Mis-typed values may throw exceptions or fail silently.ModelReflection instances in a service provider or use dependency injection:
$this->app->singleton(ModelReflection::class, fn () => ModelReflection::from(UserDTO::class));
DefineSchema for clarity.spatie/data-transfer-object if active maintenance is critical. The decoupled schema system in v2.2.1 improves modularity but may require refactoring existing code.How can I help you explore Laravel packages today?