NULL returned instead of collection after update to 2.14.0 #7092

Closed
opened 2026-01-22 15:44:28 +01:00 by admin · 13 comments
Owner

Originally created by @DigitalTimK on GitHub (Jan 5, 2023).

BC Break Report

Q A
BC Break yes
Version 2.13.4 => 2.14.0
Symfony 5.4
PHP (on localhost) 8.1.13
PHP (in composer.json) "require": { "php": "^7.3.0" ...}

Summary

After composer update "doctrine/orm" I have an error during my phpunit (v9.5.27) tests. The returned value (children) is null instead of an collection.

Previous behavior

tests are running w/o error

Current behavior

Tests raise an error: TypeError: App\Entity\Pricing::getDiscounts(): Return value must be of type Doctrine\Common\Collections\Collection, null returned

How to reproduce

This error seems to be happen only during PHPUNIT-test. I encounter no issue when calling this function in a controller or in twig.

class Pricing
{
    /**
     * @var int
     *
     * @ORM\Column(name="RabattGruppeID", type="integer", nullable=false, options={"unsigned"=true})
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="CUSTOM")
     * @ORM\CustomIdGenerator(class="App\Service\RandomIdGenerator")
     */
    private $id;

    /* ... */

    /**
     * @ORM\OneToMany(targetEntity=Discount::class, mappedBy="pricing", cascade={"persist"})
     * @ORM\OrderBy({"until" = "ASC"})
     */
    private $discounts;

    public function __construct()
    {
        $this->discounts = new ArrayCollection();
    }

    /**
     * @return Collection|Discount[]
     */
    public function getDiscounts(): Collection
    {
        return $this->discounts;
    }

    public function addDiscount(Discount $discount): self
    {
        if (!$this->discounts->contains($discount)) {
            $this->discounts[] = $discount;
            $discount->setPricing($this);
        }

        return $this;
    }

    public function removeDiscount(Discount $discount): self
    {
        if ($this->discounts->contains($discount)) {
            $this->discounts->removeElement($discount);
            // set the owning side to null (unless already changed)
            if ($discount->getPricing() === $this) {
                $discount->setPricing(null);
            }
        }

        return $this;
    }
}
class Discount
{
    /**
     * @var int
     *
     * @ORM\Column(name="RabattDetailID", type="integer", nullable=false, options={"unsigned"=true})
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="CUSTOM")
     * @ORM\CustomIdGenerator(class="App\Service\RandomIdGenerator")
     */
    private $id;

    /* ... */

    /**
     * @var Pricing
     *
     * @ORM\ManyToOne(targetEntity=Pricing::class, inversedBy="discounts")
     * @ORM\JoinColumn(name="FK_RabattGruppeID", referencedColumnName="RabattGruppeID", nullable=false)
     */
    private $pricing;


    public function getPricing(): ?Pricing
    {
        return $this->pricing;
    }

    public function setPricing(?Pricing $pricing): self
    {
        $this->pricing = $pricing;

        return $this;
    }
}
Originally created by @DigitalTimK on GitHub (Jan 5, 2023). ### BC Break Report | Q | A |------------ | ------ | BC Break | yes | Version | 2.13.4 => 2.14.0 | Symfony | 5.4 | PHP (on localhost) | 8.1.13 | PHP (in composer.json) | "require": { "php": "^7.3.0" ...} #### Summary After `composer update "doctrine/orm"` I have an error during my phpunit (v9.5.27) tests. The returned value (children) is `null` instead of an collection. #### Previous behavior tests are running w/o error #### Current behavior Tests raise an error: `TypeError: App\Entity\Pricing::getDiscounts(): Return value must be of type Doctrine\Common\Collections\Collection, null returned` #### How to reproduce This error seems to be happen only during PHPUNIT-test. I encounter no issue when calling this function in a controller or in twig. ```php class Pricing { /** * @var int * * @ORM\Column(name="RabattGruppeID", type="integer", nullable=false, options={"unsigned"=true}) * @ORM\Id * @ORM\GeneratedValue(strategy="CUSTOM") * @ORM\CustomIdGenerator(class="App\Service\RandomIdGenerator") */ private $id; /* ... */ /** * @ORM\OneToMany(targetEntity=Discount::class, mappedBy="pricing", cascade={"persist"}) * @ORM\OrderBy({"until" = "ASC"}) */ private $discounts; public function __construct() { $this->discounts = new ArrayCollection(); } /** * @return Collection|Discount[] */ public function getDiscounts(): Collection { return $this->discounts; } public function addDiscount(Discount $discount): self { if (!$this->discounts->contains($discount)) { $this->discounts[] = $discount; $discount->setPricing($this); } return $this; } public function removeDiscount(Discount $discount): self { if ($this->discounts->contains($discount)) { $this->discounts->removeElement($discount); // set the owning side to null (unless already changed) if ($discount->getPricing() === $this) { $discount->setPricing(null); } } return $this; } } ``` ```php class Discount { /** * @var int * * @ORM\Column(name="RabattDetailID", type="integer", nullable=false, options={"unsigned"=true}) * @ORM\Id * @ORM\GeneratedValue(strategy="CUSTOM") * @ORM\CustomIdGenerator(class="App\Service\RandomIdGenerator") */ private $id; /* ... */ /** * @var Pricing * * @ORM\ManyToOne(targetEntity=Pricing::class, inversedBy="discounts") * @ORM\JoinColumn(name="FK_RabattGruppeID", referencedColumnName="RabattGruppeID", nullable=false) */ private $pricing; public function getPricing(): ?Pricing { return $this->pricing; } public function setPricing(?Pricing $pricing): self { $this->pricing = $pricing; return $this; } } ```
admin added the BugRegressionWaiting feedback labels 2026-01-22 15:44:29 +01:00
admin closed this issue 2026-01-22 15:44:29 +01:00
Author
Owner

@mbabker commented on GitHub (Jan 5, 2023):

As a suggested best practice, you should instantiate a default collection in your entity's constructor to ensure it has a valid state before it's persisted.

Not knowing how your test works, at the bare minimum, a (new Pricing())->getDiscounts() would hit the same issue of returning null.

@mbabker commented on GitHub (Jan 5, 2023): As a suggested [best practice](https://www.doctrine-project.org/projects/doctrine-orm/en/2.14/reference/best-practices.html#initialize-collections-in-the-constructor), you should instantiate a default collection in your entity's constructor to ensure it has a valid state before it's persisted. Not knowing how your test works, at the bare minimum, a `(new Pricing())->getDiscounts()` would hit the same issue of returning null.
Author
Owner

@joshlopes commented on GitHub (Jan 5, 2023):

I'm actually getting the same, and on my constructor I'm setting an ArrayCollection, but the lazy proxy is returning null instead on my test and strict type failed.

image

foreach() argument must be of type array|object, null given
...
 /var/www/var/cache/test/doctrine/orm/Proxies/__CG__AppBundleEntityProductCategory.php:1402
...

The upgrade was suggested by dependabot but tests started failing.

The issue happens after the data is persisted, the proxy seems unable to fetch the right data or when it does the data is not what it is in the database, so something must be blocking it to properly initialise -- that would be my guess.

@joshlopes commented on GitHub (Jan 5, 2023): I'm actually getting the same, and on my constructor I'm setting an ArrayCollection, but the lazy proxy is returning null instead on my test and strict type failed. ![image](https://user-images.githubusercontent.com/3440849/210821946-18ac9e99-965c-469c-b9de-eecbc05a80aa.png) ``` foreach() argument must be of type array|object, null given ... /var/www/var/cache/test/doctrine/orm/Proxies/__CG__AppBundleEntityProductCategory.php:1402 ... ``` The upgrade was suggested by dependabot but tests started failing. The issue happens after the data is persisted, the proxy seems unable to fetch the right data or when it does the data is not what it is in the database, so something must be blocking it to properly initialise -- that would be my guess.
Author
Owner

@DigitalTimK commented on GitHub (Jan 5, 2023):

Hey @mbabker.
thanks for this quick reaction. Actually I am initiating the variable in the constructor. I didn't put these details in my description above. I updated my initial bug report which is now showing the constructor.

@DigitalTimK commented on GitHub (Jan 5, 2023): Hey @mbabker. thanks for this quick reaction. Actually I am initiating the variable in the constructor. I didn't put these details in my description above. I updated my initial bug report which is now showing the constructor.
Author
Owner

@simonberger commented on GitHub (Jan 5, 2023):

There is a possibility your issues or at least one of those is fixed by #10362. It could be helpful if you try those changes on your tests.

@simonberger commented on GitHub (Jan 5, 2023): There is a possibility your issues or at least one of those is fixed by #10362. It could be helpful if you try those changes on your tests.
Author
Owner

@joshlopes commented on GitHub (Jan 5, 2023):

@simonberger it sounds like it does fix the current issue -- I just tried to manually load the data after using fixtures on my test and the test passed. So between loading fixtures and using it through proxies it fails, all proxy data is empty, only have id, rest of the fields are empty.

Thanks for all your work everyone 👌

@joshlopes commented on GitHub (Jan 5, 2023): @simonberger it sounds like it does fix the current issue -- I just tried to manually load the data after using fixtures on my test and the test passed. So between loading fixtures and using it through proxies it fails, all proxy data is empty, only have id, rest of the fields are empty. Thanks for all your work everyone 👌
Author
Owner

@simonberger commented on GitHub (Jan 5, 2023):

Yes I expected the problem to be your private id field which the PR fixes. In case someone fights the problem a workaround can also be the change the visibility of the identifier to protected.

@simonberger commented on GitHub (Jan 5, 2023): Yes I expected the problem to be your private id field which the PR fixes. In case someone fights the problem a workaround can also be the change the visibility of the identifier to protected.
Author
Owner

@DigitalTimK commented on GitHub (Jan 5, 2023):

I was also able to solve the issue. In my case I lost the data below the initial object (lazy loading?) by decomposing to much the object... so this is why the result was NULL.

Not sure if I shall close this ... at least my solution is a work around.... But at the end this new version created an issue that might need to be solved on doctrine's side.

@DigitalTimK commented on GitHub (Jan 5, 2023): I was also able to solve the issue. In my case I lost the data below the initial object (lazy loading?) by decomposing to much the object... so this is why the result was NULL. Not sure if I shall close this ... at least my solution is a work around.... But at the end this new version created an issue that might need to be solved on doctrine's side.
Author
Owner

@derrabus commented on GitHub (Jan 6, 2023):

If the problem is solved for you, feel free to close. But if we should accept it as a bug, we need a stable reproducer or a failing test. A reproducer would be a repository that I can check out and execute in order to reproduce your problem.

@derrabus commented on GitHub (Jan 6, 2023): If the problem is solved for you, feel free to close. But if we should accept it as a bug, we need a stable reproducer or a failing test. A reproducer would be a repository that I can check out and execute in order to reproduce your problem.
Author
Owner

@DigitalTimK commented on GitHub (Jan 7, 2023):

So, I will close this item as I was not able to produce a failing specimen.

@DigitalTimK commented on GitHub (Jan 7, 2023): So, I will close this item as I was not able to produce a failing specimen.
Author
Owner

@flohw commented on GitHub (Jan 16, 2023):

Is it possible that this issue is related to #10336 ? I had the same behavior : using my fixtures just after they were initialized everything was fine but as soon as I started a functional test (EM::clear() is called), I got this error. Not on collection though but on standard property which were not initialized properly somewhere.

I downgraded to doctrine/orm:2.13.5 for now waiting for the next release.

@flohw commented on GitHub (Jan 16, 2023): Is it possible that this issue is related to #10336 ? I had the same behavior : using my fixtures just after they were initialized everything was fine but as soon as I started a functional test (EM::clear() is called), I got this error. Not on collection though but on standard property which were not initialized properly somewhere. I downgraded to doctrine/orm:2.13.5 for now waiting for the next release.
Author
Owner

@mpdude commented on GitHub (Jan 31, 2023):

@DigitalTimK did I get it right that you intended to close this issue?

@mpdude commented on GitHub (Jan 31, 2023): @DigitalTimK did I get it right that you intended to close this issue?
Author
Owner

@DigitalTimK commented on GitHub (Feb 1, 2023):

@mpdude yes, that correct.

@DigitalTimK commented on GitHub (Feb 1, 2023): @mpdude yes, that correct.
Author
Owner

@Herz3h commented on GitHub (Aug 11, 2023):

Just for information, this happened to me with ORM 2.9.5 after upgrading from php 7.4 to php 8.1 and after debugging, it seems that there was an "invalid mapping" exception that was silently thrown here: https://github.com/doctrine/orm/blob/2.16.x/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php#L269

The reason is that one of my entity's relation, was implementing JsonSerializable, but didn't have a return type. Which threw this error:

Error: During inheritance of JsonSerializable: Uncaught

I had to add a return type to jsonSerialized() as required by php 8.1

And that fixed my Collection being null problem

@Herz3h commented on GitHub (Aug 11, 2023): Just for information, this happened to me with ORM 2.9.5 after upgrading from php 7.4 to php 8.1 and after debugging, it seems that there was an "invalid mapping" exception that was silently thrown here: https://github.com/doctrine/orm/blob/2.16.x/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php#L269 The reason is that one of my entity's relation, was implementing JsonSerializable, but didn't have a return type. Which threw this error: `Error: During inheritance of JsonSerializable: Uncaught` I had to add a return type to `jsonSerialized()` as required by php 8.1 And that fixed my Collection being null problem
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#7092