Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot access uninitialized non-nullable property when accessing a non-db field property of a document #2349

Open
andythorne opened this issue Jul 30, 2021 · 6 comments · May be fixed by #2399
Labels
Projects

Comments

@andythorne
Copy link

Bug Report

Q A
BC Break yes
Version 2.2.1

Summary

This happens on PHP >= 7.4 where the Document properties have been type hinted.

A fatal error is thrown when you access a property of a Proxy document where the property itself is NOT a field managed by the ODM, even if that property has a default value. This only seems to happen when the Proxy document has been loaded directly from the ODM (i.e. NOT from a __get call from accessing $document->someProperty).

Current behaviour

Throws a fatal error when property is accessed:

  [Error]                                                                                              
  Cannot access uninitialized non-nullable property App\Document\Customer::$domainEvents by reference  

How to reproduce

Reproduced on this repo: https://github.com/andythorne/doctrine-odm-example-error

Key bit copied from above repo. See it's documents and command to reproduce the error

// The Order is loaded directly via the DM. The returned document is an Order object, as expected. The Order
// object has a customer property that contains a Proxy<Customer> object.
/** @var Order $order */
$order = $this->documentManager->getRepository(Order::class)->find($orderId);

// Fetch a list of Customers from the DB. Any customer object that was referenced in the above order is still
// a proxy object, however it will not have the defaults set for the un-managed $domainEvents property.
$customers = $this->documentManager->getRepository(Customer::class)->findAll();

/** @var Customer $customer */
foreach ($customers as $customer) {
    $customer->doSomeUpdate(); // This will trigger an error as we try to append to the non-existing $domainEvents property
}

Expected behaviour

Any properties should have their default values set from the Document class.

@malarzm malarzm added the Bug label Aug 1, 2021
@malarzm malarzm added this to the 2.2.2 milestone Aug 1, 2021
@malarzm malarzm added this to 2.2 (bug fixes only) in Roadmap Aug 1, 2021
@alcaeus alcaeus modified the milestones: 2.2.2, 2.2.3 Aug 5, 2021
@webmozart
Copy link
Contributor

I'm having the same problem, unfortunately.

@ozahorulia
Copy link

ozahorulia commented Feb 10, 2022

I'm having the same problem with PHP 8-style constructors:

    public function __construct(
        private ?array $userMetaData = null
    ) {

@Steveb-p
Copy link
Contributor

@hast constructors are not called by Doctrine initializer as far as I know, unless something changed recently.

Reason being this allows you to have your own constructors with enforced data, while Doctrine is "only" interested in setting the relevant fields with values coming from database.

If you need to do some set up when Doctrine loads an object, then you can use PostLoad lifecycle event.

@IonBazan
Copy link
Member

IonBazan commented Feb 10, 2022

Exactly, like @Steveb-p said - when Doctrine hydrates your entities via Proxy objects, constructor is not called because the remaining data should be hydrated from DB. This is expected behaviour and applies to ORM AFAIK.

I understand that sometimes you may need to set some missing stuff in unmanaged properties to make your Document class "valid" but you may achieve that by using PostLoad callback (and calling it again from constructor when you create the instance manually).

#[Document]
#[HasLifecycleCallbacks]
class Customer
{
    public function __construct()
    {
        $this->init();
    }

    #[PostLoad]
    public function init(): void
    {
        $this->myField = 'test';
    }
}

@ozahorulia
Copy link

ozahorulia commented Feb 14, 2022

@Steveb-p I understand that constructor is not called, but in this case those are not constructor logic or initialization, but a new way of declaring properties as of PHP 8. That's why I suppose It must follow the same logic as with old-style property declaration, mustn't it?

UPD:

Sorry, my initial code snippet was incorrect. What I meant was:

    public function __construct(
        private ?array $userMetaData = null
    ) {

@malarzm
Copy link
Member

malarzm commented Feb 15, 2022

I'm afraid this needs to be solved on https://github.com/Ocramius/ProxyManager level

@malarzm malarzm modified the milestones: 2.3.2, 2.3.3 Mar 8, 2022
@malarzm malarzm modified the milestones: 2.3.3, 2.3.4 Apr 20, 2022
@malarzm malarzm modified the milestones: 2.4.1, 2.4.2, 2.4.3 May 10, 2022
@malarzm malarzm modified the milestones: 2.4.3, 2.4.4 Dec 20, 2022
@malarzm malarzm modified the milestones: 2.4.4, 2.5.1 Apr 5, 2023
@malarzm malarzm added this to the 2.5.2 milestone Apr 5, 2023
@malarzm malarzm modified the milestones: 2.5.2, 2.5.3 Apr 13, 2023
@malarzm malarzm modified the milestones: 2.5.3, 2.5.4 Oct 23, 2023
@malarzm malarzm modified the milestones: 2.5.4, 2.5.5 Nov 3, 2023
@alcaeus alcaeus removed this from the 2.5.5 milestone Nov 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
No open projects
Roadmap
2.2 (unsupported)
Development

Successfully merging a pull request may close this issue.

8 participants