web-dev-qa-db-ja.com

JMS Serializerバンドルを使用して追加フィールドを追加する

私は通常、JMS Serializerバンドルを使用してシリアル化するエンティティを持っています。エンティティ自体には存在しないが、dbクエリで収集されるフィールドをシリアル化に追加する必要があります。

私のアイデアは、カスタムオブジェクトを作成し、フィールドにエンティティフィールドを入力して、カスタムフィールドを追加することでした。しかし、これはクラスのすべてのバリエーション(多くのシリアル化グループを使用します)に対して行うには少し注意が必要で、費用がかかりそうです。

これを行うためのより良い/標準的な方法はありますか?工場を使用していますか?シリアル化の前後のイベント?

たぶん、シリアル化をリッスンし、エンティティタイプとシリアル化グループがカスタムフィールドを追加することを確認できますか?ただし、各エンティティに対してクエリを作成する代わりに、関連するエンティティのすべてのデータを収集し、それらに追加することをお勧めします。どんな助けも大歓迎です

36
alex88

私は自分で解決策を見つけました、

シリアル化が完了した後にカスタムフィールドを追加するには、次のようなリスナークラスを作成します。

<?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');
    }
}

そうすれば、シリアル化された要素にデータを追加できます。

代わりに、シリアル化の直前にオブジェクトを編集してpre_serializeイベントを使用する場合、pre_serializeを使用して値を追加する場合は、変数(および正しいシリアル化グループ)が既に必要であることに注意してください。

69
alex88

誰もこれほど簡単な方法を提案していないのには驚いています。 @VirtualPropertyを使用するだけです:

<?php

// ...
/**
 * @JMS\VirtualProperty
 * @JMS\SerializedName("someField")
 */
public function getSomeField()
{
    return $this->getTitle() . $this->getPromo();
}
14
James Akwuh

元の質問にさらに回答するため。一部のシリアル化されたグループの追加データを制限する方法を次に示します(この例ではsome_dataは、listグループを使用していない場合にのみ追加されます。

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')には、使用されるシリアル化されたグループの配列が含まれます。

10

受け入れられた答えは、訪問者が\JMS\Serializer\GenericSerializationVisitor。つまり、JSONでは機能しますが、XMLでは機能しません。

XMLに対処するメソッドの例を次に示します。訪問者オブジェクトがサポートし適切に動作するインターフェースを調べます。 JSONおよびXMLシリアル化オブジェクトの両方にリンク要素を追加する方法を示しています...

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);
    }


}
5
Paul Dixon

これはどうですか: http://jmsyst.com/libs/serializer/master/handlers

要約すると、オブジェクトを受け取り、テキストまたは配列(jsonに変換される)を返すクラスを定義します。

何らかの理由でシリアル化時に計算する必要がある奇妙な計算フィールドを含むクラス「IndexedStuff」があります。

<?php

namespace Project/Model;

class IndexedStuff
{
   public $name;
   public $value;
   public $rawData;
}

ハンドラーを作成します

<?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
    }
}

最後にサービスを登録します

services:
  project.serializer.stuff:
      class: Project\Serializer\MyHandler
      calls:
        - [setEntityManager, ["@doctrine"]]

タイプ "IndexedStuff"のオブジェクトをシリアル化するすべての場所で、次のようなjsonを取得します。

{"name": "myName", "score" => 0.3432}

この方法により、エンティティのシリアル化方法を完全にカスタマイズできます

3
Álvaro García