Ajouter des champs supplémentaires en utilisant le paquet Serializer de JMS
j'ai une entité que j'ai l'habitude de sérialiser en utilisant le paquet Serializer de JMS. Je dois ajouter à la sérialisation quelques champs qui ne résident pas dans l'entité elle-même mais qui sont rassemblés avec quelques requêtes de la base de données.
mon idée était de créer un objet personnalisé, remplir les champs avec les champs entity et ajouter le personnalisé. Mais cela semble un peu délicat et coûteux à faire pour chaque variation (j'utilise beaucoup de groupes de sérialisation) de la classe.
y a-t-il une meilleure façon de faire? cette? À l'aide d'une usine? Événements avant / après la sérialisation?
peut-être que je peux écouter pour la sérialisation et vérifier le type d'entité et les groupes de sérialisation ajouter les champs personnalisés? Mais au lieu de faire une requête pour chaque entité, il serait préférable de recueillir toutes les données de ces entités, puis l'ajouter à eux. Toute aide est appréciée
5 réponses
j'ai trouvé la solution toute seule,
pour ajouter un champ personnalisé après que la sérialisation a été faite, nous devons créer une classe d'auditeur comme ceci:
<?php
namespace Acme\DemoBundle\Listener;
use JMS\DiExtraBundle\Annotation\Service;
use JMS\DiExtraBundle\Annotation\Tag;
use JMS\DiExtraBundle\Annotation\Inject;
use JMS\DiExtraBundle\Annotation\InjectParams;
use Symfony\Component\HttpKernel\Event\PostResponseEvent;
use Acme\DemoBundle\Entity\Team;
use JMS\Serializer\Handler\SubscribingHandlerInterface;
use JMS\Serializer\EventDispatcher\EventSubscriberInterface;
use JMS\Serializer\EventDispatcher\PreSerializeEvent;
use JMS\Serializer\EventDispatcher\ObjectEvent;
use JMS\Serializer\GraphNavigator;
use JMS\Serializer\JsonSerializationVisitor;
/**
* Add data after serialization
*
* @Service("acme.listener.serializationlistener")
* @Tag("jms_serializer.event_subscriber")
*/
class SerializationListener implements EventSubscriberInterface
{
/**
* @inheritdoc
*/
static public function getSubscribedEvents()
{
return array(
array('event' => 'serializer.post_serialize', 'class' => 'Acme\DemoBundle\Entity\Team', 'method' => 'onPostSerialize'),
);
}
public function onPostSerialize(ObjectEvent $event)
{
$event->getVisitor()->addData('someKey','someValue');
}
}
de Cette façon, vous pouvez ajouter des données à la sérialisé élément.
au lieu de cela, si vous voulez éditer un objet juste avant la sérialisation utilisez l'événement pre_serialize, soyez conscient que vous devez déjà avoir une variable (et les groupes de sérialisation corrects) si vous voulez utiliser pre_serialize pour ajouter un valeur.
je suis surpris que personne n'ait suggéré une façon beaucoup plus facile. Vous devez juste utiliser @VirtualProperty:
<?php
// ...
/**
* @JMS\VirtualProperty
* @JMS\SerializedName("someField")
*/
public function getSomeField()
{
return $this->getTitle() . $this->getPromo();
}
pour répondre à la question initiale. Voici comment limiter les données ajoutées pour certains groupes sérialisés (dans cet exemple some_data
n'est ajouté que lorsque nous n'utilisons pas le list
groupe:
public function onPostSerializeSomeEntityJson(ObjectEvent $event) {
$entity = $event->getObject();
if (!in_array('list', (array)$event->getContext()->attributes->get('groups'))) {
$event->getVisitor()->addData('user_access', array(
'some_data' => 'some_value'
));
}
}
(array)$event->getContext()->attributes->get('groups')
contient un tableau des groupes sérialisés utilisés.
la réponse acceptée ne fonctionne que lorsque le visiteur est dérivé de \JMS\Serializer\GenericSerializationVisitor
. Cela signifie que cela fonctionnera pour JSON, mais échouera pour XML.
voici un exemple de méthode qui s'adaptera au XML. Il examine les interfaces que l'objet visiteur supporte et agit de manière appropriée. Il montre comment vous pouvez ajouter un élément de lien aux objets JSON et XML sérialisés...
public function onPostSerialize(ObjectEvent $event)
{
//obtain some data we want to add
$link=array(
'rel'=>'self',
'href'=>'http://example.org/thing/1',
'type'=>'application/thing+xml'
);
//see what our visitor supports...
$visitor= $event->getVisitor();
if ($visitor instanceof \JMS\Serializer\XmlSerializationVisitor)
{
//do XML things
$doc=$visitor->getDocument();
$element = $doc->createElement('link');
foreach($link as $name => $value) {
$element->setAttribute($name, $value);
}
$doc->documentElement->appendChild($element);
} elseif ($visitor instanceof \JMS\Serializer\GenericSerializationVisitor)
{
$visitor->addData('link', $link);
}
}
Qu'en ceci: http://jmsyst.com/libs/serializer/master/handlers
en résumé, vous définissez une classe qui reçoit un objet et renvoie du texte ou un tableau (qui sera converti en json).
vous avez la classe "IndexedStuff" qui contient un champ calculé bizarre qui pour une raison quelconque devrait être calculé au moment de la sérialisation.
<?php
namespace Project/Model;
class IndexedStuff
{
public $name;
public $value;
public $rawData;
}
maintenant, créez le handler
<?php
namespace Project/Serializer;
use JMS\Serializer\Handler\SubscribingHandlerInterface;
use JMS\Serializer\GraphNavigator;
use JMS\Serializer\JsonSerializationVisitor;
use JMS\Serializer\Context;
class MyHandler implements SubscribingHandlerInterface
{
public function setEntityManager(Registry $registry) {
// Inject registry instead of entity manager to avoid circular dependency
$this->em = $registry->getEntityManager();
}
public static function getSubscribingMethods()
{
return array(
array(
'direction' => GraphNavigator::DIRECTION_SERIALIZATION,
'format' => 'json',
'type' => 'Project/Model/IndexedStuff',
'method' => 'serializeIndexedStuffToJson',
),
);
}
public function serializeIndexedStuffToJson(JsonSerializationVisitor $visitor, Project/Model/IndexedStuff $stuff, array $type, Context $context)
{
// Build your object here and return it
$score = $this->em->find("ProjectBundle:Calculator", $stuff->value)
return array("score" => $score->getIndexScore(), "name"=> $score->name
}
}
enfin enregistrer le service
services:
project.serializer.stuff:
class: Project\Serializer\MyHandler
calls:
- [setEntityManager, ["@doctrine"]]
maintenant partout où vous voulez sérialiser un objet de type "IndexedStuff" vous obtiendrez un json comme ceci
{"name": "myName", "score" => 0.3432}
de cette façon, vous pouvez personnaliser la façon dont votre entité est sérialisé