DateTimeキーを持つテーブルからデータを取得しようとするときのオブジェクトの複製

先日、Doctrine2(バージョン2.2.2)の操作で奇妙なエラーに遭遇しました。

問題の本質


DateTime型のキーを持つテーブルから(オブジェクトの配列(エンティティ)の形式で)データを取得しようとすると、Doctrineは1つのオブジェクト(最初の行)とその参照からなる配列を返しました。


エラーの説明


テーブルがあります。PublisherDailyStatisticという名前で、2つのキー(より正確には1つのキーですが、複合キー)があります。
1)PublisherID整数
2)StatisticDT DateTime
したがって、1日1回の発行元に関する統計がこの表に記録されます。
すでにデータがあるとします:
PublisherID統計DT内容
12012-04-15 00:00:00テスト15
12012-04-16 00:00:00テスト16
12012-04-17 00:00:00テスト17


さらに、リポジトリ(〜PublisherDailyStatisticRepository.php)には、一定期間データベースからパブリッシャー統計をプルする機能があります。
次のようになります。
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 .



すべてがシンプルで明確なようです。 しかし、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 .


意外とそうですか?
さらに、「return $ qb-> getQuery()-> getResult();」を「return $ qb-> getQuery()-> getArrayResult();」に変更すると、次のようになります。

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 .

つまり、正しいデータです。

問題解決


私は問題が何であるかを理解し始め、最初に私が作成したPublisherDailyStatisticオブジェクトの説明に罪を犯しましたが、 http: //stackoverflow.comでの質問に対する回答がなかった後、Doctrineコードのエラーを探し始めました。
そして、クラス「UnitOfWork」の関数「createEntity」でそれを見つけました。 次のコードがあります。

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 .


この場合、エラーはifステートメントの最初の部分に表示されます(複合キーがあるため、2番目の部分にもエラーがあります)。 ご覧のとおり、コードはテーブルのすべてのキーを通過し、これらのキーのデータが$ id配列に追加され、その後、通常の「内破」によってデータから$ idHash文字列が形成されます。 後で、$ idHashの値によって、オブジェクトを以前に作成したときにそのようなデータが検出されたかどうかが判断されます(同じキーを持つ2つのオブジェクトが同じオブジェクトであるため)。場合、関数implodeの結果として、すべての行に対して同じ$ idHashを取得します。この場合は「1」です。
これは、implodeはDateTimeオブジェクトを無視するためです(__toString()マジック関数を持たない他のオブジェクトと同様)。
私は開発者にバグトラッカーでこのエラーについて書いた。
それまでの間、ループにチェック行を追加することで、プロジェクトのこのエラーを簡単に修正できます。

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


* This source code was highlighted with Source Code Highlighter .


これはハブに関する私の最初の記事です。厳密に判断しないようお願いします。すべてのコメントを考慮します。
そして、このような小さな間違いについて多くのテキストを書いてすみません。

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


All Articles