Skip to content

Commit

Permalink
Support php7.4 nullable typed properties for JMS serializer.
Browse files Browse the repository at this point in the history
  • Loading branch information
zviryatko committed May 1, 2024
1 parent 2af8c5d commit 0bee583
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 0 deletions.
31 changes: 31 additions & 0 deletions src/ModelDescriber/JMSModelDescriber.php
Expand Up @@ -152,6 +152,7 @@ public function describe(Model $model, OA\Schema $schema)
} catch (\ReflectionException $ignored) {
}
}
$this->checkRequiredFields($reflections, $schema, $name);
if (null !== $item->setter) {
try {
$reflections[] = new \ReflectionMethod($item->class, $item->setter);
Expand Down Expand Up @@ -397,4 +398,34 @@ private function propertyTypeUsesGroups(array $type): ?bool
return null;
}
}

/**
* Mark property as required if it is not nullable.
*
* @param array<\ReflectionProperty|\ReflectionMethod> $reflections
*/
private function checkRequiredFields(array $reflections, OA\Schema $schema, string $name): void
{
foreach ($reflections as $reflection) {
$nullable = false;
if ($reflection instanceof \ReflectionProperty) {
$type = PHP_VERSION_ID >= 70400 ? $reflection->getType() : null;
if (null !== $type && !$type->allowsNull()) {
$nullable = true;
}
} elseif ($reflection instanceof \ReflectionMethod) {
$returnType = $reflection->getReturnType();
if (null !== $returnType && !$returnType->allowsNull()) {
$nullable = true;
}
}
if ($nullable) {
$required = Generator::UNDEFINED !== $schema->required ? $schema->required : [];
$required[] = $name;

$schema->required = $required;
break;
}
}
}
}
15 changes: 15 additions & 0 deletions tests/Functional/Controller/JMSController80.php
Expand Up @@ -16,6 +16,7 @@
use Nelmio\ApiDocBundle\Tests\Functional\Entity\JMSComplex80;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\JMSDualComplex;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\JMSNamingStrategyConstraints;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\JMSTyped80;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\JMSUser;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\NestedGroup\JMSChat;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\NestedGroup\JMSChatRoomUser;
Expand Down Expand Up @@ -166,4 +167,18 @@ public function minUserNestedAction()
public function discriminatorMapAction()
{
}

/**
* @Route("/api/jms_typed", methods={"GET"})
*
* @OA\Response(
* response=200,
* description="Success",
*
* @Model(type=JMSTyped80::class)
* )
*/
public function typedAction()
{
}
}
11 changes: 11 additions & 0 deletions tests/Functional/Controller/JMSController81.php
Expand Up @@ -17,6 +17,7 @@
use Nelmio\ApiDocBundle\Tests\Functional\Entity\JMSComplex81;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\JMSDualComplex;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\JMSNamingStrategyConstraints;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\JMSTyped81;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\JMSUser;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\NestedGroup\JMSChat;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\NestedGroup\JMSChatRoomUser;
Expand Down Expand Up @@ -141,4 +142,14 @@ public function enum()
public function discriminatorMapAction()
{
}

#[Route('/api/jms_typed', methods: ['GET'])]
#[OA\Response(
response: 200,
description: 'Success',
content: new Model(type: JMSTyped81::class))
]
public function typedAction()
{
}
}
46 changes: 46 additions & 0 deletions tests/Functional/Entity/JMSTyped80.php
@@ -0,0 +1,46 @@
<?php

/*
* This file is part of the NelmioApiDocBundle package.
*
* (c) Nelmio
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Nelmio\ApiDocBundle\Tests\Functional\Entity;

use JMS\Serializer\Annotation as Serializer;
use Nelmio\ApiDocBundle\Annotation\Model;
use OpenApi\Annotations as OA;

class JMSTyped80
{
/**
* @Serializer\Type("integer")
*/
private int $id;

/**
* @OA\Property(ref=@Model(type=JMSUser::class))
*
* @Serializer\SerializedName("user")
*/
private JMSUser $User;

/**
* @Serializer\Type("string")
*/
private ?string $name;

/**
* @Serializer\VirtualProperty
*
* @OA\Property(ref=@Model(type=JMSUser::class))
*/
public function getVirtualFriend(): JMSUser
{
return new JMSUser();
}
}
36 changes: 36 additions & 0 deletions tests/Functional/Entity/JMSTyped81.php
@@ -0,0 +1,36 @@
<?php

/*
* This file is part of the NelmioApiDocBundle package.
*
* (c) Nelmio
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Nelmio\ApiDocBundle\Tests\Functional\Entity;

use JMS\Serializer\Annotation as Serializer;
use Nelmio\ApiDocBundle\Annotation\Model;
use OpenApi\Attributes as OA;

class JMSTyped81
{
#[Serializer\Type('integer')]
private int $id;

#[OA\Property(ref: new Model(type: JMSUser::class))]
#[Serializer\SerializedName('user')]
private JMSUser $User;

#[Serializer\Type('string')]
private ?string $name;

#[Serializer\VirtualProperty]
#[OA\Property(ref: new Model(type: JMSUser::class))]
public function getVirtualFriend(): JMSUser
{
return new JMSUser();
}
}
25 changes: 25 additions & 0 deletions tests/Functional/JMSFunctionalTest.php
Expand Up @@ -360,6 +360,12 @@ public function testEnumSupport(): void
],
],
'schema' => 'Article81',
'required' => [
'id',
'type',
'int_backed_type',
'not_backed_type',
],
], json_decode($this->getModel('Article81')->toJson(), true));

self::assertEquals([
Expand Down Expand Up @@ -420,4 +426,23 @@ protected static function createKernel(array $options = []): KernelInterface
{
return new TestKernel(TestKernel::USE_JMS);
}

public function testModelTypedDocumentation(): void
{
self::assertEquals([
'type' => 'object',
'properties' => [
'id' => ['type' => 'integer'],
'user' => ['$ref' => '#/components/schemas/JMSUser'],
'name' => ['type' => 'string'],
'virtual_friend' => ['$ref' => '#/components/schemas/JMSUser'],
],
'required' => [
'virtual_friend',
'id',
'user',
],
'schema' => 'JMSTyped',
], json_decode($this->getModel('JMSTyped')->toJson(), true));
}
}
10 changes: 10 additions & 0 deletions tests/Functional/TestKernel.php
Expand Up @@ -21,6 +21,8 @@
use Nelmio\ApiDocBundle\Tests\Functional\Entity\BazingaUser;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\JMSComplex80;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\JMSComplex81;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\JMSTyped80;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\JMSTyped81;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\NestedGroup\JMSPicture;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\PrivateProtectedExposure;
use Nelmio\ApiDocBundle\Tests\Functional\Entity\SymfonyConstraintsWithValidationGroups;
Expand Down Expand Up @@ -220,6 +222,10 @@ protected function configureContainer(ContainerBuilder $c, LoaderInterface $load
'type' => JMSComplex80::class,
'groups' => null,
],
[
'alias' => 'JMSTyped',
'type' => JMSTyped80::class,
],
]);
} elseif (self::isAttributesAvailable()) {
$models = array_merge($models, [
Expand All @@ -237,6 +243,10 @@ protected function configureContainer(ContainerBuilder $c, LoaderInterface $load
'type' => JMSComplex81::class,
'groups' => null,
],
[
'alias' => 'JMSTyped',
'type' => JMSTyped81::class,
],
]);
}

Expand Down

0 comments on commit 0bee583

Please sign in to comment.