Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
582 views
in Technique[技术] by (71.8m points)

symfony - JMSSerializer deserialize entity by id

i'm using JMSSerializer to deserialize a JSON request and i'm having troubles with ManyToOne relations. I would like to deserialize the relation entity from a id given. Example:

Class Game {


/**
 * @var Team
 *
 * @ORMManyToOne(targetEntity="Team")
 * @ORMJoinColumn(name="home_team_id", referencedColumnName="id")
 * @JMSSerializerSerializedName("home")
 */
private $homeTeam;

/**
 * @ORMManyToOne(targetEntity="Team")
 * @ORMJoinColumn(name="visitor_team_id", referencedColumnName="id")
 * @JMSSerializerSerializedName("visitor")
 */
private $visitorTeam;
}

So when i get this Json

{"home": "id1", "visitor": "id2"}

Get the related entities. Any clouds?? i can't figure it out

Thanks in advance

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Custom serializer handler allows to do it.

At first, you need to create your own serialization handler. Something like this:

<?php

namespace AppBundleSerializerHandler;

use DoctrineORMEntityManagerInterface;
use SymfonyBridgeDoctrineRegistryInterface;
use JMSSerializerContext;
use JMSSerializerExceptionInvalidArgumentException;
use JMSSerializerGenericDeserializationVisitor;
use JMSSerializerHandlerSubscribingHandlerInterface;
use JMSSerializerVisitorInterface;
use JMSSerializerGraphNavigator;

class EntityHandler implements SubscribingHandlerInterface
{
    /**
     * @var RegistryInterface
     */
    protected $registry;

    /**
     * @return array
     */
    public static function getSubscribingMethods()
    {
        $methods = [];

        foreach (['json', 'xml', 'yml'] as $format) {
            $methods[] = [
                'type' => 'Entity',
                'direction' => GraphNavigator::DIRECTION_DESERIALIZATION,
                'format' => $format,
                'method' => 'deserializeEntity',
            ];

            $methods[] = [
                'type' => 'Entity',
                'format' => $format,
                'direction' => GraphNavigator::DIRECTION_SERIALIZATION,
                'method' => 'serializeEntity',
            ];
        }

        return $methods;
    }

    /**
     * EntityHandler constructor.
     * @param RegistryInterface $registry
     */
    public function __construct(RegistryInterface $registry)
    {
        $this->registry = $registry;
    }

    /**
     * @param VisitorInterface $visitor
     * @param $entity
     * @param array $type
     * @param Context $context
     * @return mixed
     */
    public function serializeEntity(VisitorInterface $visitor, $entity, array $type, Context $context)
    {
        $entityClass = $this->getEntityClassFromParameters($type['params']);
        if (!$entity instanceof  $entityClass) {
            throw new InvalidArgumentException(
                sprintf("Entity class '%s' was expected, but '%s' got", $entityClass, get_class($entity))
            );
        }

        $entityManager = $this->getEntityManager($entityClass);
        $primaryKeyValues = $entityManager->getClassMetadata($entityClass)->getIdentifierValues($entity);
        if (count($primaryKeyValues) > 1) {
            throw new InvalidArgumentException(
                sprintf("Composite primary keys does'nt supported now (found in class '%s')", $entityClass)
            );
        }
        if (!count($primaryKeyValues)) {
            throw new InvalidArgumentException(
                sprintf("No primary keys found for entity '%s')", $entityClass)
            );
        }

        $id = array_shift($primaryKeyValues);
        if (is_int($id) || is_string($id)) {
            return $visitor->visitString($id, $type, $context);
        } else {
            throw new InvalidArgumentException(
                sprintf(
                    "Invalid primary key type for entity '%s' (only integer or string are supported",
                    $entityClass
                )
            );
        }
    }

    /**
    * @param GenericDeserializationVisitor $visitor
    * @param string $id
    * @param array $type
    */
    public function deserializeEntity(GenericDeserializationVisitor $visitor, $id, array $type)
    {
        if (null === $id) {
            return null;
        }

        if (!(is_array($type) && isset($type['params']) && is_array($type['params']) && isset($type['params']['0']))) {
            return null;
        }

        $entityClass = $type['params'][0]['name'];
        $entityManager = $this->getEntityManager($entityClass);

        return $entityManager->getRepository($entityClass)->find($id);
    }

    /**
     * @param array $parameters
     * @return string
     */
    protected function getEntityClassFromParameters(array $parameters)
    {
        if (!(isset($parameters[0]) && is_array($parameters[0]) && isset($parameters[0]['name']))) {
            throw new InvalidArgumentException('Entity class is not defined');
        }

        if (!class_exists($parameters[0]['name'])) {
            throw new InvalidArgumentException(sprintf("Entity class '%s' is not found", $parameters[0]['name']));
        }

        return $parameters[0]['name'];
    }

    /**
     * @param string $entityClass
     * @return EntityManagerInterface
     */
    protected function getEntityManager($entityClass)
    {
        $entityManager = $this->registry->getEntityManagerForClass($entityClass);
        if (!$entityManager) {
            throw new InvalidArgumentException(
                sprintf("Entity class '%s' is not mannaged by Doctrine", $entityClass)
            );
        }

        return $entityManager;
    }
}

Then you should register it in your service configuration file. If you use yaml, it will be something like that:

custom_serializer_handle:
    class: AppBundleSerializerHandlerEntityHandler
    arguments: ['@doctrine']
    tags:
        - {name: 'jms_serializer.subscribing_handler'}

In your entity, define JMSSerializer Type annotation

/**
 * @var Team
 * * @ORMManyToOne(targetEntity="Team")
 * @ORMJoinColumn(name="home_team_id", referencedColumnName="id")
 * @JMSSerializerSerializedName("home")
 * @JMSSerializerType("Entity<AppBundleEntityTeam>")
 * List item
 */
private $homeTeam;

Don't forget clear caches. That's all.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...