Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/oracle/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 监听模型事件时的无限循环_Php_Laravel_Events_Eloquent - Fatal编程技术网

Php 监听模型事件时的无限循环

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

我有一个用于属性的Laravel应用程序,比方说在我的代码中,我有:

$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您好吗?你怎么修好的?