Doctrine creates some undefined error - doctrine

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).

Related

Three level inheritance in doctrine2 not loading second level data

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.

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 = '';
}
}

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 2, how do get data from the inverse side (many to one)

I have two entities, entry and comments.
comments:
/**
* #Entity(repositoryClass="\Entities\Blog\CommentRepository")
* #Table(name="blog_comment")
* #HasLifecycleCallbacks
*/
class Comment extends \Entities\AbstractEntity
{
/**
* #Id #Column(name="id", type="integer")
* #GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ManyToOne(targetEntity="Entry", inversedBy="comments")
* #JoinColumn(name="entry_id", referencedColumnName="id")
*/
protected $entry;
/** #Column(name="approved", type="string", length=255) */
protected $approved;
/** #Column(name="title", type="string", length=255) */
protected $title;
/** #Column(name="content", type="text") */
protected $content;
/** #Column(name="pub_date", type="datetime") */
protected $pub_date;
/** #Column(type="datetime") */
private $created_at;
/** #Column(type="datetime") */
private $updated_at;
/** #PreUpdate */
public function updated()
{
$this->updated_at = new \DateTime("now");
}
public function __construct()
{
$this->created_at = $this->updated_at = new \DateTime("now");
}
}
class CommentRepository extends \Entities\PaginatedRepository
{
protected $_entityClassName = 'Entities\Blog\Comment';
}
and entry:
<?php
namespace Entities\Blog;
/**
* #Entity(repositoryClass="\Entities\Blog\EntryRepository")
* #Table(name="blog_entry")
* #HasLifecycleCallbacks
*/
class Entry extends \Entities\AbstractEntity
{
/**
* #Id #Column(name="id", type="integer")
* #GeneratedValue(strategy="AUTO")
*/
protected $id;
/** #Column(name="permalink", type="string", length=255) */
protected $permalink;
/** #Column(name="title", type="string", length=255) */
protected $title;
/** #Column(name="pub_date", type="datetime") */
protected $pub_date;
/** #Column(name="content", type="text") */
protected $content;
/** #OneToMany(targetEntity="Comment", mappedBy="entry") */
protected $comments;
/** #Column(type="datetime") */
private $created_at;
/** #Column(type="datetime") */
private $updated_at;
/** #PreUpdate */
public function updated()
{
$this->updated_at = new \DateTime("now");
}
public function __construct()
{
$this->comments = new \Doctrine\Common\Collections\ArrayCollection();
}
I can get the collection of all comments belonging to each entry via:
foreach ($comments as $comment){
$comment-$commentId;
}
but how can I get the entry information from the comments side. for example, I would like to get the entry id from a specific comment
Each time you create a #OneToMany relation, you create a Collection of proxy objects in class on "One"-side of relation, and single proxy object in class on "Many"-side of relation. Proxy classes are automatically generated by Doctrine2 from your mapping information.
To allow Doctrine2 filling proxy object with real data from DB it's important to declare it protected or private. I'm not sure about that, but seems like Doctrine tracks down any requests to proxy objects inside your entity class and ensures that proxies are populated before first usage.
To access the associated object you have to define accessor function in your Comment class:
class Comment extends \Entities\AbstractEntity{
/** other definitions */
function getEntity(){
return $this->entity;
}
}
And use it like
$comment = $em->find("Entities\Comment",1);
$entity = $comment->getEntity();
Doctrine2 will automatically populate $comment->entity proxy with actual Entity object.
See "Workin with Objects" chapter of Doctrine documentation and "Can you explain me what is a Proxy in Doctrine 2?" on details of proxies.

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