我有一个由 JMSSerializer 正确序列化/反序列化的 Dashboard
实体(通过 JMSSerializerBundle):
/**
* @ORM\Table(name="dashboard", schema="myappID")
* @ORM\Entity(repositoryClass="Belka\MyBundle\Entity\Repository\DashboardRepository")
*/
class Dashboard
{
/**
* @Id
* @Column(type="integer")
* @GeneratedValue("SEQUENCE")
*
* @Serializer\Groups({"o-all", "o-all-getCDashboard", "i-p2-editDashboard"})
*/
protected $id;
/**
* @ORM\ManyToMany(targetEntity="Belka\MyBundle\Entity\User")
*
* @ORM\JoinTable(name="users_dashboards_associated",
* schema="myAppID",
* joinColumns={@ORM\JoinColumn(name="dashboard_id", referencedColumnName="id")},
* inverseJoinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")}
* )
*
* @Serializer\groups({
* "o-p2-create",
* "i-p2-create",
* "o-p2-patch",
* "i-p2-editDashboard"
* })
*/
protected $users;
}
并且我使用 JMSSerializer 的 jms_serializer.doctrine_object_constructor
作为对象构造函数。
一切都像魅力一样,但我有以下极端情况:有时我必须将 Dashboard::$users
设置为字符串(即当客户端发送语义不正确的 users
> 属性,检查后我返回该对象以及一个字符串以通知它。这对于前端应用程序来说非常方便)。
JMSSerializer takes advantage of the Doctrine's annotation ,但在这种情况下,我真的想以编程方式覆盖它,因为这是一个非常极端的情况。我想到的有两种方法:
- 有没有办法将
SerializationContext
设置为将Dashboard::$users
映射为字符串属性? - 有没有办法在序列化之前更改 Doctrine 的元数据?
- 还有其他我没有意识到的选项?
欢迎任何建议
最佳答案
我找到了一个解决方案,尽管它没有考虑嵌套实体的属性(has-a 关系)。这意味着要访问整个图表,但我没有时间研究优秀的 JMSSSerializer 的内部结构。不过,它非常适合强制第一级实体的属性:
首先,需要一个预序列化
订阅者。它将循环访问 protected 属性并检查它们是否包含字符串。是这样,序列化的类型将被覆盖。
class SerializationSubscriber implements EventSubscriberInterface
{
/**
* @inheritdoc
*/
static public function getSubscribedEvents()
{
return array(
array('event' => 'serializer.pre_serialize', 'method' => 'onPreserialize'),
);
}
public function onPreSerialize(PreSerializeEvent $event)
{
$entity = $event->getObject();
$metadata = $event->getContext()->getMetadataFactory()->getMetadataForClass($event->getType()['name']);
$reflect = new \ReflectionClass($entity);
$props = $reflect->getProperties(\ReflectionProperty::IS_PROTECTED);
foreach ($props as $prop) {
$prop->setAccessible(true);
if (is_string($prop->getValue($entity))) {
// here is the magic
$metadata->propertyMetadata[$prop->name]->type = array('name' => 'string', 'params' => array());
}
}
}
}
接下来,我不想每次序列化某些内容时都听这个。这是我的一项服务中的一个极端案例。我们可以利用 JMS\Serializer\EventDispatcher\EventDispatcher::addSubscriber
,尽管 EventDispatcher
服务被声明为私有(private)
。
因此,让我们通过编译器 channel 将该服务转变为public
,以便利用addSubscriber
:
class MyBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$container->addCompilerPass(new OverrideJmsSerializerEventDispatcherDefPass());
}
}
...让我们将该服务转变为公共(public)
服务
class OverrideJmsSerializerEventDispatcherDefPass implements CompilerPassInterface
{
public function process(ContainerBuilder $container)
{
$definition = $container->getDefinition('jms_serializer.event_dispatcher');
$definition->setPublic(true);
}
}
因此,我们可以将其注入(inject)到我们的服务中。 IE。在我的 services.yml
中:
belka.mybundle.dashboardhandler:
class: Belka\MyBundle\Handlers\DashboardHandler
calls:
- [setEventDispatcher, ["@jms_serializer.event_dispatcher"]]
好吧,现在我们可以在需要时轻松添加订阅者,而无需在每次应用程序执行序列化时增加另一个监听器的负担:
$serializationSubscriber = new SerializationSubscriber();
$this->eventDispatcher->addSubscriber($serializationSubscriber);
请随意使用访问整个实体图的解决方案来完成答案。那太好了。
关于symfony - 如何以编程方式更改 Doctrine 实体属性的序列化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43472400/