📜 ⬆️ ⬇️

Duplication of an object when trying to get data from a table with the DateTime key

The other day, I encountered a strange error in Doctrine2 (version 2.2.2).

The essence of the problem


When trying to get data (in the form of an array of objects (Entity)) from a table with a key of type DateTime, Doctrine returned an array consisting of a single object (the first row) and references to it.


Error description


There is a table, let's call it PublisherDailyStatistic, which has two keys (more precisely, the key is one, but composite):
1) PublisherID integer
2) StatisticDT DateTime
Accordingly, statistics on publishers are recorded in this table once a day.
Suppose there is already data:
PublisherIDStatisticDTSomecontent
one2012-04-15 00:00:00test 15
one2012-04-16 00:00:00test 16
one2012-04-17 00:00:00test 17

')
Further, in the repository (~ PublisherDailyStatisticRepository.php) there is a function that pulls the statistics on the publisher from the database for a certain period of time.
It looks like this:
public function getByPublisherAndDate($publisher_id, \ DateTime $startDt, \ DateTime $endDt)
{
$qb = $ this ->_em->createQueryBuilder()
-> select ( 'p' )
-> from ( '\Model\Entity\PublisherDailyStatistic, ' p ')
->where('
p.publisherid=:publisherid ')
->andWhere('
p.statisticdt>=:startdt ')
->andWhere('
p.statisticdt<=:enddt')
->setParameters(array( "publisherid" =>( int )$publisher_id,
"startdt" =>$startDt,
"enddt" =>$endDt));

return $qb->getQuery()->getResult();
}


* This source code was highlighted with Source Code Highlighter .



It seems everything is simple and clear. But here's what we get when we call getByPublisherAndDate (1, 2012-04-15, 2012-04-17):

array (size=3)
0 =>
object (Model\Entity\PublisherDailyStatistic)[398]
private 'publisherid' => int 1
private 'statisticdt' =>
object ( DateTime )[381]
public 'date' => string '2012-04-15 00:00:00' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'Europe/Moscow' (length=13)
private 'somecontent' => string 'test 15' (length=7)
1=>
object (Model\Entity\PublisherDailyStatistic)[398]
private 'publisherid' => int 1
private 'statisticdt' =>
object ( DateTime )[381]
public 'date' => string '2012-04-15 00:00:00' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'Europe/Moscow' (length=13)
private 'somecontent' => string 'test 15' (length=7)
2 =>
object (Model\Entity\PublisherDailyStatistic)[398]
private 'publisherid' => int 1
private 'statisticdt' =>
object ( DateTime )[381]
public 'date' => string '2012-04-15 00:00:00' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'Europe/Moscow' (length=13)
private 'somecontent' => string 'test 15' (length=7)


* This source code was highlighted with Source Code Highlighter .


Suddenly, right?
Moreover, if we change “return $ qb-> getQuery () -> getResult ();” to “return $ qb-> getQuery () -> getArrayResult ();”, we get:

array (size=3)
0 =>
array (size=3)
'publisherid' => int 1
'statisticdt' =>
object ( DateTime )[384]
public 'date' => string '2012-04-15 00:00:00' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'Europe/Moscow' (length=13)
'somecontent' => string 'test 15' (length=7)
1 =>
array (size=3)
'publisherid' => int 1
'statisticdt' =>
object ( DateTime )[384]
public 'date' => string '2012-04-16 00:00:00' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'Europe/Moscow' (length=13)
'somecontent' => string 'test 16' (length=7)
2 =>
array (size=3)
'publisherid' => int 1
'statisticdt' =>
object ( DateTime )[384]
public 'date' => string '2012-04-17 00:00:00' (length=19)
public 'timezone_type' => int 3
public 'timezone' => string 'Europe/Moscow' (length=13)
'somecontent' => string 'test 17' (length=7)


* This source code was highlighted with Source Code Highlighter .

That is, correct data.

Solution to the problem


I began to understand what the problem was, and at first I sinned on the “PublisherDailyStatistic” object descriptions I created, but after my question on http://stackoverflow.com was left unanswered, I began to look for an error in Doctrine code.
And I found it in the “createEntity” function of the “UnitOfWork” class. There is the following code:

if ($ class ->isIdentifierComposite) {
$id = array();

foreach ($ class ->identifier as $fieldName) {
$id[$fieldName] = isset($ class ->associationMappings[$fieldName])
? $data[$ class ->associationMappings[$fieldName][ 'joinColumns' ][0][ 'name' ]]
: $data[$fieldName];
}

$idHash = implode( ' ' , $id);
} else {
$idHash = isset($ class ->associationMappings[$ class ->identifier[0]])
? $data[$ class ->associationMappings[$ class ->identifier[0]][ 'joinColumns' ][0][ 'name' ]]
: $data[$ class ->identifier[0]];

$id = array($ class ->identifier[0] => $idHash);
}


* This source code was highlighted with Source Code Highlighter .


In our case, the error appears in the first part of the if operator (since, we have a composite key, but there is an error in the second part). As we can see, the code goes through all the keys of the table, and the data of these keys are added to the $ id array, and then the string $ idHash is formed from this data, through the usual “implode”. Later, by the value of $ idHash, it is determined whether such data was encountered when creating an object earlier (because two objects with the same key are the same object). Here, then everything breaks down, since in our case, as a result of the implode function, we get the same $ idHash for all strings, in our case “1”.
This is because implode ignores the DateTime object (like any other objects that do not have the __toString () magic function).
I wrote to developers about this bug in their bugtracker .
In the meantime, you can easily correct this error in your project by adding a verification line to the loop:

if ($id[$fieldName] instanceof \ DateTime )
{
$id[$fieldName] = $id[$fieldName]->getTimestamp();
}


* This source code was highlighted with Source Code Highlighter .


This is my first article on Habré, please do not judge strictly, I will take into account all comments.
And sorry for having written so much text about such a small mistake.

Source: https://habr.com/ru/post/142221/


All Articles