Answers.Body: <p>Best way to handle this is probably with a <a href="https://docs.craftcms.com/v3/extend/commands.html" rel="nofollow noreferrer">console command</a> provided by a <a href="https://docs.craftcms.com/v3/extend/module-guide.html" rel="nofollow noreferrer">module</a>.</p>
  
  <p>The console command would need to fetch all asset IDs that don’t have any corresponding rows in the <code>relations</code> database table.</p>
  
  <pre class="lang-php prettyprint-override"><code>use Craft;
  use craft\db\Query;
  use craft\db\Table;
  use craft\elements\Asset;
  use yii\console\ExitCode;
  use craft\helpers\Console;
  
  public function actionPurgeUnusedAssets(): int
  {
      // Find any asset IDs that aren't related to anything
      $assetIds = (new Query())
          -&gt;select(['a.id'])
          -&gt;from(['a' =&gt; Table::ASSETS])
          -&gt;leftJoin(Table::RELATIONS . ' r', '[[r.targetId]] = [[a.id]]')
          -&gt;where(['r.id' =&gt; null])
          -&gt;column();
  
      if (empty($assetIds)) {
          $this-&gt;stdout('No unrelated assets to delete.' . PHP_EOL);
          return ExitCode::OK;
      }
  
      // Now fetch and delete them
      $assets = Asset::find()
          -&gt;id($assetIds)
          -&gt;all();
  
      $this-&gt;stdout('Found ' . count($assets) . ' unrelated assets.' . PHP_EOL);
  
      foreach ($assets as $asset) {
          $this-&gt;stdout(" - Deleting asset {$asset-&gt;filename} ... ");
          Craft::$app-&gt;elements-&gt;deleteElement($asset);
          $this-&gt;stdout('done' . PHP_EOL, Console::FG_GREEN);
      }
  
      $this-&gt;stdout('Finished deleting unrelated assets.' . PHP_EOL);
      return ExitCode::OK;
  }
  </code></pre>
  
  <p>Then, you can set up a cron job on your server that hits this command every 24 hours.</p>
  
  <pre><code>0 0 * * * /path/to/project/craft your-module/purge-unused-assets
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2020-02-07T14:47:29.300Z
Answers.Text: 0 0 * * * /path/to/project/craft your-module/action-purge-unused-assets is it not this or?
Answers.UserDisplayName: Bram Beekman
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2020-02-07T15:02:06.810Z
Answers.Text: @BramBeekman No, you don't include the `action-` part in the command.
Answers.UserDisplayName: Brandon Kelly
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2020-02-08T08:00:47.750Z
Answers.Text: $this->stdout(" - Deleting asset {$$asset->filename} ... ");  -> $this->stdout(" - Deleting asset {$asset->filename} ... ");
Answers.UserDisplayName: Bram Beekman
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2020-02-08T13:36:15.973Z
Answers.Text: @BramBeekman Thanks, fixed.
Answers.UserDisplayName: Brandon Kelly
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2021-11-23T09:44:05.617Z
Answers.Text: @BrandonKelly the Problem with this solution is that Assets that are "linked" in Redactor fields will be deleted either way and result into dead links.
Answers.UserDisplayName: Leonardo
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2021-11-25T09:08:58.553Z
Answers.Text: @BrandonKelly Any solution for assets that are linked inside a redactor field?
Answers.UserDisplayName: Nicholas
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2020-02-07T13:21:15.200Z
Answers.LastActivityDate: 2020-02-08T13:35:57.397Z
Answers.LastEditDate: 2020-02-08T13:35:57.397Z
Answers.UserDisplayName: Brandon Kelly
Answers.Body: <p>I once wrote a custom service doing this (not for commerce, though). I'll post the code, and maybe it can help you along the way. You can set it up in a module, make a console controller, and have a cronjob hit it every minute (although, are you sure you need to do it that often?).</p>
  
  <p>The DeleteAssets is a very simple extension of BaseJob, basically looping through the assets, and deleting them one by one, just so it's handled as queue job. You could also loop through and delete each with <code>Craft::$app-&gt;getElements()-&gt;deleteElementById()</code>, but doing it as a queue job is probably better.</p>
  
  <pre class="lang-php prettyprint-override"><code>    public function deleteUnusedAssets() {
  
          //First fetch all ids from elements table
          $allIds = Entry::find()
                   -&gt;limit(null)
                   -&gt;ids();
  
          // Query for all related Assets (assets that are used somewhere)
          $usedAssets = Asset::find()
                  -&gt;relatedTo($allIds)
                  -&gt;limit(null)
                  -&gt;ids();
  
          // Query for all assets NOT in the list of the used ones
          $excludeString = 'and, not ' . implode(', not ', $usedAssets);
          $unUsedAssets = Asset::find()
                  -&gt;id($excludeString)
                  -&gt;limit(null)
                  -&gt;ids();
  
          $queue = Craft::$app-&gt;getQueue();
          $jobID = $queue-&gt;push(new DeleteAssets([
              'description' =&gt; 'Deleting ' . count($unUsedAssets) . ' assets'),
              'elements' =&gt; $unUsedAssets,
          ]));
          $queue-&gt;run();
  
      }
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2020-02-06T14:25:32.187Z
Answers.Text: Unable to locate message source for category 'cinema-program'.
Answers.UserDisplayName: Bram Beekman
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2020-02-09T19:18:39.210Z
Answers.Text: Ah, that is the handle for my plugin. Just use your own, or replace description with something that doesn't use the translate function. I'll update the answer.
Answers.UserDisplayName: KSP
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2020-02-05T11:42:59.173Z
Answers.LastActivityDate: 2020-02-09T19:19:51.547Z
Answers.LastEditDate: 2020-02-09T19:19:51.547Z
Answers.UserDisplayName: KSP
Body: <p>I'm searching for a option to remove all unused images every minute.</p>
  
  <p>The images I use are for Craft Commerce. </p>
  
Tags: <plugin-craftcommerce>
Title: I'm searching for a option to remove all unused images
ThreadText: # I'm searching for a option to remove all unused images
  
  (Asked by: username_0 on 2020-02-05)
  
  I'm searching for a option to remove all unused images every minute.
  
  The images I use are for Craft Commerce.
  
  **Tags:** plugin-craftcommerce
  
  
  ---
  
  ## 2 answers
  
  ### Answer by username_1 on 2020-02-07. Score: 3 | Accepted answer
  
  Best way to handle this is probably with a [console command](https://docs.craftcms.com/v3/extend/commands.html) provided by a [module](https://docs.craftcms.com/v3/extend/module-guide.html).
  
  The console command would need to fetch all asset IDs that don’t have any corresponding rows in the `relations` database table.
  
  ```php
  use Craft;
  use craft\db\Query;
  use craft\db\Table;
  use craft\elements\Asset;
  use yii\console\ExitCode;
  use craft\helpers\Console;
  
  public function actionPurgeUnusedAssets(): int
  {
      // Find any asset IDs that aren't related to anything
      $assetIds = (new Query())
          ->select(['a.id'])
          ->from(['a' => Table::ASSETS])
          ->leftJoin(Table::RELATIONS . ' r', '[[r.targetId]] = [[a.id]]')
          ->where(['r.id' => null])
          ->column();
  
      if (empty($assetIds)) {
          $this->stdout('No unrelated assets to delete.' . PHP_EOL);
          return ExitCode::OK;
      }
  
      // Now fetch and delete them
      $assets = Asset::find()
          ->id($assetIds)
          ->all();
  
      $this->stdout('Found ' . count($assets) . ' unrelated assets.' . PHP_EOL);
  
      foreach ($assets as $asset) {
          $this->stdout(" - Deleting asset {$asset->filename} ... ");
          Craft::$app->elements->deleteElement($asset);
          $this->stdout('done' . PHP_EOL, Console::FG_GREEN);
      }
  
      $this->stdout('Finished deleting unrelated assets.' . PHP_EOL);
      return ExitCode::OK;
  }
  
  ```
  
  Then, you can set up a cron job on your server that hits this command every 24 hours.
  
  ```
  0 0 * * * /path/to/project/craft your-module/purge-unused-assets
  
  ```
  
  > **Comments:**
  > **username_0**: 0 0 * * * /path/to/project/craft your-module/action-purge-unused-assets is it not this or?  
  > **username_1**: @username_0 No, you don't include the `action-` part in the command.  
  > **username_0**: $this->stdout(" - Deleting asset {$$asset->filename} ... ");  -> $this->stdout(" - Deleting asset {$asset->filename} ... ");  
  > **username_1**: @username_0 Thanks, fixed.  
  > **username_2**: @username_1 the Problem with this solution is that Assets that are "linked" in Redactor fields will be deleted either way and result into dead links.  
  > **username_3**: @username_1 Any solution for assets that are linked inside a redactor field?  
  
  ---
  
  ### Answer by username_4 on 2020-02-05. Score: 1
  
  I once wrote a custom service doing this (not for commerce, though). I'll post the code, and maybe it can help you along the way. You can set it up in a module, make a console controller, and have a cronjob hit it every minute (although, are you sure you need to do it that often?).
  
  The DeleteAssets is a very simple extension of BaseJob, basically looping through the assets, and deleting them one by one, just so it's handled as queue job. You could also loop through and delete each with `Craft::$app->getElements()->deleteElementById()`, but doing it as a queue job is probably better.
  
  ```php
      public function deleteUnusedAssets() {
  
          //First fetch all ids from elements table
          $allIds = Entry::find()
                   ->limit(null)
                   ->ids();
  
          // Query for all related Assets (assets that are used somewhere)
          $usedAssets = Asset::find()
                  ->relatedTo($allIds)
                  ->limit(null)
                  ->ids();
  
          // Query for all assets NOT in the list of the used ones
          $excludeString = 'and, not ' . implode(', not ', $usedAssets);
          $unUsedAssets = Asset::find()
                  ->id($excludeString)
                  ->limit(null)
                  ->ids();
  
          $queue = Craft::$app->getQueue();
          $jobID = $queue->push(new DeleteAssets([
              'description' => 'Deleting ' . count($unUsedAssets) . ' assets'),
              'elements' => $unUsedAssets,
          ]));
          $queue->run();
  
      }
  
  ```
  
  > **Comments:**
  > **username_0**: Unable to locate message source for category 'cinema-program'.  
  > **username_4**: Ah, that is the handle for my plugin. Just use your own, or replace description with something that doesn't use the translate function. I'll update the answer.  
  
  ---
  
  