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

Multiple level discriminator mapping does not work in combination with a self-referencing one-to-many relation. #6488

Closed
jeroenvrooij opened this issue Jun 7, 2017 · 7 comments

Comments

@jeroenvrooij
Copy link

jeroenvrooij commented Jun 7, 2017

Given the following structure of entities, we are experiencing fatal errors the moment we try to make use of the self-referencing relation.

diagram-167397485293621894

The multiple level discrimination on itself works fine. We can query for the object directly or get it from a related entity. But the moment we insert data in the parent_id column in the database and try to call the ‘getParent()’ method we get the following fatal error:

PHP Fatal error: Cannot instantiate abstract class FinancialBundle\Entity\Invoice\Line\SocialSupport\AbstractProduct in ..vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php on line 872

An important detail is that we CAN use the self-referencing relation when querying for the ‘AddonMedicine’ entity. So it looks like that when we use the combination of the self-referencing relation AND the multiple level discrimination something is not interpreted right and that Doctrine is trying to instantiate the ‘middle layer’, being the AbstractProduct.

These are the mapping files in use:

AbstractLine

<?xml version="1.0" encoding="utf-8"?>
<doctrine-mapping
        xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping https://raw.githubusercontent.com/doctrine/doctrine2/2.4/doctrine-mapping.xsd">
    <entity name="FinancialBundle\Entity\Invoice\Line\AbstractLine"
            table="invoiceline"
            inheritance-type="JOINED">

        <discriminator-column name="type" type="string" />

        <discriminator-map>
            <discriminator-mapping value="socialsupportproduct_effort" class="FinancialBundle\Entity\Invoice\Line\SocialSupport\AbstractProduct" />
            <discriminator-mapping value="socialsupportproduct_output" class="FinancialBundle\Entity\Invoice\Line\SocialSupport\AbstractProduct" />
            <discriminator-mapping value="addonmedicine" class="FinancialBundle\Entity\Invoice\Line\AddonMedicine" />
        </discriminator-map>

        <id name="id" column="id" type="integer">
            <generator strategy="AUTO" />
        </id>

        <field name="quantity" column="quantity" type="float" nullable="false" />
        <field name="price" column="price" type="float" nullable="false" />
        <field name="subtotal" column="subtotal" type="integer" nullable="false" />
        <field name="parentId" column="parent_id" type="integer" nullable="true" />


        <many-to-one field="invoice" target-entity="FinancialBundle\Entity\Invoice\AbstractInvoice" inversed-by="invoiceLines">
            <join-column name="invoice_id" referenced-column-name="id" nullable="false" />
        </many-to-one>

        <many-to-one field="invoiceLineVat" target-entity="FinancialBundle\Entity\Invoice\Line\Vat">
            <cascade>
                <cascade-persist/>
            </cascade>
            <join-column name="invoicelinevat_id" referenced-column-name="id" nullable="true" />
        </many-to-one>

    </entity>
</doctrine-mapping>

AbstractProduct

<?xml version="1.0" encoding="utf-8"?>
<doctrine-mapping
        xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping https://raw.githubusercontent.com/doctrine/doctrine2/2.4/doctrine-mapping.xsd">
    <entity name="FinancialBundle\Entity\Invoice\Line\SocialSupport\AbstractProduct"
            table="invoicelinesocialsupportproduct"
            repository-class="FinancialBundle\Repository\Invoice\Line\SocialSupport\AbstractProductRepository"
            inheritance-type="JOINED">

        <discriminator-column name="type" type="string" />

        <discriminator-map>
            <discriminator-mapping value="socialsupportproduct_effort" class="FinancialBundle\Entity\Invoice\Line\SocialSupport\EffortOrientedProduct" />
            <discriminator-mapping value="socialsupportproduct_output" class="FinancialBundle\Entity\Invoice\Line\SocialSupport\OutputOrientedProduct" />
        </discriminator-map>

        <field name="startDate" column="startdate" type="date" nullable="false" />
        <field name="endDate" column="enddate" type="date" nullable="true" />

        <many-to-one field="product" target-entity="FinancialBundle\Entity\Invoice\Line\GenericSocialSupportProduct">
            <cascade>
                <cascade-persist/>
            </cascade>
            <join-column name="invoicelinegenericsocialsupportproduct_id" referenced-column-name="id" />
        </many-to-one>

        <many-to-one field="contract" target-entity="FinancialBundle\Entity\Contract\AbstractContract">
            <join-column name="fin_contract_id" referenced-column-name="id" nullable="false" />
        </many-to-one>

    </entity>
</doctrine-mapping>

OutputOrientedProduct

<?xml version="1.0" encoding="utf-8"?>
<doctrine-mapping
        xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping https://raw.githubusercontent.com/doctrine/doctrine2/2.4/doctrine-mapping.xsd">
    <entity name="FinancialBundle\Entity\Invoice\Line\SocialSupport\OutputOrientedProduct"
            table="invoicelinesocialsupportproductoutput"
            repository-class="FinancialBundle\Repository\Invoice\Line\SocialSupport\Product\OutputOrientedRepository">

        <many-to-one field="treatmentActivity" target-entity="Medical\SocialSupport\AbstractTreatmentActivity">
            <join-column name="behandeling_socialsupportactivity_id" referenced-column-name="id" />
        </many-to-one>

        <many-to-one field="allocatedProduct" target-entity="FinancialBundle\Entity\Invoice\Line\GenericDispositionProductAllocated">
            <cascade>
                <cascade-persist/>
            </cascade>
            <join-column name="invoicelineallocatedproduct_id" referenced-column-name="id" />
        </many-to-one>

    </entity>
</doctrine-mapping>

AddonMedicine

<?xml version="1.0" encoding="utf-8"?>
<doctrine-mapping
        xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping https://raw.githubusercontent.com/doctrine/doctrine2/2.4/doctrine-mapping.xsd">
    <entity name="FinancialBundle\Entity\Invoice\Line\AddonMedicine"
            table="invoicelineaddonmedicine"
            repository-class="FinancialBundle\Repository\Invoice\Line\AddonMedicineRepository">

        <one-to-one field="addon" target-entity="FinancialBundle\Entity\Invoice\Line\AddonMedicine\Addon">
            <cascade>
                <cascade-persist/>
            </cascade>
            <join-column name="invoicelineaddonmedicineaddon_id" referenced-column-name="id" nullable="false" />
        </one-to-one>

        <many-to-one field="contract" target-entity="FinancialBundle\Entity\Contract\AbstractContract">
            <cascade>
                <cascade-persist/>
            </cascade>
            <join-column name="fin_contract_id" referenced-column-name="id" nullable="false" />
        </many-to-one>

    </entity>
</doctrine-mapping>
@Ocramius
Copy link
Member

Ocramius commented Jun 7, 2017

PHP Fatal error: Cannot instantiate abstract class Medicore\Bundle\FinancialBundle\Entity\Invoice\Line\SocialSupport\AbstractProduct in ..vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php on line 872

What's the code causing that error? Looks like a wrong discriminator value being used? What's the trace for that exception? Can this be reproduced in a test case?

@jeroenvrooij
Copy link
Author

Hi Ocramius, thanks for your quick reply.

  1. The error can be triggered by simply calling $this->repository->find($id);. The repository being used the the repository of the AbstracProduct, but we've also tried using the repo from the abstractLine (top level) and the OutputOrientedProduct (the concrete leaf) and that yields the same results.
  2. I'm pretty sure it cannot be a wrong discriminator value. The way we tested this was to have a valid record in the database (so without the parent_id). This record can be queried like said in bullet '1'. Everything works fine, we get an instance of an 'OutputOrientedProduct 'entity. After manually inserting a parent_id (and changing nothing else) executing the same code results in the 500.
  3. The reason I did not include a trace is because the trace didn't have much more info. But I'll include it at the bottom.
  4. I don't really know what you mean with test case, but I can't make a unit test for our code/logic, since all we do is calling find() in the repo. I didn't dive in the Doctrine code as well so no, I can not repro this in a test case.

Trace:

PHP Fatal error:  Uncaught exception 'Symfony\Component\Debug\Exception\FatalErrorException' with message 'Error: Cannot instantiate abstract class FinancialBundle\Entity\Invoice\Line\SocialSupport\AbstractProduct' in vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php:872
Stack trace:
#0 {main}
  thrown in vendor/doctrine/orm/lib/Doctrine/ORM/Mapping/ClassMetadataInfo.php on line 872

@Ocramius
Copy link
Member

Ocramius commented Jun 8, 2017

The way we tested this was to have a valid record in the database (so without the parent_id). This record can be queried like said in bullet '1'. Everything works fine, we get an instance of an 'OutputOrientedProduct 'entity. After manually inserting a parent_id (and changing nothing else) executing the same code results in the 500.

Is there an automated test for this?

@Ocramius
Copy link
Member

Ocramius commented Jun 8, 2017

I don't really know what you mean with test case, but I can't make a unit test for our code/logic, since all we do is calling find() in the repo. I didn't dive in the Doctrine code as well so no, I can not repro this in a test case.

See https://github.com/doctrine/doctrine2/tree/971c40002522cfebe58d80aff21eef9fe439fa60/tests/Doctrine/Tests/ORM/Functional for examples

@jeroenvrooij
Copy link
Author

No, there is no automated test for this. For me to make one would take me some time, which I do not have at the moment. So the best I can do for now are the repro steps I provided. Will that be an issue?

@Ocramius
Copy link
Member

Ocramius commented Jun 8, 2017

Will that be an issue?

Well, until there's a test case nobody will really look at it :-\

@jeroenvrooij
Copy link
Author

After some more investigation it turned out it had nothing to do with the self-referencing relation. Because of this, and some new insights, I have made a new issue: #6558. Will hereby close this one.

@Ocramius Ocramius self-assigned this Jul 18, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants