<p>I haven't actually tested this, but going through the <a href="https://docs.craftcms.com/api/v3/craft-services-relations.html" rel="nofollow noreferrer">Relations service</a> instead of the Elements service might avoid the issues you're facing. Using the <a href="https://www.yiiframework.com/doc/api/2.0/yii-db-query#each()-detail" rel="nofollow noreferrer"><code>each()</code></a> method when looping over the element query results (as mentioned by August in the comments above) is also a good idea.</p>
<pre class="lang-php prettyprint-override"><code>$fieldProductMediaField = Craft::$app-&gt;getFields()-&gt;getFieldByHandle('fieldProductMedia');

foreach ($query-&gt;each() as $product) {
    Craft::$app-&gt;getRelations()-&gt;saveRelations($fieldProductMediaField, $product, []);
    ...
}
</code></pre>

CC BY-SA 4.0
2022-03-02T08:19:14.470Z
That's working. Really great, thanks!
bartpulse
CC BY-SA 4.0
2022-03-01T18:33:15.083Z
2022-03-01T18:33:15.083Z
Mats Mikkel Rummelhoff
<p>I have a large import with over 5000 products. These product elements have a image field wich is a assets relation field. Looping over the products and setting the field as null and then save takes waaaayyy too long:</p>
<pre><code>    foreach($products as $product) {
        $counter++;
        $product-&gt;setFieldValue('fieldProductMedia', NULL);

        Craft::$app-&gt;getElements()-&gt;saveElement($product, false, false, false);
    
        $message = $counter .'/' . $total;
        Console::stdout($message);
        Console::stdout(PHP_EOL);
    }
</code></pre>
<p>I tried using a query like so:</p>
<pre><code>DELETE r FROM `relations` AS r INNER JOIN elements AS e ON r.sourceId = e.id WHERE r.fieldId = 131 AND (e.type LIKE '%Product%' OR e.type LIKE '%Variant%');
</code></pre>
<p>But this messes with the foreign key constraint. So I tried it another way but this causes key constraints again when re-importing the images:</p>
<pre><code>        $relations = (new Query())
            -&gt;select(['r.id'])
            -&gt;from('relations AS r')
            -&gt;innerJoin('elements AS e','r.sourceId = e.id')
            -&gt;where(['r.fieldId' =&gt; '131'])
            -&gt;andWhere(&quot;e.type LIKE '%Product%' OR e.type LIKE '%Variant%'&quot;)
            -&gt;indexBy('id')
            -&gt;all();

        $result = Craft::$app-&gt;db-&gt;createCommand()
            -&gt;delete(Table::RELATIONS, ['id' =&gt; array_keys($relations)])
            -&gt;execute();
</code></pre>
<p>Any ideas?</p>

CC BY-SA 4.0
2022-03-01T15:20:12.607Z
Why does it matter that it takes some time to loop over the elements? Is it a one-time operation or does it need to happen regularly? Can it be done in the background?
MoritzLost
CC BY-SA 4.0
2022-03-01T15:35:18.303Z
It's related to another fun issue I'm having with memory that keeps increasing after each save(). I think it's a bug in Craft:
4454/5729 - 1800.85 mb
Error: Allowed memory size of 2147483648 bytes exhausted (tried to allocate 135168 bytes)
bartpulse
CC BY-SA 4.0
2022-03-01T15:48:02.800Z
Maybe your code is just badly optimized? Based on your example, it looks like you're loading 5000+ entries into one huge array and then iterating through it? No wonder you're having memory problems. Instead, try to load the entries one by one so that only one entry is in memory at a time. Will probably solve your problem here as well. You can use `$entryQuery->ids()` to get an array of entry IDs, then iterate through that and load each entry in turn.
MoritzLost
CC BY-SA 4.0
2022-03-01T15:48:50.643Z
Also, if you think you're looking at a memory leak in Craft, try to track it down to its source and open an issue on Github. Better to address the source of the problem than to put a bandaid on it.
MoritzLost
CC BY-SA 4.0
2022-03-01T15:52:42.737Z
There is also the [`$productQuery->each($n)`](https://www.yiiframework.com/doc/api/2.0/yii-db-query#each()-detail) method that will batch the DB operation and Element population into `$n` chunks to reduce the footprint of the whole operation. As long as you don’t memoize anything outside the loop, PHP will garbage collect for you!
August Miller
CC BY-SA 4.0
2022-03-01T16:58:31.743Z
@MoritzLost
 Yeah I already contacted them using support. With the release of Craft 4 on the horizon they doubt they'll have time to look into this.
bartpulse
CC BY-SA 4.0
2022-03-01T16:59:51.170Z
@AugustMiller
The memory increase happens inside the ->saveElement() method. The array constructed outside the loop is not the problem.
bartpulse
<craft3>
Fast way to delete relations without messing up foreign keys
# Fast way to delete relations without messing up foreign keys

(Asked by: username_0 on 2022-03-01)

I have a large import with over 5000 products. These product elements have a image field wich is a assets relation field. Looping over the products and setting the field as null and then save takes waaaayyy too long:
    foreach($products as $product) {
        $counter++;
        $product->setFieldValue('fieldProductMedia', NULL);

        Craft::$app->getElements()->saveElement($product, false, false, false);
    
        $message = $counter .'/' . $total;
        Console::stdout($message);
        Console::stdout(PHP_EOL);
    }

I tried using a query like so:
DELETE r FROM `relations` AS r INNER JOIN elements AS e ON r.sourceId = e.id WHERE r.fieldId = 131 AND (e.type LIKE '%Product%' OR e.type LIKE '%Variant%');

But this messes with the foreign key constraint. So I tried it another way but this causes key constraints again when re-importing the images:
        $relations = (new Query())
            ->select(['r.id'])
            ->from('relations AS r')
            ->innerJoin('elements AS e','r.sourceId = e.id')
            ->where(['r.fieldId' => '131'])
            ->andWhere("e.type LIKE '%Product%' OR e.type LIKE '%Variant%'")
            ->indexBy('id')
            ->all();

        $result = Craft::$app->db->createCommand()
            ->delete(Table::RELATIONS, ['id' => array_keys($relations)])
            ->execute();

Any ideas?

**Tags:** craft3

> **Comments:**
> **username_1**: Why does it matter that it takes some time to loop over the elements? Is it a one-time operation or does it need to happen regularly? Can it be done in the background?  
> **username_0**: It's related to another fun issue I'm having with memory that keeps increasing after each save(). I think it's a bug in Craft: 4454/5729 - 1800.85 mb Error: Allowed memory size of 2147483648 bytes exhausted (tried to allocate 135168 bytes)  
> **username_1**: Maybe your code is just badly optimized? Based on your example, it looks like you're loading 5000+ entries into one huge array and then iterating through it? No wonder you're having memory problems. Instead, try to load the entries one by one so that only one entry is in memory at a time. Will probably solve your problem here as well. You can use `$entryQuery->ids()` to get an array of entry IDs, then iterate through that and load each entry in turn.  
> **username_1**: Also, if you think you're looking at a memory leak in Craft, try to track it down to its source and open an issue on Github. Better to address the source of the problem than to put a bandaid on it.  
> **username_2**: There is also the [`$productQuery->each($n)`](https://www.yiiframework.com/doc/api/2.0/yii-db-query#each()-detail) method that will batch the DB operation and Element population into `$n` chunks to reduce the footprint of the whole operation. As long as you don’t memoize anything outside the loop, PHP will garbage collect for you!  
> **username_0**: @username_1  Yeah I already contacted them using support. With the release of Craft 4 on the horizon they doubt they'll have time to look into this.  
> **username_0**: @AugustMiller The memory increase happens inside the ->saveElement() method. The array constructed outside the loop is not the problem.  

---

## 1 answers

### Answer by username_3 on 2022-03-01. Score: 2 | Accepted answer

I haven't actually tested this, but going through the Relations service instead of the Elements service might avoid the issues you're facing. Using the each() method when looping over the element query results (as mentioned by August in the comments above) is also a good idea.
$fieldProductMediaField = Craft::$app->getFields()->getFieldByHandle('fieldProductMedia');

foreach ($query->each() as $product) {
    Craft::$app->getRelations()->saveRelations($fieldProductMediaField, $product, []);
    ...
}

> **Comments:**
> **username_0**: That's working. Really great, thanks!  

---

