Php 监听模型事件时的无限循环
我有一个用于属性的Laravel应用程序,比方说在我的代码中,我有:Php 监听模型事件时的无限循环,php,laravel,events,eloquent,Php,Laravel,Events,Eloquent,我有一个用于属性的Laravel应用程序,比方说在我的代码中,我有: $property = new Property(); $property->city = "New York"; ... $property->save(); 然后我有一个监听特定事件的事件侦听器: $events->listen( 'eloquent.saved: Properties\\Models\\Property', 'Google\Listeners\SetGeoLocati
$property = new Property();
$property->city = "New York";
...
$property->save();
然后我有一个监听特定事件的事件侦听器:
$events->listen(
'eloquent.saved: Properties\\Models\\Property',
'Google\Listeners\SetGeoLocationInfo@fire'
);
最后在SetGeoLocationInfo.php
public function fire($event)
{
$property = $event;
...
//get GPS data from Google Maps
$property->latitude = $googleMapsObject->latitude;
$property->longitude = $googleMapsObject->longitude;
$property->save();
}
当我在中保存模型时,由于处理程序中调用了save()
如何更改代码,使其在保存后只填充一次位置数据并避免递归
我无法使用flushEventListeners()
,因为在这种情况下,其他侦听器停止工作(例如,属性照片附加)。在这种情况下,最好使用保存方法。但是请注意,在保存期间
不应再使用保存
方法,因此您的激发
方法应如下所示:
public function fire($event)
{
$property = $event;
...
//get GPS data from Google Maps
$property->latitude = $googleMapsObject->latitude;
$property->longitude = $googleMapsObject->longitude;
}
public function fire($event)
{
$property = $event;
...
//get GPS data from Google Maps
$property->latitude = $googleMapsObject->latitude;
$property->longitude = $googleMapsObject->longitude;
$property->save(['nosavedevent' => true]);
}
另一种解决方案是添加条件,仅在尚未设置GPS位置时设置并保存GPS位置:
if (empty($property->latitude) || empty($property->longitude)) {
$property->latitude = $googleMapsObject->latitude;
$property->longitude = $googleMapsObject->longitude;
$property->save();
}
属性保存方法(必须为其定义属性常量):
叫它:
public function fire($event)
{
$property = $event;
...
//get GPS data from Google Maps
$property->latitude = $googleMapsObject->latitude;
$property->longitude = $googleMapsObject->longitude;
$property->save(Property::SAVE_FOO);
}
或
什么好?
所有条件都在一个位置(在save方法中)。您可以使用forget()
取消设置事件侦听器
Event::listen('a', function(){
Event::forget('a');
echo 'update a ';
event("b");
});
Event::listen('b', function(){
Event::forget('b');
echo 'update b ';
event("a");
});
event("a"); // update a update b
模型事件键被命名为“eloquent.{$event}:{$name}
”例如“eloquent.updated:Foo
”我点击了相同的按钮。在Laravel 5.6中,您可以简单地覆盖模型中的finishSave()函数:
protected function finishSave(array $options)
{
// this condition allow to control whenever fire the vent or not
if (!isset($options['nosavedevent']) or empty($options['nosavedevent'])) {
$this->fireModelEvent('saved', false);
}
if ($this->isDirty() && ($options['touch'] ?? true)) {
$this->touchOwners();
}
$this->syncOriginal();
}
然后你可以这样使用它:
public function fire($event)
{
$property = $event;
...
//get GPS data from Google Maps
$property->latitude = $googleMapsObject->latitude;
$property->longitude = $googleMapsObject->longitude;
}
public function fire($event)
{
$property = $event;
...
//get GPS data from Google Maps
$property->latitude = $googleMapsObject->latitude;
$property->longitude = $googleMapsObject->longitude;
$property->save(['nosavedevent' => true]);
}
在您的fire
方法中,您可以让它在应用新属性和保存之前调用$property->syncOriginal()
模型类有一个“脏”属性和“原始”属性的概念,作为一种了解哪些值已经被推送到数据库,哪些值仍然被安排在下一次保存的方式。通常情况下,Laravel在观察者开火之前不会将这些同步到一起。严格地说,这是一种蚂蚁模式,让你的听众表现得好像它知道它被激发的环境;因为您正在通过saved
操作触发它,因此可以确信数据已经到达数据库。但既然你是,问题只是模型还没有意识到这一点。任何后续的更新都会让观测者感到困惑,认为原来的价值观是全新的。因此,通过在应用任何其他更改之前自己显式调用syncOriginal()
方法,您应该能够避免递归。在我的例子中,使用savequirey()
而不是save()
解决了该问题,因为安全保存
不会触发任何事件,因此您不会陷入无限循环的事件中
编辑:我认为savequiety()
方法目前仅在laravel 8中可用。触发时,使用saving,而不是saveJust override save方法,在保存模式下使用Property-pass参数。在fire()中,如下所示:$property->save(property::save_FROM_GOOGLE_fire);为了其他的事情,这是不可避免的recursion@Deep你为什么这么认为?如果只保存两次,您将不会有任何递归,因为属性(值,存在)不能是保存模式的主/主属性。您的意思是我必须为保存elountent而侦听。保存:Exclusives\\Models\\Exclusive
事件?其他解决方案不适用,因为在用户更改街道地址的情况下,我必须找到新的坐标。@ademin是的,我的意思是,但每次更新坐标可能也不合理,您可能会对getDirty
方法感兴趣,该方法仅在特定字段发生更改时更新坐标,你的意思是我应该直接写DB而不是为foo写东西吗?是的,为什么不呢?你自己已经开始递归了。你有两种方法:1)不使用递归重写2)重写save方法,并在其中写入所需的所有内容。我不知道,这看起来不像是解决方案,更像是一种黑客行为。我希望我可以创建自定义事件或与事件传递一些争论…@ademin我同意。老实说,我在拉威尔方面没有什么技巧。我们一起等。我也对这些机会感兴趣。但您必须了解,这种情况是应用程序体系结构设计不佳的原因。@ademin您好吗?你怎么修好的?