为什么实体管理器为每个实体刷新相同的日期



我正在为symfony 5.2中的实体制作固定装置。

其中一个代表一本有标题、描述和发行日期的杂志。

我的固定装置代码:

class MagazineFixtures extends Fixture
{
public function load(ObjectManager $manager)
{
$faker = Factory::create();
$intialReleaseDate = new DateTime('1913-04-01');
for ($i=0; $i < 100; $i++) { 
$title = $faker->sentence($nbrwords=6, $variableNbWords=true);
$number = $i + 1;
// A magazine is release each month.
$releaseDate = $intialReleaseDate;
$intialReleaseDate->modify('first day of next month');
$magazine = new Magazine();
$magazine
->setTitle($title)
->setNumber($number)
->setReleaseDate($releaseDate)
;
$manager->persist($magazine);
}
$manager->flush();
}
}

问题是,当我运行我的固定装置时,每个杂志都有相同的日期(这是最后一个日期(,我通过在for循环中打印它们来验证所有日期,所有日期都是正确的。

我试着在for循环中移动flush方法,它成功了!日期已正确保存在数据库中。

我了解到,当所有实体都被持久化时,必须调用flush方法,直到现在它一直有效。我不明白为什么它在这里不起作用。

发生这种情况的原因通常与flush方法或实体管理器无关。之所以会发生这种情况,是因为包含对象的变量并不包含对象本身,而是包含一个标识符(引用对象和引用页面(:

PHP引用是一个别名,它允许两个不同的变量写入相同的值。从PHP5开始,对象变量不再包含对象本身作为值。它只包含一个对象标识符,该标识符允许对象访问器查找实际对象。当一个对象通过参数发送、返回或分配给另一个变量时,不同的变量不是别名:它们持有指向同一对象的标识符的副本。

要进行演示,请考虑一个简单的案例:

class A
{
public $b;
}
class B
{
public $num;
}
$b = new B();
$b->num = 2;
$a = new A();
$a->b = $b;
print_r($a);
$b->num = 4;
print_r($a);

第一个print_r将显示以下内容:

A Object (
[b] => B Object (
[num] => 2
)
)

第二个将显示:

A Object (
[b] => B Object (
[num] => 4
)
)

尽管我们没有对$a本身进行任何修改,但通过修改(突变($b,我们已经确保$a->b保持更新的值。

因此,代码中发生的情况是,由于DateTime是可变的,通过在每次迭代中修改它,您就修改了分配给每个实体的同一实例。这意味着,在循环结束时,每个实体都将引用修改后的实例,从而导致刷新相同的日期。

绕过这个问题的解决方案是,在循环后仍然允许单个flush调用,或者切换到DateTimeImmutable,后者在修改时创建一个新实例,或者将DateTime对象的克隆传递到实体中。

最新更新