Guide for contributing to the OpenAPI layer using processor pattern, complexity management, and functional programming...
Guide for contributing to the OpenAPI layer at src/Shared/Application/OpenApi/.
This skill covers architecture patterns, complexity management techniques, and best practices for maintaining OpenAPI specifications while keeping code quality high.
Related Skills:
The OpenAPI layer follows a Processor Pattern with clear separation of concerns:
src/Shared/Application/OpenApi/
โโโ Builder/ # Schema and parameter builders
โโโ Factory/
โ โโโ Endpoint/ # Custom endpoint factories
โ โโโ Request/ # Request body schemas
โ โโโ Response/ # Response schemas
โ โโโ UriParameter/ # Path parameter factories
โโโ Processor/ # Spec transformation processors
โโโ OpenApiFactory.php # Main coordinator for tagged factories/processors
with*() methods, never mutate directlyarray_map, array_filter over loopsprivate const OPERATIONS = ['Get', 'Post', 'Put', 'Patch', 'Delete'];
private function processPathItem(PathItem $pathItem): PathItem
{
foreach (self::OPERATIONS as $operation) {
$pathItem = $pathItem->{'with' . $operation}(
$this->processOperation($pathItem->{'get' . $operation}())
);
}
return $pathItem;
}
private function processOperation(?Operation $operation): ?Operation
{
if ($operation === null) {
return null;
}
if ($operation->getParameters() === []) {
return $operation;
}
return $operation->withParameters(...);
}
// โ
Functional (complexity: 2)
private function collectRequired(array $params): array
{
return array_values(
array_map(
static fn (Parameter $p) => $p->name,
array_filter($params, static fn (Parameter $p) => $p->isRequired())
)
);
}
// โ Procedural (complexity: 3+)
private function collectRequired(array $params): array
{
$required = [];
foreach ($params as $param) {
if ($param->isRequired()) {
$required[] = $param->name;
}
}
return $required;
}
// Split long methods into focused helpers
private function processContent(Operation $operation): Operation
{
$content = $operation->getRequestBody()->getContent();
$modified = false;
foreach ($content as $mediaType => $mediaTypeObject) {
$fixedProperties = $this->fixProperties($mediaTypeObject); // Extracted!
if ($fixedProperties !== null) {
$content[$mediaType]['schema']['properties'] = $fixedProperties;
$modified = true;
}
}
return $modified ? $operation->withRequestBody(...) : $operation;
}
// Extracted method with single responsibility
private function fixProperties(array $mediaTypeObject): ?array
{
if (!isset($mediaTypeObject['schema']['properties'])) {
return null;
}
$properties = $mediaTypeObject['schema']['properties'];
return array_map(
static fn ($prop) => self::fixProperty($prop),
$properties
);
}
src/Shared/Application/OpenApi/Processor/OpenApiProcessorInterfaceapp.openapi_processor and an explicit priorityOpenApiFactorySee REFERENCE.md - Adding Processors for complete examples.
EndpointFactoryInterfaceapp.openapi_endpoint_factory in services.yamlOpenApiFactorySee REFERENCE.md - Adding Endpoint Factories for step-by-step guide.
Extend ParameterDescriptionProcessor with a focused provider and wire it into getParameterDescriptions():
private function getYourFilterDescriptions(): array
{
return [
'yourParam' => 'Description of parameter',
'yourParam[]' => 'Array variant description',
];
}
private function getParameterDescriptions(): array
{
return array_merge(
$this->getOrderDescriptions(),
$this->getYourFilterDescriptions(),
);
}
withX() methods, not direct assignment$array === [] or $string === ''make generate-openapi-spec # Generate spec
make validate-openapi-spec # Validate with Spectral
make phpinsights # Check quality scores
make unit-tests # Run tests
Expected Results:
OpenAPI code belongs in the Application layer:
src/Shared/Application/OpenApi/OpenAPI components can depend on:
For comprehensive patterns, step-by-step guides, and examples:
ParameterDescriptionProcessor.php - Parameter-description provider patternIriReferenceTypeProcessor.php - Schema transformation patternPathParametersProcessor.php - Delegation pattern for path cleanupBefore committing OpenAPI changes:
empty() - explicit type checksmake validate-openapi-spec passesmake phpinsights meets thresholdsmake unit-tests pass with 100% coverageRemember: Low complexity and high quality go hand-in-hand. Use functional programming, guard clauses, and method extraction to keep code maintainable.