Three level inheritance in doctrine2 not loading second level data - laravel

I have a inheritance of three levels using class table inheritance like this:
Class Test
namespace App\Entities\Test;
use Doctrine\ORM\Mapping as ORM;
/**
* Class Test
* #package App\Entities\Test
* #ORM\Entity
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="discr", type="string")
* #ORM\DiscriminatorMap({"TestA" = "TestA", "TestB" = "TestB"})
* #ORM\Table(name="test")
*/
abstract class Test {
/**
* #var integer
* #ORM\Column(type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
protected $id;
/**
* #var string
* #ORM\Column(type="string")
*/
protected $columnTest;
}
Class TestM extends Test
namespace App\Entities\Test;
use Doctrine\ORM\Mapping as ORM;
/**
* Class TestM
* #package App\Entities\Test
* #ORM\Entity
*/
abstract class TestM extends Test{
/**
* #var string
* #ORM\Column(type="string")
*/
protected $columnTestM;
}
Class TestA extends TestM
namespace App\Entities\Test;
use Doctrine\ORM\Mapping as ORM;
/**
* Class TestA
* #package App\Entities\Test
* #ORM\Entity
*/
class TestA extends TestM{
/**
* #var string
* #ORM\Column(type="string")
*/
private $columnTestA;
public function __construct(string $columnTest, string $columnTestM, string $columnTestA) {
$this->columnTest = $columnTest;
$this->columnTestM = $columnTestM;
$this->columnTestA = $columnTestA;
}
/**
* #return string
*/
public function getColumnTest(): string {
return $this->columnTest;
}
/**
* #param string $columnTest
*/
public function setColumnTest(string $columnTest): void {
$this->columnTest = $columnTest;
}
/**
* #return string
*/
public function getColumnTestM(): string {
return $this->columnTestM;
}
/**
* #param string $columnTestM
*/
public function setColumnTestM(string $columnTestM): void {
$this->columnTestM = $columnTestM;
}
/**
* #return string
*/
public function getColumnTestA(): string {
return $this->columnTestA;
}
/**
* #param string $columnTestA
*/
public function setColumnTestA(string $columnTestA): void {
$this->columnTestA = $columnTestA;
}
}
I'm having problem because, when I'm going to retrieve the entity from my DB, it comes with the second level with no data, only the first and last levels comes with all the data. Notice that columnTestM is blank. What am I missing? It's persisting with all three levels with data, the problem is only when I have to get it. As an example I put the column's content with it's own name
>>> print_r(\EntityManager::getRepository('App\Entities\Test\Test')->find(1));
App\Entities\Test\TestA Object
(
[columnTestA:App\Entities\Test\TestA:private] => columnTestA
[columnTestM:protected] =>
[id:protected] => 1
[columnTest:protected] => columnTest
)
EDIT:
Maybe it's a bug and I filed an issue at GitHub about a wrong query generation. I put my MariaDB to log all queries to check the query being generated when I try to retrieve the data back and that's the result:
SELECT
t0.id AS id_3,
t0.column_test AS column_test_4,
t0.discr,
t1.column_test_a AS column_test_a_5,
t2.column_test_b AS column_test_b_6
FROM
test t0
LEFT JOIN
test_as t1 ON t0.id = t1.id
LEFT JOIN
test_bs t2 ON t0.id = t2.id
WHERE
t0.id = 1
It tries to left join with TestB instead of doing the join with TestM

As stated by #LBA the problem is two abstract classes in the chain. It should work even with two abstract classes, but only the first is retrieved, but all the others in the middle stays blank. Issue already reported in doctrine's github project.

Related

Doctrine generate Models. Metadata Configuration

I generated Entities with Doctrine's generating method inside Doctrine.php from CodeIgniter Controller:
function generate_classes(){
$this->em->getConfiguration()
->setMetadataDriverImpl(
new DatabaseDriver(
$this->em->getConnection()->getSchemaManager()
)
);
$cmf = new DisconnectedClassMetadataFactory();
$cmf->setEntityManager($this->em);
$metadata = $cmf->getAllMetadata();
$generator = new EntityGenerator();
$generator->setUpdateEntityIfExists(true);
$generator->setGenerateStubMethods(true);
$generator->setGenerateAnnotations(true);
$generator->generate($metadata, APPPATH."models/Entities");
}
The generated Entity had this syntax:
use Doctrine\ORM\Mapping as ORM;
/**
* Users
*
* #ORM\Table(name="users")
* #ORM\Entityz
*/
class Users
{
/**
* #var integer
*
* #ORM\Column(name="idusers", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $idusers;
/**
* #var string
*
* #ORM\Column(name="first_name", type="string", length=45, nullable=true)
*/
private $firstName;
/**
* #var string
*
* #ORM\Column(name="last_name", type="string", length=45, nullable=true)
*/
Now calling these Entities form my model didn't work. The model didn't find the entity. After manually changing the syntax to the following code example, I got it to work:
use Doctrine\ORM\Mapping as ORM;
/**
* Users
*
* #Entity #Table(name="users")
*/
class Users
{
/**
* #var integer
*
* #Column(name="idusers", type="integer", nullable=false)
* #Id
*
*/
private $idusers;
/**
*
* #GeneratedValue
* #Column(name="first_name", type="string", length=45, nullable=true)
*/
private $firstName;
And now everything works!
I am not going to manually change every Entity.
How can I configure Doctrine to produce Entity's with the correct syntax. As used in their tutorials as well? http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/getting-started.html
Noob question. Please help!
Found the solution:
changed in application/vendor/doctrine/orm/lib/Doctrine/ORM/Tools/EntityGenerator.php the constructor from:
public function __construct()
{
if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) {
$this->annotationsPrefix = 'ORM\\';
}
}
To:
public function __construct()
{
if (version_compare(\Doctrine\Common\Version::VERSION, '2.2.0-DEV', '>=')) {
$this->annotationsPrefix = '';
}
}

Sonata admin configureFormField

i use sonata admin and i have a 'Fonctionnare' entity. i changed the 'codeFonctionnaire' type of this entity to string but when i create the Fonctionnaire admin class and try to add new fonctionaire i got this error message:
Neither the property "codeFonctionnaire" nor one of the methods "setCodeFonctionnaire()", "_set()" or "_call()" exist and have public access in class "Examens\ExamensBundle\Entity\Fonctionnaire".
Fonctionnaire.php:
<?php
namespace Examens\ExamensBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* Fonctionnaire
*/
class Fonctionnaire
{
/**
* #var string
*/
private $codeFonctionnaire;
//////
/**
* Get codeFonctionnaire
*
* #return string
*/
public function getCodeFonctionnaire()
{
return $this->codeFonctionnaire;
}
////////
FonctionnaireAdmin.php:
<?php
namespace Examens\ExamensBundle\Admin;
use Sonata\AdminBundle\Admin\Admin;
use Sonata\AdminBundle\Datagrid\ListMapper;
use Sonata\AdminBundle\Datagrid\DatagridMapper;
use Sonata\AdminBundle\Validator\ErrorElement;
use Sonata\AdminBundle\Form\FormMapper;
use Sonata\AdminBundle\Show\ShowMapper;
use Examens\ExamensBundle\Entity\Fonctionnaire;
class FonctionnaireAdmin extends Admin
{
protected $datagridValues = array(
'_sort_order' => 'ASC',
'_sort_by' => 'codeFonctionnaire'
);
protected function configureFormFields(FormMapper $formMapper)
{
$formMapper
->add('codeFonctionnaire','text',array('label'=>'Code fonctionnaire'))
//////
}
what's wrong with the entity?
You need to regenerate the getters and setters of your Fonctionnaire class. Your IDE can do it for you.
Or at least, just add a
public function setCodeFonctionnaire($codeFonctionnaire) {
$this->codeFonctionnaire = $codeFonctionnaire;
}
EDIT
Here is what might be your complete Fonctionnaire class :
namespace TechVehi\BlogBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
class Fonctionnaire {
/**
* #var string
*
* #ORM\Column(name="codeFonctionnaire", type="string", length=255, nullable=true)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $codeFonctionnaire;
/**
* #param string $codeFonctionnaire
*/
public function setCodeFonctionnaire($codeFonctionnaire) {
$this->codeFonctionnaire = $codeFonctionnaire;
}
/**
* #return string
*/
public function getCodeFonctionnaire() {
return $this->codeFonctionnaire;
}
}
You might have forgotten other informations, like the ORM annotation to link your object to your database.
i deleted this code from fonctionnaire.orm.yml:
generator:
strategy: IDENTITY
and it works.

Symfony2/Doctrine2 One-To-Many same object twice

My current Client-Entity has an unloading and a loading Area, which are both ClientArea-Entities.
namespace ACME\DemoBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Sorien\DataGridBundle\Grid\Mapping as GRID;
use Symfony\Component\Validator\Constraints as Assert;
use Doctrine\Common\Collections\ArrayCollection;
/**
* ACME\DemoBundle\Entity\Client
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="ACME\DemoBundle\Entity\ClientRepository")
*/
class Client
{
/**
* #ORM\OneToMany(targetEntity="ClientArea",mappedBy="client", cascade={"persist", "remove"})
*/
public $unloading_areas;
/**
* #ORM\OneToMany(targetEntity="ClientArea",mappedBy="client", cascade={"persist", "remove"})
*/
public $loading_areas;
}
class ClientArea
{
/**
* #ORM\ManyToOne(targetEntity="Client")
*/
public $client;
}
This does not work because client can only map 1 association.
How can i map the relation properly?
To create entity relations you need to have keys to use when joining tables. Your Client class should have an id key defined and you need to initialize collections, like this:
class Client
{
//....
/**
* #var integer $id
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #ORM\OneToMany(targetEntity="ClientArea", mappedBy="client", cascade={"persist", "remove"})
*/
public $unloading_areas;
/**
* #ORM\OneToMany(targetEntity="ClientArea", mappedBy="client", cascade={"persist", "remove"})
*/
public $loading_areas;
public function __construct() {
// Initialize collections
$this->unloading_areas = new \Doctrine\Common\Collections\ArrayCollection();
$this->loading_areas = new \Doctrine\Common\Collections\ArrayCollection();
}
// ....
}
Your ClientArea class should then look something like this:
class ClientArea
{
// ....
/**
* #ORM\Column(name="client_id", type="int", nullable=false)
*/
private $clientId;
/**
* #ORM\ManyToOne(targetEntity="Client")
* #JoinColumn(name="client_id", referencedColumnName="id")
*/
public $client;
// ....
}
Now, those two entities should be mapped correctly.
To learn more about association mappings in Doctrine, read article here: http://docs.doctrine-project.org/projects/doctrine-orm/en/2.0.x/reference/association-mapping.html
Hope this helps.

Doctrine creates some undefined error

For some special entities (I cannot tell which ones) I get the following error when I try to do count($Club->getUmbrellas()), where $Club->getUmbrellas() is a Doctrine\ORM\PersistentCollection.
Notice: Undefined index: id in /var/www/apache/StudCon/library/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php on line 401 Fatal error: Call to a member function getValue() on a non-object in /var/www/apache/StudCon/library/Doctrine/ORM/Internal/Hydration/ObjectHydrator.php on line 401
Club is main.
ClubClub extends Club
ClubUmbrella extends Club
ClubUmbrella and ClubClub are indirectly related over RelationClubUmbrella
These are the most important bits of information (please ask if u need more):
class Club extends \StudCon_Entity
{
/**
* #Column(type="string", length="150")
* #var string
*/
protected $type;
/**
* #Id #GeneratedValue
* #Column(type="bigint")
* #var integer
*/
protected $id;
class ClubClub extends Club
{
/**
* #OneToMany(targetEntity="RelationClubUmbrella", mappedBy="club", indexBy="id")
*/
protected $umbrellas;
class ClubUmbrella extends Club
{
/**
* #OneToMany(targetEntity="RelationClubUmbrella", mappedBy="umbrella")
*/
protected $clubs;
class RelationClubUmbrella
{
const STATUS_REQUESTED = 1;
const STATUS_CONFIRMED = 2;
const STATUS_REJECTED = 3;
/**
* #Column(type="integer")
* #var integer
*/
protected $status;
/**
* #Id
* #ManyToOne(targetEntity="Club")
* #JoinColumn(name="umbrellaid", referencedColumnName="id")
* #var Category
*/
protected $umbrella;
/**
* #Id
* #ManyToOne(targetEntity="Club")
* #JoinColumn(name="clubid", referencedColumnName="id")
* #var Category
*/
protected $club;
Looks like you're using an outdated version of Doctrine. Similar issues have been fixed in the meantime, you should try the latest version (released today).

Polymorphic associations in doctrine 2?

I need a concrete sample of code with doctrine 2 that uses "polymorphic associations".
Let me clarify myself. I have a Entity called Contract and a contract can have many price rules and these price rules can be different kind of classes and presisted in different tables. I suppose this is what's polymorphic associations for or am I wrong?
class contract {
private $id;
private $priceRules;
}
class discountRule implements priceRule{
function calculate() {
// calculate new price after this rule
}
}
class extraSpecialRule implements priceRule {
function calculate() {
// calculate new price after this rule
}
}
There can be new types of price rules in the future,so how can I associate these rules to the main entity and presist them in seperate tables?
Update:
This is my new code:
contract.php
namespace Entities;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #Entity #Table(name="contract")
*/
class Contract {
/**
*
* #Id #Column(type="integer")
* #GeneratedValue(strategy="AUTO")
*/
private $id;
/**
*
* #Column(type="integer")
*/
private $propertyId;
/**
*
* #Column(type="integer")
*/
private $agencyId;
/**
*
* #OneToMany(targetEntity="priceRule" ,mappedBy="contract")
*
*/
private $priceRules;
public function __construct($propertyId,$agencyId){
$this->propertyId=$propertyId;
$this->agencyId=$agencyId;
$this->priceRules=new ArrayCollection();
}
public function addPriceRule(priceRule $rule){
$this->priceRules[]=$rule;
}
public function getPriceRules(){
return $this->priceRules;
}
}
pricerule.php
namespace Entities;
/**
* #Entity
* #InheritanceType("JOINED")
* #DiscriminatorColumn(name="discr" , type="string")
* #DiscriminatorMap({"discountrule"="discountRule","extradiscountrule"="extraDiscountRule"})
*/
class priceRule{
/**
*
* #Id #Column(type="integer")
* #GeneratedValue(strategy="AUTO")
*/
private $id;
/**
*
* #ManyToOne(targetEntity="contract",inversedBy="availibilityRules")
* #JoinColumn("contract_id",referencedColumnName="id")
*/
private $contract;
}
discountrule.php
namespace Entities;
/**
* #Entity
*
*
*/
class discountRule extends priceRule {
/**
*
* #Id #Column(type="integer")
* #GeneratedValue(strategy="AUTO")
*/
private $id;
public function calculatePrice(){
// calculate new price
}
}
extradiscountrule.php
namespace Entities;
/**
* #Entity
*
*
*/
class extraDiscountRule extends priceRule {
/**
*
* #Id #Column(type="integer")
* #GeneratedValue(strategy="AUTO")
*/
private $id;
public function calculate() {
// calculate new price
}
}
sampleusage.php
$contract=new Contract(1,1);
$discount=new discountRule();
$em->persist($discount);
$contract->addPriceRule($discount);
$em->persist($contract->getPriceRules());
$em->persist($contract);
$em->flush();
But when I try to add new rule to the contract I get error message (Fatal error: Uncaught exception 'Doctrine\ORM\Mapping\MappingException' with message 'Class Doctrine\Common\Collections\ArrayCollection is not a valid entity or mapped super class.)
What am I doing wrong ?
You may be missing an #MappedSuperclass on your PriceRule parent object
Refer to: Inheritance Mapping
I don't this is possible because an interface cannot define properties in a class, so you can't guarantee there will be properties for Doctrine to manipulate.
If you could provide more details on your entities I could help better.

Resources