Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JMS serializer - Why are new objects not being instantiated through constructor

Why are new entities instantiated with null for all values except the data in the json, why is the entity constructor not setting defaults - putting a die() in the constructor never gets executed.

Update:

Ok so digging into the code, when no managed entity is found, JMSS will use the doctrine instantiator class to create the entity - its sole job, to create entities without calling the constructor. Is there a reason for this? this is inside JMS\Serializer\Construction\UnserializeObjectConstructor


I've configured the object constructor to use the doctrine object constructor written by JMS, but the same issue happens with and without this.

jms_serializer.object_constructor:
    alias: jms_serializer.doctrine_object_constructor
    public: false

Existing entities are updated without trouble, however new entities are missing all constructor set defaults.

Under 'fields' element 0 is existing, element 1 is new.

array (size=3)
  'id' => int 2
  'name' => string 'Categories' (length=10)
  'fields' => 
    array (size=2)
      0 => 
        array (size=7)
          'id' => int 49
          'displayName' => string 'Car Branded' (length=11)
          'type' => string 'checkboxlist' (length=12)
          'required' => boolean false
          'disabled' => boolean false
          'name' => string 'h49' (length=3)
      1 => 
        array (size=3)
          'type' => string 'email' (length=5)
          'name' => string 'field3491' (length=9)
          'displayName' => string 'Email' (length=5)

The entity looks like this after deserializing:

object(stdClass)[2000]
  public '__CLASS__' => string 'AppBundle\Entity\FormElement' (length=28)
  public 'id' => null
  public 'label' => string 'Email' (length=5)
  public 'type' => string 'email' (length=5)
  public 'defaultValue' => null
  public 'required' => null
  public 'mappedField' => null
  public 'garbageCollection' => null
  public 'sortOrder' => null
  public 'disabled' => null
  public 'uuid' => null
  public 'form' => null
  public 'multiOptions' => null
  public 'filters' => null
  public 'submissions' => null

The entity constructor:

public function __construct()
{
    $this->required = false;
    $this->disabled = false;
    $this->garbageCollection = false;
    $this->sortOrder = 0;
    $this->type = 'text';
}

And finally this is how im deserializing:

$serializer = $this->get('jms_serializer');

$entryForm = $serializer->deserialize($json_data, 'AppBundle\Entity\EntryForm', 'json');
like image 391
mr12086 Avatar asked Aug 11 '15 17:08

mr12086


1 Answers

The issue is the default ObjectConstructor uses Doctrine's Instantiator, which does not call the class' constructor. To solve this, you can create your own ObjectConstructor that just returns a new instance of the class.

Example:

<?php

namespace AppBundle\Serializer;

use JMS\Serializer\Construction\ObjectConstructorInterface;
use JMS\Serializer\DeserializationContext;
use JMS\Serializer\Metadata\ClassMetadata;
use JMS\Serializer\VisitorInterface;

class ObjectConstructor implements ObjectConstructorInterface
{
    /**
     * {@inheritdoc}
     */
    public function construct(
        VisitorInterface $visitor,
        ClassMetadata $metadata,
        $data,
        array $type,
        DeserializationContext $context
    ) {
        $className = $metadata->name;

        return new $className();
    }
}

If you're using the bundle, just set jms_serializer.unserialize_object_constructor.class parameter to that new class. Otherwise in your builder, use the class as your object constructor.

like image 71
mloberg Avatar answered Oct 16 '22 20:10

mloberg