shedeza / sybase-orm
Pure PHP ORM for Sybase ASE — framework-agnostic
Requires
- php: ^8.1
- ext-pdo_dblib: *
- psr/log: ^2.0|^3.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.50
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.0
README
ORM puro en PHP para Sybase ASE, independiente de framework. Soporta mapeo de entidades con atributos PHP 8.1, consultas OQL, QueryBuilder, relaciones, herencia, caché de dos niveles, migraciones y más.
Estado: Este proyecto es estable y apto para uso en producción. Reporta cualquier problema en Issues.
Características
- Mapeo de entidades con atributos nativos de PHP 8.1 (sin XML ni YAML)
- Patrón Data Mapper con Unit of Work e Identity Map
- Lenguaje de consultas OQL y QueryBuilder fluido
- Relaciones ManyToOne, OneToMany, OneToOne, ManyToMany con lazy/eager loading
- Herencia de entidades (TPH, TPT, TPC)
- Caché de dos niveles (memoria + Redis)
- Sistema de migraciones integrado
- Soft delete declarativo
- Hooks de ciclo de vida (PrePersist, PostPersist, PreUpdate, etc.)
- Value Objects embebibles
- Conexiones múltiples con EntityManagerRegistry
- Soporte para conexiones de solo lectura
- CLI de migraciones (
bin/sybase-orm) - Timestamps automáticos (
#[Timestamps]) - Paginación con metadatos (
paginate()) - Query Scopes globales (
#[GlobalScope]) - Subqueries en QueryBuilder (
whereIn,whereNotIn,whereExists) - BETWEEN, NOT BETWEEN, IS NULL, LIKE, NOT LIKE en OQL y QueryBuilder
- Funciones SQL built-in (UPPER, LOWER, COALESCE, LEN, DATEADD, etc.)
- UNION / UNION ALL entre queries
- Todos los JOINs: INNER, LEFT, RIGHT, CROSS, FULL OUTER
- Serialización (
toArray()/toJson()) - Mass assignment protection (
fill()/forceFill()) - Batch processing para grandes datasets
- Native SQL mapping (
createNativeQuery()) - Entity listeners externos (
#[EntityListener]) - Read-only entities (
#[Immutable]) - Cache regions por entidad (
#[CacheRegion]) - Model factories para testing (
EntityFactory) - Sistema de instrumentación/profiling
- Query Explain Plan (SHOWPLAN)
- Metadata introspection
- Optimistic locking (
#[Version]) - Pessimistic locking (HOLDLOCK/UPDLOCK vía
LockMode) - Connection pooling para workers long-running
- Partial entity loading (cargar solo columnas específicas)
- Result Set Mapping para Native SQL multi-entidad
- Audit trail automático (
#[Auditable]) - Database seeders (
SeederInterface) - Validación pre-persist (length, NOT NULL, precision)
#[UniqueEntity]— validación de unicidad antes de INSERT#[Index],#[Check],#[UniqueConstraint]— DDL constraints#[Column(default: value)]— valores por defecto#[JoinColumn(onDelete: 'CASCADE')]— FK referential actions
Requisitos del sistema
- PHP 8.1 o superior
- Extensión
ext-pdo_dblib - Servidor Sybase ASE
Instalación
composer require shedeza/sybase-orm
Configuración rápida
Configuración por array
use SybaseORM\ORM\OrmFactory; $em = OrmFactory::create([ 'connection' => [ 'host' => '192.168.1.100', 'port' => 5000, 'dbname' => 'mi_base', 'username' => 'sa', 'password' => 'secret', 'charset' => 'UTF-8', ], 'entity_directories' => [__DIR__ . '/src/Entity'], ]);
Configuración por URL DSN
use SybaseORM\ORM\OrmFactory; $em = OrmFactory::createFromUrl( 'sybase://sa:secret@192.168.1.100:5000/mi_base?charset=UTF-8', entityDirectories: [__DIR__ . '/src/Entity'], );
Los caracteres especiales en la contraseña deben codificarse con URL encoding (por ejemplo, p@ss → p%40ss).
Uso básico
Persistir una entidad
$usuario = new Usuario(); $usuario->nombre = 'Juan'; $usuario->email = 'juan@example.com'; $em->persist($usuario); $em->flush();
Buscar por ID
$usuario = $em->find(Usuario::class, 1);
Consultar con OQL
$usuarios = $em->query( 'SELECT u FROM Usuario u WHERE u.activo = :activo', ['activo' => true] );
QueryBuilder
$usuarios = $em->createQueryBuilder(Usuario::class) ->where('e.activo = :activo') ->andWhere('e.creadoEn > :fecha') ->setParameter('activo', true) ->setParameter('fecha', new \DateTime('-30 days')) ->orderBy('e.nombre', 'ASC') ->setMaxResults(10) ->getResult();
Transacciones
$em->transactional(function () use ($em) { $cuenta1 = $em->find(Cuenta::class, 1); $cuenta2 = $em->find(Cuenta::class, 2); $cuenta1->saldo -= 500; $cuenta2->saldo += 500; $em->flush(); });
Para manejo manual de transacciones:
$em->beginTransaction(); try { $em->persist($entidad); $em->flush(); $em->commit(); } catch (\Throwable $e) { $em->rollback(); throw $e; }
Eliminar una entidad
$em->remove($usuario); $em->flush();
Mapeo de entidades
use SybaseORM\Attribute\Entity; use SybaseORM\Attribute\Id; use SybaseORM\Attribute\Column; use SybaseORM\Attribute\GeneratedValue; #[Entity(table: 'usuarios')] class Usuario { #[Id] #[GeneratedValue] #[Column(type: 'integer')] public ?int $id = null; #[Column(type: 'string', length: 100)] public string $nombre = ''; #[Column(type: 'string', length: 255, nullable: true)] public ?string $email = null; #[Column(type: 'datetime')] public ?\DateTimeInterface $creadoEn = null; }
Atributos PHP principales
| Atributo | Destino | Descripción |
|---|---|---|
#[Entity] |
Clase | Marca una clase como entidad mapeada. Parámetros: table, schema, repositoryClass, connection |
#[Id] |
Propiedad | Marca la propiedad como clave primaria |
#[Column] |
Propiedad | Mapea una propiedad a una columna. Parámetros: name, type, nullable, length, precision, scale |
#[GeneratedValue] |
Propiedad | Indica que el valor es generado por la base de datos. Parámetro: strategy |
#[ManyToOne] |
Propiedad | Relación muchos-a-uno. Parámetros: targetEntity, inversedBy, cascade, fetch |
#[OneToMany] |
Propiedad | Relación uno-a-muchos. Parámetros: targetEntity, mappedBy, cascade, fetch, orphanRemoval |
#[SoftDelete] |
Clase | Activa eliminación lógica. Parámetro: column (por defecto deleted_at) |
#[HasLifecycleHooks] |
Clase | Activa hooks de ciclo de vida en la entidad |
Para la lista completa de atributos (OneToOne, ManyToMany, Embedded, herencia, hooks), consulte el manual de mapeo de entidades.
Relaciones
El ORM soporta relaciones #[ManyToOne], #[OneToMany], #[OneToOne] y #[ManyToMany] con lazy loading automático mediante proxies y eager loading vía QueryBuilder::with().
Para documentación detallada de relaciones, consulte el manual de relaciones.
Conexiones múltiples
Use EntityManagerRegistry para gestionar conexiones a múltiples bases de datos:
use SybaseORM\ORM\OrmFactory; use SybaseORM\ORM\EntityManagerRegistry; $emDefault = OrmFactory::create(['connection' => $configDefault, 'entity_directories' => $dirs]); $emReportes = OrmFactory::create(['connection' => $configReportes, 'entity_directories' => $dirs]); $registry = new EntityManagerRegistry([ 'default' => $emDefault, 'reportes' => $emReportes, ], defaultConnection: 'default'); // Obtener un manager específico $em = $registry->getManager('reportes'); // Obtener el manager por defecto $em = $registry->getDefaultManager(); // Obtener el manager que gestiona una entidad (por su atributo connection) $em = $registry->getManagerForEntity(Reporte::class);
Opciones de configuración
| Opción | Tipo | Valor por defecto | Descripción |
|---|---|---|---|
host |
string |
'localhost' |
Host del servidor Sybase ASE |
port |
int |
5000 |
Puerto de conexión |
dbname |
string |
(requerido) | Nombre de la base de datos |
username |
string |
'' |
Usuario de conexión |
password |
string |
'' |
Contraseña de conexión |
charset |
string |
'UTF-8' |
Charset de la conexión |
persistent |
bool |
false |
Usar conexiones persistentes |
charset_conversion |
bool |
false |
Activar conversión automática UTF-8 ↔ ISO-8859-1 |
read_only |
bool |
false |
Modo solo lectura (previene escrituras) |
entity_directories |
string[] |
[] |
Directorios donde buscar entidades |
entity_classes |
string[] |
[] |
Clases de entidad explícitas |
proxy_directory |
string |
sys_get_temp_dir() . '/sybase-orm-proxies' |
Directorio para proxies generados |
metadata_cache_dir |
string|null |
null |
Directorio de caché de metadatos |
redis |
array|null |
null |
Configuración de Redis para caché L2 (ver abajo) |
second_level_cache |
SecondLevelCacheInterface|null |
null |
Instancia de caché L2 ya configurada |
file_permissions |
int |
0o666 |
Permisos de archivos generados (proxies, cache) |
directory_permissions |
int |
0o777 |
Permisos de directorios generados |
Caché de segundo nivel (Redis)
Para activar el caché compartido entre requests:
$em = OrmFactory::create([ 'connection' => $connectionConfig, 'entity_directories' => [__DIR__ . '/src/Entity'], 'redis' => [ 'host' => '127.0.0.1', 'port' => 6379, 'password' => null, // opcional 'database' => 0, // opcional 'timeout' => 2.0, // segundos 'prefix' => 'myapp:', // prefijo de keys ], ]);
O si ya tenés una instancia de Redis configurada:
use SybaseORM\Cache\RedisCacheAdapter; $redis = new \Redis(); $redis->connect('127.0.0.1'); $em = OrmFactory::create([ 'connection' => $connectionConfig, 'entity_directories' => [__DIR__ . '/src/Entity'], 'second_level_cache' => new RedisCacheAdapter($redis), ]);
Documentación completa
- Documentación completa — Punto de entrada central a toda la documentación
- Manual de usuario — Guía detallada de cada módulo del ORM
- Manual de operación — Despliegue, optimización y troubleshooting
- Manual técnico — Arquitectura interna, patrones y extensibilidad
Contribuir
Las contribuciones son bienvenidas. Antes de enviar un PR, consulta la guía de contribución para conocer el flujo de trabajo, estándares de código y convenciones del proyecto.
# Ejecutar tests composer test # Análisis estático composer phpstan # Formateo de código composer cs-fix
CLI
php bin/sybase-orm help # Show all commands # Migrations php bin/sybase-orm migrate # Run pending migrations php bin/sybase-orm migrate:rollback # Rollback last migration php bin/sybase-orm migrate:status # Show migration status php bin/sybase-orm migrate:generate # Generate migration from entity diff php bin/sybase-orm migrate:preview # Preview SQL without executing php bin/sybase-orm migrate:fresh # Drop all + re-run (requires --force) php bin/sybase-orm migrate:reset # Rollback ALL migrations (requires --force) # Generators php bin/sybase-orm make:migration # Auto-diff entities vs DB, generate migration php bin/sybase-orm make:migration --empty # Create empty migration for manual SQL php bin/sybase-orm make:entity User # Generate entity class skeleton # Schema & Cache php bin/sybase-orm schema:validate # Validate mapping vs DB schema php bin/sybase-orm cache:clear # Clear proxy and metadata caches php bin/sybase-orm orm:info # Show all mapped entities
Seeders
use SybaseORM\Testing\SeederInterface; class UserSeeder implements SeederInterface { public function run(\SybaseORM\ORM\EntityManagerInterface $em): void { $user = new User(); $user->email = 'admin@app.com'; $em->persist($user); $em->flush(); } } // Run seeders $runner = new \SybaseORM\Testing\SeederRunner($em); $runner->run([new UserSeeder(), new ProductSeeder()]);
Licencia
Este proyecto está licenciado bajo la Licencia MIT.