17

Symfony2、Doctrine、FOSRestBundle、および JMSSerializer を使用して REST API を構築しています。

私が抱えている問題は、エンティティをシリアル化するときに、シリアライザーが関連するエンティティを引き込むことです。たとえば、ボードの一部であるストーリーの一部であるタスクの場合、タスクをシリアル化すると、ボードを含むストーリーを含む出力が得られ、ボード上の他のすべてのストーリーが含まれます。

これを制限し、代わりにforeignIdsを含める簡単な方法はありますか?

4

5 に答える 5

19

JMS 除外ポリシーを使用します。

子と製品関連のエンティティを含めたくない場合に、カテゴリ エンティティに注釈を使用する例:

use ...
    JMS\SerializerBundle\Annotation\ExclusionPolicy,
    JMS\SerializerBundle\Annotation\Exclude,
    ...;

/**
 * ...
 * @ExclusionPolicy("none")
 */
class Category
{
   /**
    * ...
    * @Exclude
    */
   private $children;

   /**
    * ...
    * @Exclude
    */
   private $products;

}

詳細については、JMSSerializer のドキュメントを参照してください。

編集:

たとえば、部分キーワードを使用して、必要なデータのみを選択できます。私の人生では、エンティティオブジェクトをシリアライザーに渡すと(DoctrineProxyHandlerでロードを無効にしても)、関連エンティティ全体のロードを無効にすることはできませんでしたが(DoctrineProxyHandlerでロードを無効にしても)、配列を使用すると、それよりもプロキシを介して教義の遅延読み込みを使用しません (ofc の予想どおり)。

サンプル エンティティを使用した例:

$dql = "SELECT t, s, partial b.{id}, partial ss.{id}
        FROM Acme\AppBundle\Entity\Task t
        JOIN t.story s
        JOIN s.board b
        JOIN b.stories ss"

$q = $this->_em-createQuery($dql);

$result = $q->getArrayResult();

このようにして、次のようなものが得られます。

[
{
    id: 33,
    title: "My Task",
    story: [
    {
        id: 554,
        board: [
        {
            id: 14,
            stories: [
            {
                id: 554
            },
            {
                id: 3424
            },
            {
                id: 3487
            }
            ]
        }
        ]
    }
    ]

}
]

PS私は実際にこの「問題」に興味をそそられています。とにかく、配列の結果を使用せずにエンティティ オブジェクトをシリアル化する方法の解決策を考えてみます。

于 2012-08-14T16:25:33.413 に答える
8

JMSSerializerBundleのSerializer/Handler/DoctrineProxyHandler.phpファイルを確認してください。ここで、次の行をコメントすると:

public function serialize(VisitorInterface $visitor, $data, $type, &$handled)
    {
        if (($data instanceof Proxy || $data instanceof ORMProxy) && (!$data->__isInitialized__ || get_class($data) === $type)) {
            $handled = true;

            if (!$data->__isInitialized__) {
                //$data->__load();
            }

エンティティの遅延読み込みを停止します。これが探しているものである場合は、先に進んで、遅延読み込みを行わない独自のハンドラーを作成してください。

これが正しくない場合は、必要に応じてエンティティを JMSSerializerBundle に送信する前にカスタマイズすることをお勧めします。たとえば、関連エンティティでは ID が必要ですが、他のエンティティではコードや名前などのカスタム列名が必要です。

エンティティ オブジェクトのコピーを作成し、関係に必要なフィールドの取得を開始するだけです。次に、そのコピーを連載します。JMSSerializerBundle は、既に適切なフィールドを提供しているため、遅延ロードしません。

于 2012-08-15T05:57:11.460 に答える
0

これは、結合を使用せずに、1 対 1 または 1 対多の関連付けられたエンティティの ID を一般的な方法で選択する関数です。

function selectWithAssociations($doctrine, $className) {

    $em = $doctrine->getManager();
    $meta = $em->getClassMetadata($className);

    //explicitly get IDs of associated entities
    $assocClauses = array();
    foreach ($meta->getAssociationMappings() as $assocName => $assoc) {
        if (isset($assoc['joinTable'])) {
            //todo: doesn't handle many to many associations
        } else {
            $assocClauses[] = ", IDENTITY(e.$assocName) AS $assocName";
        }
    }

    //run custom DQL query
    $q = $em->createQuery('SELECT e AS _d' . implode('', $assocClauses) . ' FROM ' . $className . ' e');
    $result = $q->getArrayResult();

    return $result;
}
于 2013-12-09T17:54:53.117 に答える
0

以下は、JMS シリアライザー ExclusionStrategy として使用できる 1 つまたは複数のアソシエーションの遅延読み込みを防止するクラスです。

use Doctrine\ORM\PersistentCollection;
use Doctrine\ORM\Proxy\Proxy;
use JMS\Serializer\Context;
use JMS\Serializer\Exclusion\ExclusionStrategyInterface;
use JMS\Serializer\Metadata\ClassMetadata;
use JMS\Serializer\Metadata\PropertyMetadata;
use JMS\Serializer\SerializationContext;

/**
 * Class OnlyLoadedAssociationsExclusionStrategy
 *
 * http://stackoverflow.com/questions/11851197/avoiding-recursion-with-doctrine-entities-and-jmsserializer
 */
class OnlyLoadedAssociationsExclusionStrategy implements ExclusionStrategyInterface
{
    public function shouldSkipClass(ClassMetadata $metadata, Context $context)
    {
    }

    public function shouldSkipProperty(PropertyMetadata $property, Context $context)
    {
        if ($context instanceof SerializationContext){
            $vistingSet=$context->getVisitingSet();

            //iterate over object to get last object
            foreach ($vistingSet as $v){
                $currentObject=$v;
            }

            $propertyValue=$property->getValue($currentObject);

            if ($propertyValue instanceof Proxy){
                // skip not loaded one association
                if (!$propertyValue->__isInitialized__){
                    return true;
                }
            }

            if ($propertyValue instanceof PersistentCollection){
                // skip not loaded many association
                if (!$propertyValue->isInitialized()){
                    return true;
                }
            }
        }
        return false;
    }
}

使用例:

$serializationContext->addExclusionStrategy(
     new OnlyLoadedAssociationsExclusionStrategy()
);
于 2015-08-24T15:15:18.057 に答える