Исключение групп пропуска десериализации JMSSerializerBundle для свойства id с использованием DoctrineObjectConstructor

Я использую jms/serializer-bundle 2.4.3 на symfony 4.2 и заметил неприятную проблему в своем приложении: когда я публикую объект, DoctrineObjectConstructor использует идентификатор в содержимом для извлечения другого объекта и, таким образом, исправляет его, пока он исключен моими группами безопасности.

см. скорее сущность

class Entity
{
/**
 * @var int
 *
 * @ORM\Column(name = "id", type = "int")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy = "IDENTITY")
 * @Serializer\Groups({"GetEntity"})
 */
private $id;

/**
 * @var string
 *
 * @ORM\Column(name = "name", type = "string")
 * @Serializer\Groups({"GetEntity", "PostEntity"})
 */
private $name;
}

контроллер

/**
 * @Route("/entity", name = "post_entity", methods = {"POST"})
 */
public function postEntity(Request $request, EntityManagerInterface $entityManager, SerializerInterface $serializer): JsonResponse
{
    $deserializationContext = DeserializationContext::create();
    $deserializationContext->setGroups(['PostEntity']);

    $entity = $serializer->deserialize($request->getContent(), Entity::class, 'json', $deserializationContext);
    $entityManager->persist($entity);
    $entityManager->flush();

    return $this->json($entity, Response::HTTP_OK, [], ['groups' => ['GetEntity']]);
}

У меня есть некоторые изменения конфигурации JMS в службах

jms_serializer.object_constructor:
    alias: jms_serializer.doctrine_object_constructor
    public: true

jms_serializer.unserialize_object_constructor:
    class: App\Serializer\ObjectConstructor

Если кто-нибудь может объяснить мне, как игнорировать идентификатор в этом случае, я открыт для любых предложений.

С уважением и благодарностью за любую помощь

Вы пробовали добавлять набор ExclusionPolicy ко всем?

i.am.michiel 28.01.2019 17:26

Привет, да, я пытался, но я получил тот же результат. Наконец, я нашел решение, переопределив DoctrineObjectConstructor и исключив объекты, у которых нет группы десериализации контекста для их идентификатора. Теперь работает как шарм, все равно спасибо!

Firejaguar 06.02.2019 16:45
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Symfony Station Communiqué - 17 февраля 2023 г
Symfony Station Communiqué - 17 февраля 2023 г
Это коммюнике первоначально появилось на Symfony Station , вашем источнике передовых новостей Symfony, PHP и кибербезопасности.
Управление ответами api для исключений на Symfony с помощью KernelEvents
Управление ответами api для исключений на Symfony с помощью KernelEvents
Много раз при создании api нам нужно возвращать клиентам разные ответы в зависимости от возникшего исключения.
0
2
493
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Чтобы решить эту проблему, просто добавьте переопределение в свой services.yaml.

jms_serializer.doctrine_object_constructor:
    class: App\Serializer\DoctrineObjectConstructor
    arguments:
        - '@doctrine'
        - '@jms_serializer.unserialize_object_constructor'

jms_serializer.object_constructor:
    alias: jms_serializer.doctrine_object_constructor

и добавьте локальный DoctrineObjectConstructor, обновленный, чтобы игнорировать сущности без текущей группы десериализации в свойстве id

class DoctrineObjectConstructor implements ObjectConstructorInterface
{
const ON_MISSING_NULL      = 'null';
const ON_MISSING_EXCEPTION = 'exception';
const ON_MISSING_FALLBACK  = 'fallback';

private $fallbackStrategy;
private $managerRegistry;
private $fallbackConstructor;

/**
 * Constructor.
 *
 * @param ManagerRegistry            $managerRegistry     Manager registry
 * @param ObjectConstructorInterface $fallbackConstructor Fallback object constructor
 * @param string                     $fallbackStrategy
 */
public function __construct(ManagerRegistry $managerRegistry, ObjectConstructorInterface $fallbackConstructor, $fallbackStrategy = self::ON_MISSING_NULL)
{
    $this->managerRegistry     = $managerRegistry;
    $this->fallbackConstructor = $fallbackConstructor;
    $this->fallbackStrategy    = $fallbackStrategy;
}

/**
 * {@inheritdoc}
 */
public function construct(VisitorInterface $visitor, ClassMetadata $metadata, $data, array $type, DeserializationContext $context)
{
    // Locate possible ObjectManager
    $objectManager = $this->managerRegistry->getManagerForClass($metadata->name);

    if (!$objectManager) {
        // No ObjectManager found, proceed with normal deserialization
        return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type, $context);
    }

    // Locate possible ClassMetadata
    $classMetadataFactory = $objectManager->getMetadataFactory();

    if ($classMetadataFactory->isTransient($metadata->name)) {
        // No ClassMetadata found, proceed with normal deserialization
        return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type, $context);
    }

    // Managed entity, check for proxy load
    if (!\is_array($data)) {
        // Single identifier, load proxy
        return $objectManager->getReference($metadata->name, $data);
    }

    // Fallback to default constructor if missing identifier(s)
    $classMetadata  = $objectManager->getClassMetadata($metadata->name);
    $identifierList = [];

    foreach ($classMetadata->getIdentifierFieldNames() as $name) {
        $propertyGroups = [];

        if ($visitor instanceof AbstractVisitor) {
            /** @var PropertyNamingStrategyInterface $namingStrategy */
            $namingStrategy = $visitor->getNamingStrategy();
            $dataName       = $namingStrategy->translateName($metadata->propertyMetadata[$name]);
            $propertyGroups = $metadata->propertyMetadata[$name]->groups;
        } else {
            $dataName = $name;
        }

        if (!array_key_exists($dataName, $data) || true === empty(array_intersect($context->getAttribute('groups'), $propertyGroups))) {
            return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type, $context);
        }

        $identifierList[$name] = $data[$dataName];
    }

    // Entity update, load it from database
    $object = $objectManager->find($metadata->name, $identifierList);

    if (null === $object) {
        switch ($this->fallbackStrategy) {
            case self::ON_MISSING_NULL:
                return null;
            case self::ON_MISSING_EXCEPTION:
                throw new ObjectConstructionException(sprintf('Entity %s can not be found', $metadata->name));
            case self::ON_MISSING_FALLBACK:
                return $this->fallbackConstructor->construct($visitor, $metadata, $data, $type, $context);
            default:
                throw new InvalidArgumentException('The provided fallback strategy for the object constructor is not valid');
        }
    }

    $objectManager->initializeObject($object);

    return $object;
}

}

Другие вопросы по теме