Symfony 4 serialize entity wihout relations

时间:2018-02-01 18:32:14

标签: serialization doctrine-orm entity symfony4

I've have to log changes of each entities. I've Listener which listen for doctrine's events on preRemove, postUpdate and postDelete. My enity AccessModule has relations:

App\Entity\AccessModule.php

/**
 * @ORM\OneToMany(targetEntity="App\Entity\AccessModule", mappedBy="parent")
 * @ORM\OrderBy({"id" = "ASC"})
 */
private $children;

/**
 * @ORM\ManyToOne(targetEntity="App\Entity\AccessModule", inversedBy="children")
 * @ORM\JoinColumn(name="parent_id", referencedColumnName="id", nullable=true)
 */
private $parent;

/**
 * @ORM\ManyToMany(targetEntity="App\Entity\AccessModuleRoute", inversedBy="access_modules")
 * @ORM\JoinTable(name="access_routes",
 *     joinColumns={@ORM\JoinColumn(name="access_module_id", referencedColumnName="id")},
 *     inverseJoinColumns={@ORM\JoinColumn(name="route_id", referencedColumnName="id")})
 *
 */
private $routes;

in listener: App\EventListener\EntityListener.php

use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Encoder\XmlEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;


    $encoders = [new XmlEncoder(), new JsonEncoder()];
    $normalizer = new ObjectNormalizer();

        $normalizer->setCircularReferenceHandler(function ($object) {
            return $object->getId();
        });

    $this->serializer = new Serializer([$normalizer], $encoders);


public function createLog(LifecycleEventArgs $args, $action){
    $em = $args->getEntityManager();
    $entity = $args->getEntity();
    if ($this->tokenStorage->getToken()->getUser()) {
        $username = $this->tokenStorage->getToken()->getUser()->getUsername();
    } else {
        $username = 'anon'; // TODO Remove anon. set null value
    }

    $log = new Log();
//      $log->setData('dddd')
        $log->setData($this->serializer->serialize($entity, ''json)
            ->setAction($action)
            ->setActionTime(new \DateTime())
            ->setUser($username)
            ->setEntityClass(get_class($entity));
        $em->persist($log);
        $em->flush();
    }

I've problem with serialization When I use $log->setData($entity) I get problem with Circular. Whan I do serialization $log->setData($this->serializer->serialize($entity, ''json) I get full of relations, with parent's children, with children children. In a result I get full tree :/ I'd like to get

Expect

[
 'id' => ID,
 'name' => NAME,
 'parent' => parent_id // ManyToOne, I'd like get its id
 'children' => [$child_id, $child_id, $child_id] // array of $id of children array collection
]

(ofcourse this is draft before encode it to json)

How can I get expected data without full relations?

3 个答案:

答案 0 :(得分:2)

您正在寻找的是称为序列化组:herehere

现在让我解释它是如何工作的。这很简单。假设你有帖子实体:

class Post
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     * @Groups({"default"})
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\User\User")
     * @Groups({"default"})
     */
    private $author;
}

你还有用户实体:

class User
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     * @Groups({"default"})
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=40)
     * @Groups({"default"})
     */
    private $firstName;

    /**
     * @ORM\Column(type="string", length=40)
     */
    private $lastName;
}

帖子可以有作者(用户),但我不想每次都返回所有用户数据。我只对id和名字感兴趣。

仔细查看 @Groups 注释。您可以指定所谓的序列化组。它只是告诉Symfony您希望在结果集中包含哪些数据的便捷方式。

您必须通过在属性/ getter上方添加注释形式的相关组来告诉Symfony序列化程序您希望保留哪些关系。您还必须指定要保留的关系的属性或getter。

现在如何让Symfony知道这些东西?

准备/配置序列化服务时,您只需提供如下定义的组:

return $this->serializer->serialize($data, 'json', ['groups' => ['default']]);

围绕本机symfony序列化程序构建某种包装器服务是很好的,这样可以简化整个过程并使其更具可重用性。

还要确保序列化程序配置正确 - 否则不会考虑这些组。

这也是“处理”循环引用的一种方式(以及其他方式)。

现在您只需要研究如何格式化结果集。

答案 1 :(得分:2)

在Symfony 4.1中进行了测试,这是实际上有效的文档https://symfony.com/blog/new-in-symfony-2-7-serialization-groups

Robert的解释https://stackoverflow.com/a/48756847/579646缺少$ classMetadataFactory才能正常工作。这是我的代码:

    $classMetadataFactory = new ClassMetadataFactory(new AnnotationLoader(new AnnotationReader()));
    $encoders = [new JsonEncoder()];
    $normalizer = new ObjectNormalizer($classMetadataFactory);
    $normalizer->setCircularReferenceLimit(2);
    // Add Circular reference handler
    $normalizer->setCircularReferenceHandler(function ($object) {
        return $object->getId();
    });
    $normalizers = [$normalizer];
    $serializer = new Serializer($normalizers, $encoders);
    $jsonContent = $serializer->serialize($jobs, 'json', array('groups' => ['default']));

    return JsonResponse::fromJsonString($jsonContent);

答案 2 :(得分:2)

我同意max4ever关于Roberts的回答。不对我已经测试过max4ever的解决方案,并且在Symfony 4.2上运行良好。

但是,我想向表中添加一个更简单的解决方案。您可以使用$normalizer->setignoredattributes()完全完全忽略属性。无需对属性,工厂,注释进行分组。很简单的。看看。

use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;

$normalizer = new ObjectNormalizer();
$normalizer->setIgnoredAttributes(array('age'));
$encoder = new JsonEncoder();

$serializer = new Serializer(array($normalizer), array($encoder));
$serializer->serialize($person, 'json');

关键是:

$normalizer->setIgnoredAttributes(array('age')); 

那只是文档中的示例,您可以在此处阅读: https://symfony.com/doc/current/components/serializer.html#ignoring-attributes


我想添加我的使用方案,以防偶然帮助某人。因此,我试图在Symfony项目上将Axios与VueJs一起使用,因此我需要将一些json发送到前端。不幸的是,我尝试序列化的对象与Users对象具有ManyToOne关系。因此,在这里,我调试了“ 序列化类的对象时检测到循环引用... ”。我只是将这段代码放在控制器中。

use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\Encoder\JsonEncoder;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;

$user = $this->getUser();     // get the current user.
$lists = $this->getDoctrine()->getRepository(Lists::class)->findBy(array('userid' => $user->getId()));     // Find all lists owned by current user.

$normalizer = new ObjectNormalizer();  
$normalizer->setIgnoredAttributes(array('userid'));    // I only want data about each list. $userid is a reference back to the user that owns the list. So we ignore userid.
$encoder = new JsonEncoder();
$serializer = new Serializer(array($normalizer), array($encoder));
$json = $serializer->serialize($lists, 'json');     
return JsonResponse::fromJsonString($json);

截至2018年12月17日使用Symfony 4.2的作品。