Wrong value coming from Doctrine 2 'getClassMetadata()->getIdentifier()', how or why? - php

I have the following Entity as part of a Symfony 3.2.6 project in Doctrine 2:
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Timestampable\Traits\TimestampableEntity;
use QuoteBundle\Model\SourceTrait;
/**
* #ORM\Entity
* #ORM\Table(name="quote")
*/
class Quote
{
use SourceTrait;
use TimestampableEntity;
/**
* #ORM\Id
* #ORM\Column(type="integer",unique=true,nullable=false)
* #ORM\GeneratedValue
*/
private $quoteId;
/**
* #return int
*/
public function getQuoteId(): ?int
{
return $this->quoteId;
}
}
In another class dynamically I need to get the identifier of the entities (I wrote just an example of the entities - the one above - but this applies to all of the entities I have all over the application) so the best way I found to achieve this was using getClassMetaData() method from Doctrine.
This is the dynamic code into the class:
$modelMetaData = $em->getClassMetadata($bundleName.':'.$modelName);
Which gets translated for example in:
$modelMetaData = $em->getClassMetadata('QuoteBundle:Quote');
From there I should have all the data related to the entity.
One of the problem is we're two developers and we're having different results regarding getClassMetaData().
I am running a stack in Docker using Docker Compose which is exactly the same and this mean: same PHP version (7.1.2), same Apache version, same MySQL version (this one is in a common server outside the stack) and same Symfony libraries. I have checked, double checked and triple checked and can do it again if it's necessary.
For the examples from now on I'll call myself Dev1 and the second developer will be Dev2.
Dev1 code:
$id = $em->getClassMetadata('QuoteBundle:Quote')->getIdentifier()[0];
dump($id);
quote_id
Dev2 code:
$id = $em->getClassMetadata('QuoteBundle:Quote')->getIdentifier()[0];
dump($id);
quoteId
As you can see the ID obtained is different which is making the code to fails on Dev2 environment and I don't know what else to look since everything seems to be fine for me.
The question is: should getIdentifier() return quote_id (the column name) or quoteId (the mapped name)??
As an addition here is an example of dump($modelMetaData) for Dev1 and here the same example for Dev2.

Related

Doctrine launches INSERT always instead of UPDATE for existing entities

Let's say I have the following entities:
App\Entity\MainEntity:
/**
* #var object
*
* #ORM\OneToOne(targetEntity="App\Entity\DependentEntity", fetch="EAGER")
* #ORM\JoinColumn(name="DependentEntityType1FK", referencedColumnName="DependentEntityIDPK")
*/
private $dependentEntityType1;
/**
* #var object
*
* #ORM\OneToOne(targetEntity="App\Entity\DependentEntity", fetch="EAGER")
* #ORM\JoinColumn(name="DependentEntityType2FK", referencedColumnName="DependentEntityIDPK")
*/
private $dependentEntityType2;
Basically, one-directional 1:1 relationship from main entity to the same dependent entity using two different columns in the main entity table.
It doesn't matter, whether I use fetch="EAGER" or normal lazy loading through Doctrine proxy classes, when I do something like this:
$mainEntity = $this->mainEntityRepository->find(74);
$mainEntity->setDependentEntityType1($this->dependentEntityRepository->find(35));
$this->mainEntityRepository->saveTest($mainEntity);
where ::saveTest() is:
public function saveTest(MainEntity $mainEntity) {
$this->_em->persist($mainEntity->getDependentEntityType1());
$this->_em->merge($mainEntity);
$this->_em->flush();
}
it always tries to INSERT a new dependent entity to the table, even though I never made any changes (and even if I made them, it should have been UPDATE! for it)
The question is: why does Doctrine decide this dependent entity is a new one if I did $this->dependentEntityRepository->find(35) , so loaded an existing one?
I tried fetch="EAGER" thinking that spl_object_hash might return different hashes for a Proxy class instance and the actual DependantEntity one, but it doesn't matter, the DependantEntity is for some reason always considered as "new".
UPDATE: here is the code of ::setDependentEntityType1()
public function setDependentEntityType1(DependentEntity $dependentEntity) : void {
$this->dependentEntity = $dependentEntity;
}

How do I search by properties of entity which do not have #Column anotation in Doctrine2?

/**
* #ORM\Entity
*/
class Order extends BaseEntity
{
// this is trait for #Id
use Identifier;
/**
* #ORM\Column(type="integer")
*/
protected $costPerUnit;
/**
* #ORM\Column(type="integer")
*/
protected $numberOfUnits;
// i want to search by this property
protected $totalCost;
public function getTotalCost()
{
return $this->numberOfUnits * $this->costPerUnit;
}
}
I have an entity like this and I'd like to be able to do for example
$orderRepository->findOneByTotalCost('999')
$orderRepository->findBy(['totalCost' => '400']);
Is this possible in Doctrine2? Or would I go about it differently?
Like I said in my comments, it's likely you're wrestling with an issue that shouldn't have occurred in the first place. Still, having a SUM value mapped to a property is possible using doctrine, in a variety of ways: Check the aggregate field docs to find out which would solve your problem best.
To my eyes, is that you're using entities as more than what they really are: Entities represent records in a database, Doctrine is a DBAL. Searching data using entities (or repositories) is querying the database. You could solve the problem by adding custom methods to your entity manager or a custom repository class that'll query all of the data required to compute the totalCost value for all entities, and return only those you need. Alternatively, use the connection from your DBAL to query for the id's you're after (for example), then use those values to get to the actual entities. Or, like I said before: use aggregate fields.
The problems you have with the findOneByTotalCost and findBy examples you show is that the first requires you to write a method Called findOneByTotalCost yourself. The problem with your use of findBy is simply that your argument is malformed: the array should be associative: use the mapped column names as keys, and the values are what you want to query for:
$repo->findBy(
['totalCost' => 400]
);
is what you're looking for, not ['totalCost', 400]. As for the entity itself, you'll need to add an annotation:
Yes it is, judging by your use of #ORM\Entity annotations in the doc-blocks, this ought to do it:
/**
* #ORM\Column(type="string", length=255)
*/
protected $regioun = 'Spain';
The update the table, and you'll be able to:
$entities = $repo->findBy(
['region' => 'Spain']
);
Don't forget that this code represents a table in a DB: you can search on any of the fields, but use indexes, which you can do by adding annotations at the top of your class definition:
/**
* #ORM\Table(name="tblname", indexes={
* #ORM\Index(name="region", columns={"region"})
* })
*/
class Foo
{}
As ever: in DB's, indexes matter
You should write a method findOneByTotalCost on your entity repository, something like:
public function findOneByTotalCost ($queryParams){
$query = 'select o
from <yourEntity> o
where o.numberOfUnits * o.costPerUnit = :myParam';
$dql = $this->getEntityManager()->createQuery($query);
$dql->setParameter('myParam', $queryParams);
return $dql ->execute();
}
Them, $orderRepository->findOneByTotalCost('999') should work.

Symfony/Doctrine class table inheritance and foreign key as primary key

I am currently designing a web application with Symfony 2.5 (and Doctrine 2.4.2) that has to be flexible to easily plug in new modules/bundles.
So I have an entity (let say A) that has two one-to-one associations with abstract classes (B and C). The future modules will implement one of the two abstract classes. Since the associations are one-to-one, I made them the ID of the abstract classes to ease the access to them when we know the ID of an instance of A
So here is what the code looks like:
class A:
<?php
namespace Me\TestBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table()
* #ORM\Entity
*/
class A
{
/**
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\OneToOne(targetEntity="B", cascade={"all"}, mappedBy="a")
* #ORM\JoinColumn(name="id", referencedColumnName="a_id")
*/
private $b;
/**
* #ORM\OneToOne(targetEntity="C", cascade={"all"}, mappedBy="a")
* #ORM\JoinColumn(name="id", referencedColumnName="a_id")
*/
private $c;
}
class B:
<?php
namespace Me\TestBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Table()
* #ORM\Entity
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="discr", type="string")
*/
abstract class B
{
/**
* #ORM\Id
* #ORM\OneToOne(targetEntity="A", inversedBy="b")
* #ORM\JoinColumn(name="a_id", referencedColumnName="id")
*/
private $a;
}
I will not post the code of the class C since it is the same as class B.
In my point of view, it seems all good. Even for the mapping verification command.
Indeed, when I execute php app/console doctrine:schema:validate, it tells me my schema is valid. However, this command then try to compare my schema to the schema in the database and it just fails at that point. php app/console doctrine:schema:update --dump-sql fails in the exact same way. So it is pretty embarrassing since it tells me my schema is valid but it cannot use it properly.
The error:
[ErrorException]
Warning: array_keys() expects parameter 1 to be array, null given in /vendor/doctrine/dbal/lib/Doctrine/DBAL/Schema/Index.php line 95
The error appears as soon as I add the InheritanceType and DiscriminatorColumn annotation to the classes B and C. Thing is that it tells me my schema is valid.
So does anyone have any clue if I am doing something wrong? Or is it definitely a bug in Doctrine? Do you have any other idea that would bring at least as much flexibility as my current solution?
Elioty
EDIT: I changed the owning side to be the abstract classes B and C since, accordingly to the doc, the owning side is the one with the foreign key and must use inversedBy attribute. Even with these changes, my schema is still valid and the same error still occurs.
EDIT2: If I create another field in B (and C) to hold the identity of the entity instead of the one-to-one association, the error disappears but it is no more like my valid schema.
EDIT3: I had a chat with a member of Doctrine's development team and (s)he told me it definitely looks like a bug. Bug report here.
First of all, the command php app/console doctrine:schema:validate checks the validity of the current schema. The current schema is the one generated by your last called to the command php app/console doctrine:schema:update --force
On the other hand the command php app/console doctrine:schema:update --dump-sql does not execute an actual update to the schema, though it may find some errors some others will not be come up...
I dont understand class B. The identity of this (abstract) class is a OneToOne relationship? Anyway, the thing is that Doctine will ignore the #ORD\Id if you don't add inheritanceType definitions. When you add this inheritanceType definition Doctrine will make attribute 'a' the primary key of B. Also it will create another attribute named 'a_id' which will serve as a foreign key for future subclasses (extends of B). But...relationships are not inherited.
Hope this helps...

I want to learn about PHP Anotations

What is PHP Anotations and where can I learn more about it? I need simple example to understand it. I have googled around alot. Somehow i reached this point:
Example Code:
<?php
class AddressShipment{
/* Attributes of AddressShipment */
/**
* private TypePlaceShipment type
* #ORM\Id #ORM\Column #ORM\GeneratedValue
* #dummy
* #var long
*/
private $type;
/**
* private boolean lift
* #ORM\Column(type="boolean")
* #Assert\NotEmpty
* #var boolean
*/
private $lift;
/**
* private String comment
* #ORM\Column(type="string")
* #Assert\NotEmpty
* #var string
*/
private $comment;
?>
Can someone explain this a bit ? What are PHP Anotations? How can we use it ? What is the purpose of using it ?
It is basically for documentation generation.
Also IDEs which support php look up the annotations and give you information on what the function does, what it returns, what parameter it takes.
So if you make a php library, another person can read the annotations easily to know what it does without having to dig through your code and his IDE will recognize those annotations
here is an example of netbeans using this annotation from a function somewhere
You can learn by visiting this link:
http://manual.phpdoc.org/HTMLSmartyConverter/PHP/phpDocumentor/tutorial_elements.pkg.html
Aside from providing documentation (e.g. http://www.phpdoc.org/ ) and providing hints for the IDE, some libraries use annotations to determine how to handle classes. One example is Doctrine which provides an Object Relational Mapper that can map objects to a database based on annotations (using its database abstraction layer).
In the given code fragment I recognize #ORM\Id, #ORM\Column and #ORM\GeneratedValue as some of the annotations that Doctrine uses. However, AddressShipment is not declared as an Entity with the appropriate annotation, so they're not correctly used for that purpose.
Zend Framework 2 utilizes Doctrine's annotation library to enable the creation of forms from annotations (including validations and filtering).
Under the hood Doctrine uses reflection to read these annotations. If you use annotation-dependent libraries in combination with certain PHP accelerators, then you need to take care to configure the accelerator not to discard the annotations when it caches the code.
(Take note before reading that in fact I know you are talking about how to use some php ORM framework documentation tags and where to find what they mean).
Annotations are just annotations in PHP world they are called "PHPDoc Tags" but unfortunately they are used for nothing more than some PHP IDE code autocompletion / intellisense / documentation generation.
PHPDoc by itself is a standard that raised from nowhere but opensource php documentation project many years ago and was positively received from php community and now even php reflection class supports them.
For example phpdoc tag #return specifies what will be php class method return type or php function return type.
#var specifies what is property type. PHPDoc has its own declaration rules. For example documentation must begin with /** and must and with */. You can find this standards on http://en.wikipedia.org/wiki/PHPDoc#Tags link.
In example below all tags with # symbol are legal and might be treated by some PHP IDE like Netbeans or Eclipse or even Sublime sometimes. But:
class planet
{
/**
* primary
* length 16
* #var string
*/
public $name;
/**
* type smallint
* #var integer
*/
public $order;
/**
* #var boolean
*/
public $water;
/**
* #var \galaxy\star
*/
public $star;
/**
* enum
* #var \galaxy\moon
*/
public $moons = array ();
But what is interesting in your question is that you also mentioned #ORM. This means you are using some ORM framework probably Doctrine with Symphony and you need to find what does these annotations mean for specific framework. This can be found easily in specific framework's documentation. They are so many that there is no clue to post them on here.
For example if you are planning to use Doctrine you can visit here http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/getting-started.html
Or if you are using single file framework db.php which is database first style also and uses annotations as well you can visit here http://dbphp.net.

Doctrine 2 - Access level problems when using Class Table Inheritance

I'm trying to implent the Class Table Inheritance Doctrine 2 offers in my Symfony 2 project.
Let's say a have a Pizza class, Burito class and a MacAndCheese class which all inherit from a Food class.
The Food class has the following settings:
<?php
namespace Kitchen;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="food")
* #ORM\InheritanceType("JOINED")
* #ORM\DiscriminatorColumn(name="dish", type="string")
* #ORM\DiscriminatorMap({"pizza" = "Pizza", "burito" = "Burito", "mac" => "MacAndCheese"})
*/
class Food {
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
And the inherited classes have these settings (Pizza for example):
<?php
namespace Kitchen;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="food_pizza")
*/
class Pizza extends Food {
When running doctrine:schema:update --force from the Symfony 2 app/console I get an error about the access level of $id in the children of Food (Pizza for example), stating it must be protected or weaker. I haven't declared $id anywhere in the Pizza, since I reckoned it would be inherited from Food.
So I tried to declare $id, but that gives me an error, cause I can't redeclare $id.
I figure I need some kind of reference to $id from Food in Pizza, but the Doctrine 2 documentation didn't really give me a clear answer on what this would look like.
Hopefully you understand what I mean and can help me.
Apparently I should have investigated the code generated by doctrine:generate:entities a bit more. When I started my IDE this morning and seeing the code again, I noticed that it had 'copied' all of the inherited fields (like $id in Food, in the example above) to the children (Pizza, in the example above).
For some reason it decided to make these fields private. I manually changed the access level to protected in all of the classes and I tried to run doctrine:schema:update --force again: it worked!
So, as in many cases, the solution was a good night's rest! ;)
If someone comes up with a better solution and / or explanation for this problem, please do post it. I'd be more than happy to change the accepted answer.
Something to keep in mind:
Every Entity must have an identifier/primary key. You cannot generate
entities in an inheritance hierachy currently (beta) As a workaround
while generating methods for new entities, I moved away from project
inheritated entities and after generating I moved them back.
source
May be you should define the #ORM\DiscriminatorMap in a such way:
/**
*
..
* #ORM\DiscriminatorMap({"food" = "Food", "pizza" = "Pizza", "burito" = "Burito", "mac" => "MacAndCheese"})
*/
If you compare your code with the example from Doctrine site, you will see that they added parent entity to the DiscriminatorMap.

Categories