Jmsserializerbundle: @VirtualProperty makes collection null

Created on 25 Jul 2017  Â·  24Comments  Â·  Source: schmittjoh/JMSSerializerBundle

Hi.
I use @VirtualProperty annotation within a method that implements Criteria to filter down results. However, the collection gets null and Symfony throws a FatalErrorException:

Error: Call to a member function matching() on null

Without VirtualProperty, Criteria works normal. Could you help? Thanks in advance.

    /**
     * @var \Doctrine\Common\Collections\Collection
     * @Serializer\Exclude()
     */
    private $bestThings;

    /**
     * @Serializer\VirtualProperty()
     * @return \Doctrine\Common\Collections\Collection
     */
    public function getBestThings()
    {
        $criteria = Criteria::create();
        $criteria->where(Criteria::expr()->eq('visible', true));
        return $this->bestThings->matching($criteria);
    }

Stack Trace: (1 Line only)

[1] Symfony\Component\Debug\Exception\FatalErrorException: Error: Call to a member function matching() on null
    at n/a
        in /home/webridge/public_html/demo/igg/src/System/RestaurantBundle/Entity/Restaurant.php line 1326

Restaurant.php
https://gist.github.com/aliemre/8cc4afd39bf612031243f4352a96d8d3

Running Symfony 2.8 and JmsSerializerBundle 1.1.

bug

Most helpful comment

Fixed in https://github.com/schmittjoh/serializer/pull/1126

Was quite a challenge! Thanks to @flohw for the effort in isolating the issue in a reproducible project

All 24 comments

Can you post the error trace?

@goetas Updated the post.

$this->bestThings is null... you have to instantiate it in the constructor.

is this a deserialized object that gets re-serialized?

  • if the object was instantiated manually (and the constructor was called), the behavior you described should be impossible.
  • if the object was instantiated by doctrine, the bestThings should be a PersistentCollection injected by doctrine (depending on your metadata)

This is a normal object. After instantiated by doctrine, it is serialized. BestThings is a one-to-many association. So, I think it should be an ArrayCollection. I have also initiated in the constructor as ArrayCollection.

Iniated ArrayCollection with real objects (by doctrine) is serialized successfully without using VirtualProperty. I get JSON array after request. My fault if I don't understand you @goetas .

screen shot 2017-07-25 at 18 55 22

~are you using it with the symfony bundle, or using is as standalone library?~
saw now, you are using the bundle...

hmm... weird... you can try to see if the object is a proxy... maybe the serialized did not trigger the proxy initialization... but still weird to me..

Hi. I hope to offend nobody to wake an old/stale issue. But I'm affraid to face the same problem without any solution/workaround found yet. The only difference is that the all the entitie's fields are null and not only the collection and as aliemre mentionned, my collection is not empty and none of the entities are null.

I pasted the stacktrace on pasatebin. This is an array of CommercialArea with a commercials ArrayCollection of AdminUsers with a roles ArrayCollection of Roles. The property role is not exposed directly but exposed with two virtual properties : getRoles (serialized as roleNames) wich returns an array of role names as string (this virtual property is typed as array. The second one is getRoleIds (serialized as rolesIds) wich returns an array of role ids as int (the virtual property is not typed). I use the ArrayCollection::map and toArray to transform my ArrayCollection to an array of string/int.

I use SF4.3 with jms serializer bundle 3.4.1 (jms serializer 3.2.0). This appens during a migration from SF3.4 with jms serializer bundle 2.4.4 (jms serializer 1.14.0) where I had no issues.
Indeed, it looks to appear on collections/ArrayCollection (wich are PersistentCollection when retrieved by doctrine automatically)
I tried to debug with xdebug but didn't understand very well how the entities are traversed. I looks to traverse the whole PersistentCollection preperties (which I don't care) and fails at some point but didn't understand how.

I hope we can find a solution as this is the only problem blocking me for the migration. I have only 6 virtual properties for now which are commented to work on other parts of the migration and it's a small amount of perperties reguarding the base code but still, this is a major issue for me.

Let me know if I can give more info to help.

Context visitingStack not working well

Do you have some custom handlers/event-listeners ? this error seems caused by them.

Thanks for providing the detailed description for your problem, but unfortunately is still very specific and connected to your application... any chance to extract the issue and create a repository that reproduces the issue independently? (so I can debug it)

I have listeners but not related to jms. Related to doctrine but not on the entities mentionned.

I will try to reproduce on a smaller app but it may take some time. As there will be only three entities, I bet this will work fine. :smiling_imp:

@goetas I managed to create a minimal project. I use the same dependencies (not all only the required ones), same configuration and serialization configuration. The only difference between the small project and mine is the mysql version (5.7 here, 5.6 on my project) but is not relevant here as I was able to reproduce !

Here is the project : https://github.com/flohw/jms_issue
You will need docker (or install the project with any other method but it ensure the same configuration). Then run make start to start the environment (php, mysql, nginx, phpmyadmin). And finally DUMP=dump.sql make db-reset to import data (anonymized but field which are null in my project are null here and those with data have data here)
The url to get the error: http://localhost/api/commercial-area there are no other route in the application.

Let me know if you can find something! Thanks

Fixed in https://github.com/schmittjoh/serializer/pull/1126

Was quite a challenge! Thanks to @flohw for the effort in isolating the issue in a reproducible project

Awesome! I didn't expect a fix so quickly!!!
Can I ask when a new release with the fix will be provided? Do I need to update the bundle with it's dependencies and it will update the jms/serializer dependency?

Thank you!

updating jms/serializer should be enough, no need to update the bundle

It works just fine when I installed jms/serializer and targeted dev-master. Will you release a new tag soon?

The same problem seems to appear with a serialization configuration a bit more complex:

App\Entity\SomeEntity:
    properties:
        marketCharges:
            expose: true
            serialized_name: marketCharges
            type: array<int, App\Entity\MarketCharge>
            accessor:
                getter: getMarketCharges

I think it's only due to the type info. I use this method to be able to enforce a key/value association on my array to use it on an angular app in front. The key is important as it represent an id to map on another variable used to display info.
I hope you will be able to reproduce wihtout another isolated project (or at least my previous one handles it)

Did not understand the second part of your message. Did https://github.com/schmittjoh/serializer/pull/1126 solve your issue or not?

Yes but not everywhere...
When I add the type configuration it didn't work. I have the same error message.

This both example work:

App\Entity\SomeEntity:
    properties:
        marketCharges:
            expose: true
            serialized_name: marketCharges
            accessor:
                getter: getMarketCharges
####
App\Entity\SomeEntity:
    virtual_properties:
        getMarketCharges:
            expose: true
            serialized_name: marketCharges

These both didn't:

App\Entity\SomeEntity:
    properties:
        marketCharges:
            expose: true
            serialized_name: marketCharges
            type: array<int, App\Entity\MarketCharge>
            accessor:
                getter: getMarketCharges
######
App\Entity\SomeEntity:
    virtual_properties:
        getMarketCharges:
            expose: true
            serialized_name: marketCharges
            type: array<int, App\Entity\MarketCharge>

Can you share the code of getMarketCharges ?

    public function getMarketCharges()
    {
        // Reindex collection by market id
        $charges = new ArrayCollection();
        $this->marketCharges->map(function (MarketCharge $marketCharge) use ($charges) {
            $charges->set($marketCharge->getMarketId(), $marketCharge);
        });

        return $charges;
    }

I think I can return a real php array instead of an ArrayCollection

Ideed. The serialization works fine but I loss the indexing by $marketCharge->getMarketId() wich is important for the front for now.

I have another part of the code where I return a new ArrayCollection too:

    public function getActivePrices()
    {
        if (null === $this->activePrices) {
            $criteria = Criteria::create()->where(Criteria::expr()->eq('isActive', true));

            $this->activePrices = $this->getPrices()->matching($criteria);
        }

        return $this->activePrices;
    }

Here the order doesn't matter but I use some ArrayCollection method on it. This method is a virtual property too and cause a problem when not commented.

After some more testing I can confirm that returning a new ArrayCollection produces the error.

I hope you can find a solution for that too :slightly_smiling_face:
I won't be able to do anything to help you more this weekend. I wish you a good one :wink:

If you are returning ArrayCollection or any other "doctrine" collection types, you have to use the right type hint (ArrayCollection in this case. See https://jmsyst.com/libs/serializer/master/reference/annotations#type)

It works beautifully with type: ArrayCollection<int, App\Entity\Member\MarketCharge>.

Huge thank you again. I didn't expect a fix so quickly! :grinning:

Was this page helpful?
0 / 5 - 0 ratings