<p>The easiest solution for this would be either a special fieldvalue or a property in a <a href="https://www.yiiframework.com/doc/guide/2.0/en/concept-behaviors" rel="nofollow noreferrer">behavior</a> that you set in your job.</p>

<pre class="lang-php prettyprint-override"><code>class MyJob extends BaseJob
{
    public function execute($queue): bool
    {
        // do some api calls here
        // ....
        $entry-&gt;isSavedViaApi = true;
        $entry-&gt;setFieldValue('browsershotUrl', [$assetId]);
        Craft::$app-&gt;elements-&gt;saveElement($entry)
        // ....
    }
}

</code></pre>

<p>and in your event, you can check against this property</p>

<pre class="lang-php prettyprint-override"><code>Event::on(
  Elements::class,
  Elements::EVENT_BEFORE_SAVE_ELEMENT,
  function(Event $event) {
    $entry = $event-&gt;sender;
    if ($event-&gt;element instanceof \craft\elements\Entry) {
      $entry = $event-&gt;element;
      if (ElementHelper::isDraftOrRevision($entry)) {
        return;
      }

      if(($entry-&gt;sectionId == 18) &amp;&amp; ($entry-&gt;typeId == 26)) {

        // make sure to check the section first, because otherwise the entry
        // may not have this property of field
        if($entry-&gt;isSavedViaApi === true){
            return;
        }
        //


        $signpostURL = $entry-&gt;getFieldValue('signpostUrl');
        if(($signpostURL) &amp;&amp; ($signpostURL !== '')) {
          Craft::$app-&gt;queue-&gt;push(new BrowshotTask([
            'description' =&gt; 'Grabbing screenshot for '.$signpostURL,
            'url' =&gt; $signpostURL,
            'entryId' =&gt; $entry-&gt;id
          ]));
        }
      }
    }
  }
);
</code></pre>

<p>If you want to use a behavior instead of a field value for this</p>

<pre class="lang-php prettyprint-override"><code>use yii\base\Behavior;

class MyBehavior extends Behavior
{
    public $isSavedViaApi = false;
}
</code></pre>

<p>and in your main module file</p>

<pre class="lang-php prettyprint-override"><code>use craft\elements\Entry;
use craft\events\DefineBehaviorsEvent;

Event::on(
    Entry::class,
    Entry::EVENT_DEFINE_BEHAVIORS,
    static function(DefineBehaviorsEvent $event) {
        //so every element gets a isSubscribed attached
        $event-&gt;behaviors[] = MyBehavior::class;
    }
);
</code></pre>

CC BY-SA 4.0
2019-11-08T09:57:47.453Z
2019-11-08T09:57:47.453Z
Robin Schambach
<p>I'm calling EVENT_BEFORE_SAVE_ELEMENT to trigger a task that then updates the entry, but it's causing a loop?</p>

<p>The event is triggered when a specific entry type is saved:</p>

<pre><code>Event::on(
  Elements::class,
  Elements::EVENT_BEFORE_SAVE_ELEMENT,
  function(Event $event) {
    $entry = $event-&gt;sender;
    if ($event-&gt;element instanceof \craft\elements\Entry) {
      $entry = $event-&gt;element;
      if (ElementHelper::isDraftOrRevision($entry)) {
        return;
      }
      if(($entry-&gt;sectionId == 18) &amp;&amp; ($entry-&gt;typeId == 26)) {
        $signpostURL = $entry-&gt;getFieldValue('signpostUrl');
        if(($signpostURL) &amp;&amp; ($signpostURL !== '')) {
          Craft::$app-&gt;queue-&gt;push(new BrowshotTask([
            'description' =&gt; 'Grabbing screenshot for '.$signpostURL,
            'url' =&gt; $signpostURL,
            'entryId' =&gt; $entry-&gt;id
          ]));
        }
      }
    }
  }
);
</code></pre>

<p><code>BrowshotTask</code> then performs some API calls to a third-party service, gets a screenshot, saves an asset and saves the asset in the asset field of the entry.</p>

<p>But this triggers the event again and causes a loop:</p>

<pre><code>$entry-&gt;setFieldValue('browsershotUrl', [$assetId]);
if(Craft::$app-&gt;elements-&gt;saveElement($entry)) {
</code></pre>

<p>Sometimes BrowshotTask can take 20 seconds due to the third-party service.. so I thought putting it in a task would be a great idea, but now it's causing me so many problems :(</p>

<entries><events><loop><tasks><queue>
EVENT_BEFORE_SAVE_ELEMENT Saving element loop in task/queue
# EVENT_BEFORE_SAVE_ELEMENT Saving element loop in task/queue

(Asked by: username_0 on 2019-11-07)

I'm calling EVENT_BEFORE_SAVE_ELEMENT to trigger a task that then updates the entry, but it's causing a loop?
The event is triggered when a specific entry type is saved:
Event::on(
  Elements::class,
  Elements::EVENT_BEFORE_SAVE_ELEMENT,
  function(Event $event) {
    $entry = $event->sender;
    if ($event->element instanceof \craft\elements\Entry) {
      $entry = $event->element;
      if (ElementHelper::isDraftOrRevision($entry)) {
        return;
      }
      if(($entry->sectionId == 18) && ($entry->typeId == 26)) {
        $signpostURL = $entry->getFieldValue('signpostUrl');
        if(($signpostURL) && ($signpostURL !== '')) {
          Craft::$app->queue->push(new BrowshotTask([
            'description' => 'Grabbing screenshot for '.$signpostURL,
            'url' => $signpostURL,
            'entryId' => $entry->id
          ]));
        }
      }
    }
  }
);

BrowshotTask then performs some API calls to a third-party service, gets a screenshot, saves an asset and saves the asset in the asset field of the entry.
But this triggers the event again and causes a loop:
$entry->setFieldValue('browsershotUrl', [$assetId]);
if(Craft::$app->elements->saveElement($entry)) {

Sometimes BrowshotTask can take 20 seconds due to the third-party service.. so I thought putting it in a task would be a great idea, but now it's causing me so many problems :(

**Tags:** entries, events, loop, tasks, queue

---

## 1 answers

### Answer by username_1 on 2019-11-08. Score: 2 | Accepted answer

The easiest solution for this would be either a special fieldvalue or a property in a behavior that you set in your job.
class MyJob extends BaseJob
{
    public function execute($queue): bool
    {
        // do some api calls here
        // ....
        $entry->isSavedViaApi = true;
        $entry->setFieldValue('browsershotUrl', [$assetId]);
        Craft::$app->elements->saveElement($entry)
        // ....
    }
}


and in your event, you can check against this property
Event::on(
  Elements::class,
  Elements::EVENT_BEFORE_SAVE_ELEMENT,
  function(Event $event) {
    $entry = $event->sender;
    if ($event->element instanceof \craft\elements\Entry) {
      $entry = $event->element;
      if (ElementHelper::isDraftOrRevision($entry)) {
        return;
      }

      if(($entry->sectionId == 18) && ($entry->typeId == 26)) {

        // make sure to check the section first, because otherwise the entry
        // may not have this property of field
        if($entry->isSavedViaApi === true){
            return;
        }
        //


        $signpostURL = $entry->getFieldValue('signpostUrl');
        if(($signpostURL) && ($signpostURL !== '')) {
          Craft::$app->queue->push(new BrowshotTask([
            'description' => 'Grabbing screenshot for '.$signpostURL,
            'url' => $signpostURL,
            'entryId' => $entry->id
          ]));
        }
      }
    }
  }
);

If you want to use a behavior instead of a field value for this
use yii\base\Behavior;

class MyBehavior extends Behavior
{
    public $isSavedViaApi = false;
}

and in your main module file
use craft\elements\Entry;
use craft\events\DefineBehaviorsEvent;

Event::on(
    Entry::class,
    Entry::EVENT_DEFINE_BEHAVIORS,
    static function(DefineBehaviorsEvent $event) {
        //so every element gets a isSubscribed attached
        $event->behaviors[] = MyBehavior::class;
    }
);


---

