develia / symfony
Symfony utility library
Requires
- php: >=8.1
- ext-simplexml: *
- develia/commons: ^0.9.14
- symfony/console: *
- symfony/framework-bundle: *
- symfony/http-client: *
- symfony/lock: *
- symfony/process: *
- symfony/serializer: *
- symfony/translation: ^5.4 || ^6.0 || ^7.0 || ^8.0
- symfony/yaml: *
Requires (Dev)
- doctrine/annotations: *
- doctrine/collections: *
- doctrine/dbal: *
- doctrine/doctrine-bundle: ^2.12 || ^3.0
- doctrine/orm: ^2.19 || ^3.0
- doctrine/persistence: *
- knpuniversity/oauth2-client-bundle: ^2.18
- nyholm/psr7: ^1.8
- openai-php/client: *
- phpstan/phpstan: ^2.1
- phpunit/phpunit: *
- symfony/form: *
- symfony/security-core: *
- symfony/security-http: *
Suggests
- doctrine/annotations: Para utilizar AnnotationUtilities
- doctrine/doctrine-bundle: Para registrar mappings y servicios persistentes con Doctrine en Symfony
- doctrine/orm: Para utilizar repositorios, entidades, specifications y demás APIs persistentes
- knpuniversity/oauth2-client-bundle: Para iniciar sesión con OAuth (OAuthAuthenticator y las rutas de routes/oauth.php)
- mark-gerarts/auto-mapper-plus: ^1.4.1
- openai-php/client: Para los servicios de IA (OpenAIService, AnthropicAIService, NameGenderClassifier y FullNameSplitter), con una implementación de PSR-17 como nyholm/psr7
- symfony/form: Para utilizar BaseCrudController
- symfony/property-info: Para utilizar JsonSchemaGenerator
- symfony/security-core: Para los autenticadores StaticApiKey, API key de usuario y Bearer
- symfony/security-http: Para los autenticadores StaticApiKey, API key de usuario y Bearer
- symfony/type-info: Para utilizar JsonSchemaGenerator
- twig/twig: Para utilizar DateExtension
Provides
None
Conflicts
None
Replaces
None
- dev-master
- 0.7.2
- 0.7.1
- 0.7.0
- 0.6.14
- 0.6.13
- 0.6.12
- 0.6.11
- 0.6.10
- 0.6.9
- 0.6.8
- 0.6.7
- 0.6.6
- 0.6.5
- 0.6.4
- 0.6.3
- 0.6.2
- 0.6.1
- 0.6.0
- 0.5.0
- 0.4.4
- 0.4.3
- 0.4.2
- 0.4.1
- 0.4.0
- 0.3.57
- 0.3.56
- 0.3.55
- 0.3.54
- 0.3.53
- 0.3.52
- 0.3.51
- 0.3.50
- 0.3.49
- 0.3.48
- 0.3.47
- 0.3.46
- 0.3.45
- 0.3.44
- 0.3.43
- 0.3.42
- 0.3.41
- 0.3.40
- 0.3.39
- 0.3.38
- 0.3.37
- 0.3.36
- 0.3.35
- 0.3.34
- 0.3.33
- 0.3.25
- 0.3.24
- 0.3.23
- 0.3.22
- 0.3.21
- 0.3.20
- 0.3.19
- 0.3.18
- 0.3.17
- 0.3.15
- 0.3.14
- 0.3.13
- 0.3.12
- 0.3.11
- 0.3.10
- 0.3.9
- 0.3.8
- 0.3.7
- 0.3.6
- 0.3.5
- 0.3.4
- 0.3.3
- 0.3.2
- 0.3.1
- 0.3.0
- 0.2.7
- 0.2.6
- 0.2.5
- 0.2.4
- 0.2.3
- 0.2.2
- 0.2.1
- 0.1.17
- 0.1.16
- 0.1.15
- 0.1.14
- 0.1.13
- 0.1.12
- 0.1.11
- 0.1.10
- 0.1.9
- 0.1.8
- 0.1.7
- 0.1.6
This package is auto-updated.
Last update: 2026-11-04 01:12:46 UTC
README
Este paquete proporciona una serie de utilidades y servicios para acelerar el desarrollo de aplicaciones Symfony en el ecosistema Develia.
Características principales
Controladores Base:
ApiController: Clase base para controladores de API.deserialize()ypopulate()leen delRequestel cuerpo JSON o, si la petición es un formulario (multipart o URL-encoded), sus campos convertidos a los tipos declarados del objeto (bool,int,float,""anull), en Symfony 7.4 y 8; un campo cuya propiedad es un objeto o un array se decodifica si llega como texto JSON, como envían los objetos los clientes generados. Los ficheros subidos se leen aparte.ExceptionStatusMapperInterface: decide el código HTTP de las respuestas de error de losApiController. La implementación por defecto,StandardExceptionStatusMapper, responde 4xx a las excepciones HTTP de Symfony, 400 aInvalidArgumentException,DomainException,OutOfRangeExceptionyOutOfBoundsException, y 500 al resto (sin mostrar el mensaje). Las excepciones a las que asigna un 4xx se registran en el log como errores de cliente (ERROR) y no como fallos críticos. La aplicación asigna sus excepciones decorando el servicio, sin que el dominio dependa de HTTP:#[AsDecorator(ExceptionStatusMapperInterface::class)] final class AppExceptionStatusMapper implements ExceptionStatusMapperInterface { public function __construct( #[AutowireDecorated] private readonly ExceptionStatusMapperInterface $inner, ) {} public function getStatusCode(Throwable $exception): int { return match (true) { $exception instanceof InvoiceAlreadyPaidException => Response::HTTP_CONFLICT, default => $this->inner->getStatusCode($exception), }; } }En versiones de Symfony sin esos atributos, se declara con
decoratesenservices.yaml.RestController: Implementación básica para controladores REST con métodosGET,POST,PUTyDELETEmapeados automáticamente.BaseCrudController: Facilitador para operaciones CRUD estándar.
Servicios de Gestión:
SettingsManager: Interfaz y servicio para gestionar configuraciones dinámicas (con soporte para base de datos y caché); permite leer, guardar y eliminar un ajuste o todos los de un ámbito.DatabaseLogger: Logger compatible con PSR-3 que persiste los registros en la base de datos (tablaslog_entryylog_entry_meta).LogEntryNormalizerserializa sus entradas (id,level,message,createdAt,scope,scopeIdy los metadatos comocontext), ydevelia:logs:purge --days=90(LogEntryRepository::purgeOlderThan()) borra las más antiguas con sus metadatos; la aplicación lo programa, p. ej. a diario en suSchedule.OpenAiService: Integración simplificada con la API de OpenAI.JsonSchemaGenerator: Genera JSON Schema a partir de clases PHP (para Structured Outputs de OpenAI).YouTrackService: Cliente de la API REST de YouTrack (incidencias, comentarios, usuarios y roles). Configurable con las variables de entornoYOUTRACK_BASE_URL,YOUTRACK_TOKENyYOUTRACK_PROJECT_ID. Al crear usuarios la contraseña es obligatoria (YouTrack la exige) y, en YouTrack Cloud,deleteUser()no está disponible: usarbanUser().getIssues()ygetUsers()aceptan una consulta opcional para filtrar en el servidor (reporter: <login>, login o nombre del usuario…). Los roles se gestionan en Hub (getRoles(),findRole(),getUserRoles(),assignUserRole()), que identifica a los usuarios por suringIdy sólo permite conceder roles sobre proyectos registrados en Hub; para el resto, usar el proyecto global (HUB_GLOBAL_PROJECT_ID, valor por defecto).SlackNotifier: Envía notificaciones a Slack mediante un Incoming Webhook. Configurable con la variable de entornoSLACK_WEBHOOK_URL.TransactionManager: Utilidad para envolver ejecuciones en transacciones de base de datos.LockManager: Gestión de bloqueos (locks) para evitar condiciones de carrera.
Listados filtrados en servidor:
Listing\FilterSet: Declaración inmutable, creada conFilterSet::builder()(FilterSetBuilder:addFilter,addSortableColumn,setDefaultSort,setTieBreaker,setPageSizeybuild()), de los filtros, columnas ordenables y límites de paginación de un listado.resolve()convierte los parámetros de la petición (page,itemsPerPage,sortBy,sortDescy un parámetro por filtro, como los que envíauseServerTablede@develia/vuetify) en unListingCriterialisto paraBaseRepository::findAllBySpecs(). Ignora los filtros vacíos, rechaza conInvalidArgumentExceptionlas columnas no ordenables, limitaitemsPerPagey añade un campo de desempate para que las páginas sean estables.
Seguridad:
StaticApiKeyAuthenticator: Autenticador sencillo mediante una clave API estática enviada en las cabeceras.ApiKeyAuthenticatoryBearerApiKeyAuthenticator: Autenticadores opcionales e independientes que localizan un usuario mediante un cargador configurable usandoX-Api-KeyoAuthorization: Bearer.OAuthAuthenticator: inicio de sesión con los clientes deknpuniversity/oauth2-client-bundle(Google, Facebook...), con rutas propias y la decisión de qué usuario entra en manos de la aplicación.
Inicio de sesión con OAuth
Se registra cuando están instalados knpuniversity/oauth2-client-bundle y los componentes de seguridad. La librería hace el
flujo: la redirección al proveedor, la comprobación del state guardado en la sesión, el intercambio del código y los
errores. La aplicación decide qué usuario entra con cada identidad implementando OAuthUserResolverInterface: lo busca,
lo vincula a una cuenta existente o lo crea, o rechaza el acceso con CustomUserMessageAuthenticationException, cuyo
mensaje se muestra en el login. Vincular por email solo es seguro si el proveedor garantiza que el email es del usuario
(p. ej. email_verified de Google).
# config/routes/develia_oauth.yaml: /oauth/{client}/connect (inicio) y /oauth/{client}/check (vuelta)
develia_oauth:
resource: '@DeveliaBundle/Resources/config/routes/oauth.php'
# config/packages/knpu_oauth2_client.yaml: cada cliente vuelve a develia_oauth_check con su nombre
knpu_oauth2_client:
clients:
google:
type: google
client_id: '%env(OAUTH_GOOGLE_CLIENT_ID)%'
client_secret: '%env(OAUTH_GOOGLE_CLIENT_SECRET)%'
redirect_route: develia_oauth_check
redirect_params: { client: google }
# config/services.yaml
parameters:
# Clientes con los que se puede iniciar sesión y sus permisos; los demás responden 404.
develia.security.oauth_scopes: { google: [openid, email, profile] }
develia.security.oauth_login_route: app_login # adonde vuelve tras un error (por defecto, app_login)
develia.security.oauth_target_route: app_login_success # tras entrar, si ninguna página pidió la sesión (por defecto, /)
services:
Develia\Symfony\Security\OAuth\OAuthUserResolverInterface: '@App\Security\OAuthUserResolver'
# config/packages/security.yaml
security:
firewalls:
main:
custom_authenticators:
- Develia\Symfony\Security\OAuth\OAuthAuthenticator
Como form_login, tras entrar se vuelve a la página que pidió la sesión; tras un error, al login con el error en la
sesión (AuthenticationUtils::getLastAuthenticationError()), también si el proveedor no responde. El user_checker
del firewall se aplica igual que en cualquier otro inicio de sesión, y con remember_me y always_remember_me la
sesión también se recuerda.
- Hace falta el bundle de knpu activado, no solo instalado.
- Los clientes deben conservar
use_state(activado por defecto): elstatede la sesión es lo que impide que un tercero inicie la sesión de otro con su propia cuenta (CSRF del login). - Un cliente con
~endevelia.security.oauth_scopesqueda desactivado, igual que uno que no aparece: sus rutas responden 404. - La vuelta solo admite GET; no sirven los proveedores que la envían por POST (
form_post).
Autenticación API/MCP
La librería no crea rutas, servidores MCP, firewalls ni reglas de autorización. La aplicación consumidora puede preparar su entidad Doctrine con ApiKeyUserTrait, sin implementar un contrato adicional al UserInterface requerido por Symfony:
StaticApiKeyAuthenticator valida una única clave estática y delega la carga de api_user al proveedor configurado en el firewall, que es quien determina sus roles. El cuarto argumento del constructor permite usar otro identificador sin extender el autenticador:
services:
Develia\Symfony\Security\StaticApiKeyAuthenticator:
arguments:
$apiKey: '%env(STATIC_API_KEY)%'
$userIdentifier: service_account
use Develia\Symfony\Security\ApiKeyUserTrait;
class User implements UserInterface
{
use ApiKeyUserTrait;
// Implementar aquí el resto de UserInterface.
}
El trait añade una columna Doctrine nullable, única y de hasta 255 caracteres. La aplicación debe generar la migración y no debe incluir apiKey en sus grupos de Serializer ni exponerlo en respuestas.
Por defecto se usa PlainTextApiKeyEncoder. Para almacenar el valor de búsqueda como SHA-256 determinista, sustituye el alias en la aplicación:
services:
Develia\Symfony\Security\ApiKeyEncoderInterface:
alias: Develia\Symfony\Security\Sha256ApiKeyEncoder
El mismo encoder debe usarse al crear o rotar la clave y al autenticarla. SHA-256 determinista permite una consulta directa por apiKey y es apropiado para claves aleatorias largas; no sustituye un hash adaptativo para secretos de baja entropía.
$rawApiKey = bin2hex(random_bytes(32));
$user->setApiKey($apiKeyEncoder->encode($rawApiKey));
// Entregar $rawApiKey una sola vez al propietario y no registrarla.
Por defecto, DoctrineApiKeyUserLoader busca el valor codificado en apiKey. Solo hay que indicar la entidad de usuario; la propiedad también puede personalizarse:
parameters:
develia.security.api_key_user_class: App\Entity\User
develia.security.api_key_property: apiKey
Para usar Redis, una API remota u otro almacenamiento, implementa ApiKeyUserLoaderInterface y sustituye su alias. La aplicación conecta los autenticadores a sus firewalls API o MCP y sigue siendo responsable de las rutas y la autorización:
services:
Develia\Symfony\Security\ApiKeyUserLoaderInterface:
alias: App\Security\ApiKeyUserLoader
security:
firewalls:
api:
stateless: true
custom_authenticators:
- Develia\Symfony\Security\ApiKeyAuthenticator
- Develia\Symfony\Security\BearerApiKeyAuthenticator
ApiKeyAuthenticator usa X-Api-Key por defecto; el parámetro de servicio develia.security.api_key_header permite cambiarlo sin configurar BearerApiKeyAuthenticator. Los dos autenticadores no inspeccionan el transporte del otro ni rechazan su presencia simultánea. Los fallos devuelven 401 sin la credencial y el flujo Bearer añade WWW-Authenticate: Bearer.
Extensiones Twig:
DateExtension: Filtros adicionales para el manejo de fechas en plantillas.
Utilidades:
- Helpers para manejo de anotaciones, rutas y respuestas XML.
CommandEmitterimplementaDevelia\TaskOutputInterfacey serializa cadaTaskEventen la salida de Symfony Console.sendPayload()se mantiene para consumidores anteriores que todavía envían otros objetos.
Requisitos
- PHP 8.1 o superior.
develia/commons, FrameworkBundle, Serializer, Console, Process, Lock, HttpClient y YAML se instalan como dependencias del paquete.- Los servicios de IA (
OpenAIService,AnthropicAIService,NameGenderClassifieryFullNameSplitter) solo se registran conopenai-php/clientinstalado, que necesita una implementación de PSR-17, comonyholm/psr7:composer require openai-php/client nyholm/psr7. - Los componentes Cache, Config, DependencyInjection, EventDispatcher, HttpFoundation, HttpKernel y Routing los aporta FrameworkBundle.
Instalación
Instala el paquete vía Composer:
composer require develia/symfony
La instalación base no incluye Doctrine. Para habilitar BaseRepository, entidades y specifications, mappings ORM, DatabaseLogger, DatabaseSettingsManager, TransactionManager, PdoLockManager, LogEntryRepository y el loader Doctrine de API keys, instala:
composer require doctrine/doctrine-bundle doctrine/orm
El bundle detecta DoctrineBundle, ORM y DBAL y registra automáticamente sus mappings, servicios y aliases. Aunque symfony/lock forma parte de la instalación base, la implementación PdoLockManager solo se registra cuando DBAL está disponible.
Con Doctrine disponible, el bundle también registra el tipo DBAL time_of_day (TimeOfDayType), compatible con DBAL 3 y 4, que persiste un Develia\TimeOfDay en una columna TIME:
#[ORM\Column(type: TimeOfDayType::NAME, nullable: true)]
private ?TimeOfDay $scheduledTime = null;
La autenticación es opcional y requiere los componentes de seguridad. El loader y los autenticadores de usuario se registran automáticamente cuando también está disponible Doctrine; sin Doctrine, la aplicación puede implementar ApiKeyUserLoaderInterface y registrar manualmente el loader y los autenticadores.
composer require symfony/security-core symfony/security-http
Para habilitar DateExtension, instala Twig:
composer require twig/twig
AnnotationUtilities, los formularios y JsonSchemaGenerator también mantienen sus dependencias opcionales indicadas en las sugerencias de Composer.
Asegúrate de registrar el bundle en tu archivo config/bundles.php si no se hace automáticamente:
return [
// ...
Develia\Symfony\DeveliaBundle::class => ['all' => true],
];
Actualizar a 0.7
La tabla settings guarda los ajustes sin identificador de ámbito (los globales) con scope_id vacío en vez de NULL y
tiene un índice único sobre scope, scope_id y name, que antes dejaba entrar filas repetidas. Antes de generar la
migración con doctrine:migrations:diff, añade al principio de su up() el borrado de las filas repetidas, que conserva la
más reciente, y el cambio de NULL a vacío:
// PostgreSQL
$this->addSql("DELETE FROM settings a USING settings b WHERE a.scope = b.scope AND COALESCE(a.scope_id, '') = COALESCE(b.scope_id, '') AND a.name = b.name AND a.id < b.id");
// MariaDB / MySQL
$this->addSql("DELETE a FROM settings a JOIN settings b ON a.scope = b.scope AND COALESCE(a.scope_id, '') = COALESCE(b.scope_id, '') AND a.name = b.name AND a.id < b.id");
$this->addSql("UPDATE settings SET scope_id = '' WHERE scope_id IS NULL");
SettingsManagerInterface no cambia: null sigue significando «sin identificador».
Configuración
Puedes configurar los servicios en tus archivos de configuración de Symfony (ej. config/packages/develia.yaml). Consulta los servicios disponibles en el contenedor de servicios de Symfony.
Licencia
Propiedad de Antonio Gil Espinosa. Todos los derechos reservados.