title: CakePHP 3 - executing multiple Commands from 1 Command and logging errors if they occur
body: <p>I'm building an application in CakePHP 3.8 which uses <a href="https://book.cakephp.org/3/en/console-and-shells/commands.html" rel="nofollow noreferrer">Console Commands</a> to execute several processes.</p>
  <p>These processes are quite resource intensive so I've written them with Commands because they would easily time-out if executed in a browser.</p>
  <p>There are 5 different scripts that do different tasks: <code>src/Command/Stage1Command.php</code>,
  ... <code>src/Command/Stage5Command.php</code>.</p>
  <p>The scripts are being executed in order (Stage 1 ... Stage 5) <b>manually</b>, i.e. <code>src/Command/Stage1Command.php</code> is executed with:</p>
  <pre><code>$ php bin/cake.php stage1
  </code></pre>
  <p>All 5 commands accept one parameter - an ID - and then perform some work. This has been set up as follows (the code in <code>buildOptionsParser()</code> exists in each command):</p>
  <pre><code>class Stage1Command extends Command
  {
      protected function buildOptionParser(ConsoleOptionParser $parser)
      {
          $parser-&gt;addArgument('filter_id', [
              'help' =&gt; 'Filter ID must be passed as an argument',
              'required' =&gt; true
          ]);
          return $parser;
      }
  }
  </code></pre>
  <p>So I can execute &quot;Stage 1&quot; as follows, assuming <code>428</code> is the ID I want to pass.</p>
  <pre><code>$ php bin/cake.php stage1 428
  </code></pre>
  <p>Instead of executing these manually, I want to achieve the following:</p>
  <ol>
  <li><p>Create a new Command which loops through a set of Filter ID's and then calls each of the 5 commands, passing the ID.</p>
  </li>
  <li><p>Update a table to show the outcome (success, error) of each command.</p>
  </li>
  </ol>
  <p>For (1) I have created <code>src/Command/RunAllCommand.php</code> and then used a loop on my table of Filters to generate the IDs, and then execute the 5 commands, passing the ID. The script looks like this:</p>
  <pre><code>namespace App\Command;
  use Cake\ORM\TableRegistry;
  // ...
  
  class RunAllCommand extends Command
  {
      
      public function execute(Arguments $args, ConsoleIo $io)
      {
          $FiltersTable = TableRegistry::getTableLocator()-&gt;get('Filters');
  
          $all_filters = $FiltersTable-&gt;find()-&gt;toArray();
  
          foreach ($all_filters as $k =&gt; $filter) {
              $io-&gt;out($filter['id']);
          
              // execute Stage1Command.php        
              $command = new Stage1Command(['filter_id' =&gt; $filter['id']]);
              $this-&gt;executeCommand($command);
       
              // ...
  
              // execute Stage5Command.php
              $command5 = new Stage5Command(['filter_id' =&gt; $filter['id']]);
              $this-&gt;executeCommand($command5);
          }
      }
  }
  </code></pre>
  <p>This doesn't work. It gives an error:</p>
  <blockquote>
  <p>Filter ID must be passed as an argument</p>
  </blockquote>
  <p>I can tell that the commands are being called because these are my own error messages from <code>buildOptionsParser()</code>.</p>
  <p>This makes no sense because the line <code>$io-&gt;out($filter['id'])</code> in <code>RunAllCommand.php</code> is showing that the filter IDs are being read from my database. How do you pass an argument in this way? I'm following the docs on Calling Other Commands (<a href="https://book.cakephp.org/3/en/console-and-shells/commands.html#calling-other-commands" rel="nofollow noreferrer">https://book.cakephp.org/3/en/console-and-shells/commands.html#calling-other-commands</a>).</p>
  <p>I don't understand how to achieve (2). In each of the Commands I've added code such as this when an error occurs which stops execution of the rest of that Command. For example if this gets executed in <code>Stage1Command</code> it should abort and move to <code>Stage2Command</code>:</p>
  <pre><code>// e.g. this code can be anywhere in execute() in any of the 5 commands where an error occurs.
  $io-&gt;error('error message');
  $this-&gt;abort();
  </code></pre>
  <p>If <code>$this-&gt;abort()</code> gets called anywhere I need to log this into another table in my database. Do I need to add code before <code>$this-&gt;abort()</code> to write this to a database, or is there some other way, e.g. <code>try...catch</code> in <code>RunAllCommand</code>?</p>
  <p><b>Background information:</b> The idea with this is that <code>RunAllCommand.php</code> would be executed via <a href="https://book.cakephp.org/3/en/console-and-shells/cron-jobs.html" rel="nofollow noreferrer">Cron</a>. This means that the processes carried out by each Stage would occur at regular intervals without requiring manual execution of any of the scripts - or passing IDs manually as command parameters.</p>