Answers.Body: <p>I took the route of adding a controller with methods that could be triggered as craft console commands, and then simply ran these commands via cron tasks.</p>
  
  <p>In the <strong>init()</strong> public method of the main plugin class, I added the console commands controller:</p>
  
  <pre><code>public function init()
  {
     // ...
  
     // Add in our console commands
     if (Craft::$app instanceof ConsoleApplication) {
        $this-&gt;controllerNamespace = 'vendor\shopify\console\controllers';
     }
  
     // ...
  }
  </code></pre>
  
  <p>Inside this namespace of console controllers above, I have a single controller named <strong>CronCroller</strong>, which extends <em>yii\console\Controller</em>:</p>
  
  <pre><code>&lt;?php
  
  namespace vendor\shopify\console\controllers;
  
  use vendor\shopify\Shopify;
  
  use Craft;
  use yii\console\Controller;
  use craft\elements\Entry;
  
  /**
   * Shopify plugin commands for cron automation
   *
   * The first line of this class docblock is displayed as the description
   * of the Console Command in ./craft help
   *
   * Craft can be invoked via commandline console by using the `./craft` command
   * from the project root.
   *
   * Console Commands are just controllers that are invoked to handle console
   * actions. The segment routing is plugin-name/controller-name/action-name
   *
   * The actionIndex() method is what is executed if no sub-commands are supplied.
   *
   * Actions must be in 'kebab-case' so actionDoSomething() maps to 'do-something'
   *
   */
  class CronController extends Controller
  {
      // Public Methods
      // =========================================================================
  
      /**
       * Run Shopify product Sync
       *
       * The first line of this method docblock is displayed as the description
       * of the Console Command in ./craft help
       *
       * @return mixed
       */
      public function actionSync()
      {
          echo "\nSyncing Craft products with Shopify.\n";
  
          // [Automated Task Processes]
  
          echo "\nSuccess.\n";
          return;
      }
  }
  </code></pre>
  
  <p>Since the name of the controller is <em>CronController</em>, I can run the code in the <strong>actionSync()</strong> public method through the terminal with a command like:</p>
  
  <pre><code>php craft shopify/cron/sync;
  </code></pre>
  
  <p><a href="https://pluginfactory.io/" rel="nofollow noreferrer">PluginFactory.io</a> is a terrific resource for exploring and learning more, which is how I set up my plugin's console commands.</p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2019-02-12T19:04:28.693Z
Answers.LastActivityDate: 2019-02-12T19:04:28.693Z
Answers.UserDisplayName: Josh Kautz
Answers.Body: <p>You could add console command in your plugin then use third party cron job package to run the console command, your console command will be responsible for calling index function. i had similar kind of requirement earlier, i accomplished this using third party cron job package which was dependency for my plugin to work.</p>
  
  <p>There is new craft 3 plugin call scheduler <a href="https://github.com/supercool/scheduler" rel="nofollow noreferrer">https://github.com/supercool/scheduler</a>.</p>
  
  <p>I am not 100% sure that this idea will work on your side but just a heads up for you to get started.</p>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2019-02-12T18:35:34.933Z
Answers.Text: Thank you for the suggestion about console commands - this is what I ended up doing today and it worked perfectly. I will post another answer about this to follow up. I appreciate you taking the time to steer me in the right direction!
Answers.UserDisplayName: Josh Kautz
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2019-02-12T01:08:34.450Z
Answers.LastActivityDate: 2019-02-12T01:08:34.450Z
Answers.UserDisplayName: sailendra mangranti
Body: <p>I have made a Craft 3 plugin that is manually used in the Craft Admin CP area; click a button and we sync products in Craft with a Shopify API. Now I am trying to automate the process.</p>
  
  <p><a href="https://i.sstatic.net/iMymh.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/iMymh.png" alt="Craft 3 Admin CP Plugin Section "></a></p>
  
  <p>I would like to schedule a task that runs the controller that is executed when an admin user manually clicks the <em>Manual Sync</em> button, shown above. Clicking the button runs the actionIndex() method of my plugin's controller, which extends the Craft 3 web controller:</p>
  
  <pre><code>&lt;?php
  
  namespace vendor\shopify\controllers;
  
  use \vendor\shopify\Shopify;
  use craft\elements\Entry;
  use craft\elements\MatrixBlock;
  use DateTime;
  
  class SyncController extends \craft\web\Controller
  {
      public function actionIndex()
      {
          $apiKey = \vendor\shopify\Shopify::getInstance()-&gt;getSettings()-&gt;apiKey;
          $password = \vendor\shopify\Shopify::getInstance()-&gt;getSettings()-&gt;password;
          $secret = \vendor\shopify\Shopify::getInstance()-&gt;getSettings()-&gt;secret;
          $hostname = \vendor\shopify\Shopify::getInstance()-&gt;getSettings()-&gt;hostname;
  
          $curl = curl_init();
          curl_setopt_array($curl, array(
              CURLOPT_URL =&gt; "https://$apiKey:$password@$hostname/admin/products.json",
              CURLOPT_RETURNTRANSFER =&gt; true,
              CURLOPT_ENCODING =&gt; "",
              CURLOPT_MAXREDIRS =&gt; 10,
              CURLOPT_TIMEOUT =&gt; 30,
              CURLOPT_HTTP_VERSION =&gt; CURL_HTTP_VERSION_1_1,
              CURLOPT_CUSTOMREQUEST =&gt; "GET",
              CURLOPT_POSTFIELDS =&gt; "",
              CURLOPT_HTTPHEADER =&gt; array(
                  "cache-control: no-cache"
              ),
          ));
          $response = curl_exec($curl);
          $err = curl_error($curl);
          curl_close($curl);
  
          if ($err) {
              \Craft::info("(Vendor Shopify plugin) cURL Error #:" . $err, __METHOD__);
          }
          else {
              $section = \Craft::$app-&gt;sections-&gt;getSectionByHandle('products');
              $entryTypes = $section-&gt;getEntryTypes();
  
              foreach (json_decode($response)-&gt;products as $product) {
                  $query = Entry::find();
                  $query-&gt;section('products');
                  $query-&gt;shopifyProduct($product-&gt;id);
                  $currentEntries = $query-&gt;exists();
  
                  if ($currentEntries) {
                      $existingEntry = $query-&gt;all();
                      $existingEntry = $existingEntry[0];
                      $blockIds = [];
  
                      $field = \Craft::$app-&gt;getFields()-&gt;getFieldByHandle('productVariants');
                      $existingMatrixQuery = $existingEntry-&gt;getFieldValue('productVariants');
  
                      foreach ($existingMatrixQuery-&gt;all() as $block) {
                          $blockIds[] = $block-&gt;id;
                          \Craft::info("(Vendor Shopify plugin) Block Id??: " . json_encode($block-&gt;id), __METHOD__);
                      }
  
                      $serializedMatrix = $field-&gt;serializeValue($existingMatrixQuery, $existingEntry);
  
                      if (count($product-&gt;variants) === count($blockIds)) {
                          foreach ($product-&gt;variants as $key =&gt; $variant) {
                              $serializedMatrix[$blockIds[$key]] = [
                                  'type' =&gt; 'productVariant',
                                  'enabled' =&gt; true,
                                  'fields' =&gt; [
                                      'sku' =&gt; $variant-&gt;sku,
                                      'variantSize' =&gt; $variant-&gt;title,
                                      'variantPrice' =&gt; number_format((float)$variant-&gt;price, 2, '.', ''),
                                      'variantCompareAtPrice' =&gt; number_format((float)$variant-&gt;compare_at_price, 2, '.', ''),
                                      'variantQuantity' =&gt; $variant-&gt;inventory_quantity,
                                  ]
                              ];
                          }
                      }
                      else {
                          $serializedMatrix = [];
                          foreach ($product-&gt;variants as $key =&gt; $variant) {
                              $serializedMatrix['new' . $key] = [
                                  'type' =&gt; 'productVariant',
                                  'enabled' =&gt; true,
                                  'fields' =&gt; [
                                      'sku' =&gt; $variant-&gt;sku,
                                      'variantSize' =&gt; $variant-&gt;title,
                                      'variantPrice' =&gt; number_format((float)$variant-&gt;price, 2, '.', ''),
                                      'variantCompareAtPrice' =&gt; number_format((float)$variant-&gt;compare_at_price, 2, '.', ''),
                                      'variantQuantity' =&gt; $variant-&gt;inventory_quantity,
                                  ]
                              ];
                          }
                      };
  
                      $existingEntry-&gt;setFieldValues([
                          'productDescription' =&gt; $product-&gt;body_html,
                          'productVariants' =&gt; $serializedMatrix,
                      ]);
  
                      $success = \Craft::$app-&gt;elements-&gt;saveElement($existingEntry);
  
                      if (!$success) {
                          \Craft::info("(Vendor Shopify plugin) Error Saving Product: " . $product-&gt;title . " " . $err, __METHOD__);
                      }
                  }
              }
          }
          \Craft::$app-&gt;getSession()-&gt;setNotice('Manual Sync complete.');
          return $this-&gt;renderTemplate('shopify/done');
      }
  }
  </code></pre>
  
  <p>So far, I have explored options discussed in these two StackExchange questions that are somewhat related to my issue (<a href="https://craftcms.stackexchange.com/questions/16233/call-craft-controller-with-cron-job">Call Craft controller with cron job</a> and <a href="https://craftcms.stackexchange.com/questions/12973/craft-taskservice-overview-cron-job">Craft TaskService Overview/Cron Job</a>). Unfortunately my labors haven't been fruitful! I am open to any and all suggestions in regards to how I might accomplish the automation of calling this plugin class's actionIndex method. Thank you, I sincerely appreciate any feedback!</p>
  
Tags: <plugin-development><craft3><plugins><cron>
Title: Craft 3: Cron job to run Controller actionIndex method of a Plugin
ThreadText: # Craft 3: Cron job to run Controller actionIndex method of a Plugin
  
  (Asked by: username_0 on 2019-02-12)
  
  I have made a Craft 3 plugin that is manually used in the Craft Admin CP area; click a button and we sync products in Craft with a Shopify API. Now I am trying to automate the process.
  
  [![Craft 3 Admin CP Plugin Section ](https://i.sstatic.net/iMymh.png)](https://i.sstatic.net/iMymh.png)
  
  I would like to schedule a task that runs the controller that is executed when an admin user manually clicks the *Manual Sync* button, shown above. Clicking the button runs the actionIndex() method of my plugin's controller, which extends the Craft 3 web controller:
  
  ```
  <?php
  
  namespace vendor\shopify\controllers;
  
  use \vendor\shopify\Shopify;
  use craft\elements\Entry;
  use craft\elements\MatrixBlock;
  use DateTime;
  
  class SyncController extends \craft\web\Controller
  {
      public function actionIndex()
      {
          $apiKey = \vendor\shopify\Shopify::getInstance()->getSettings()->apiKey;
          $password = \vendor\shopify\Shopify::getInstance()->getSettings()->password;
          $secret = \vendor\shopify\Shopify::getInstance()->getSettings()->secret;
          $hostname = \vendor\shopify\Shopify::getInstance()->getSettings()->hostname;
  
          $curl = curl_init();
          curl_setopt_array($curl, array(
              CURLOPT_URL => "https://$apiKey:$password@$hostname/admin/products.json",
              CURLOPT_RETURNTRANSFER => true,
              CURLOPT_ENCODING => "",
              CURLOPT_MAXREDIRS => 10,
              CURLOPT_TIMEOUT => 30,
              CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
              CURLOPT_CUSTOMREQUEST => "GET",
              CURLOPT_POSTFIELDS => "",
              CURLOPT_HTTPHEADER => array(
                  "cache-control: no-cache"
              ),
          ));
          $response = curl_exec($curl);
          $err = curl_error($curl);
          curl_close($curl);
  
          if ($err) {
              \Craft::info("(Vendor Shopify plugin) cURL Error #:" . $err, __METHOD__);
          }
          else {
              $section = \Craft::$app->sections->getSectionByHandle('products');
              $entryTypes = $section->getEntryTypes();
  
              foreach (json_decode($response)->products as $product) {
                  $query = Entry::find();
                  $query->section('products');
                  $query->shopifyProduct($product->id);
                  $currentEntries = $query->exists();
  
                  if ($currentEntries) {
                      $existingEntry = $query->all();
                      $existingEntry = $existingEntry[0];
                      $blockIds = [];
  
                      $field = \Craft::$app->getFields()->getFieldByHandle('productVariants');
                      $existingMatrixQuery = $existingEntry->getFieldValue('productVariants');
  
                      foreach ($existingMatrixQuery->all() as $block) {
                          $blockIds[] = $block->id;
                          \Craft::info("(Vendor Shopify plugin) Block Id??: " . json_encode($block->id), __METHOD__);
                      }
  
                      $serializedMatrix = $field->serializeValue($existingMatrixQuery, $existingEntry);
  
                      if (count($product->variants) === count($blockIds)) {
                          foreach ($product->variants as $key => $variant) {
                              $serializedMatrix[$blockIds[$key]] = [
                                  'type' => 'productVariant',
                                  'enabled' => true,
                                  'fields' => [
                                      'sku' => $variant->sku,
                                      'variantSize' => $variant->title,
                                      'variantPrice' => number_format((float)$variant->price, 2, '.', ''),
                                      'variantCompareAtPrice' => number_format((float)$variant->compare_at_price, 2, '.', ''),
                                      'variantQuantity' => $variant->inventory_quantity,
                                  ]
                              ];
                          }
                      }
                      else {
                          $serializedMatrix = [];
                          foreach ($product->variants as $key => $variant) {
                              $serializedMatrix['new' . $key] = [
                                  'type' => 'productVariant',
                                  'enabled' => true,
                                  'fields' => [
                                      'sku' => $variant->sku,
                                      'variantSize' => $variant->title,
                                      'variantPrice' => number_format((float)$variant->price, 2, '.', ''),
                                      'variantCompareAtPrice' => number_format((float)$variant->compare_at_price, 2, '.', ''),
                                      'variantQuantity' => $variant->inventory_quantity,
                                  ]
                              ];
                          }
                      };
  
                      $existingEntry->setFieldValues([
                          'productDescription' => $product->body_html,
                          'productVariants' => $serializedMatrix,
                      ]);
  
                      $success = \Craft::$app->elements->saveElement($existingEntry);
  
                      if (!$success) {
                          \Craft::info("(Vendor Shopify plugin) Error Saving Product: " . $product->title . " " . $err, __METHOD__);
                      }
                  }
              }
          }
          \Craft::$app->getSession()->setNotice('Manual Sync complete.');
          return $this->renderTemplate('shopify/done');
      }
  }
  
  ```
  
  So far, I have explored options discussed in these two StackExchange questions that are somewhat related to my issue ([Call Craft controller with cron job](https://craftcms.stackexchange.com/questions/16233/call-craft-controller-with-cron-job) and [Craft TaskService Overview/Cron Job](https://craftcms.stackexchange.com/questions/12973/craft-taskservice-overview-cron-job)). Unfortunately my labors haven't been fruitful! I am open to any and all suggestions in regards to how I might accomplish the automation of calling this plugin class's actionIndex method. Thank you, I sincerely appreciate any feedback!
  
  **Tags:** plugin-development, craft3, plugins, cron
  
  
  ---
  
  ## 2 answers
  
  ### Answer by username_1 on 2019-02-12. Score: 4 | Accepted answer
  
  You could add console command in your plugin then use third party cron job package to run the console command, your console command will be responsible for calling index function. i had similar kind of requirement earlier, i accomplished this using third party cron job package which was dependency for my plugin to work.
  
  There is new craft 3 plugin call scheduler <https://github.com/supercool/scheduler>.
  
  I am not 100% sure that this idea will work on your side but just a heads up for you to get started.
  
  > **Comments:**
  > **username_0**: Thank you for the suggestion about console commands - this is what I ended up doing today and it worked perfectly. I will post another answer about this to follow up. I appreciate you taking the time to steer me in the right direction!  
  
  ---
  
  ### Answer by username_0 on 2019-02-12. Score: 2
  
  I took the route of adding a controller with methods that could be triggered as craft console commands, and then simply ran these commands via cron tasks.
  
  In the **init()** public method of the main plugin class, I added the console commands controller:
  
  ```
  public function init()
  {
     // ...
  
     // Add in our console commands
     if (Craft::$app instanceof ConsoleApplication) {
        $this->controllerNamespace = 'vendor\shopify\console\controllers';
     }
  
     // ...
  }
  
  ```
  
  Inside this namespace of console controllers above, I have a single controller named **CronCroller**, which extends *yii\console\Controller*:
  
  ```
  <?php
  
  namespace vendor\shopify\console\controllers;
  
  use vendor\shopify\Shopify;
  
  use Craft;
  use yii\console\Controller;
  use craft\elements\Entry;
  
  /**
   * Shopify plugin commands for cron automation
   *
   * The first line of this class docblock is displayed as the description
   * of the Console Command in ./craft help
   *
   * Craft can be invoked via commandline console by using the `./craft` command
   * from the project root.
   *
   * Console Commands are just controllers that are invoked to handle console
   * actions. The segment routing is plugin-name/controller-name/action-name
   *
   * The actionIndex() method is what is executed if no sub-commands are supplied.
   *
   * Actions must be in 'kebab-case' so actionDoSomething() maps to 'do-something'
   *
   */
  class CronController extends Controller
  {
      // Public Methods
      // =========================================================================
  
      /**
       * Run Shopify product Sync
       *
       * The first line of this method docblock is displayed as the description
       * of the Console Command in ./craft help
       *
       * @return mixed
       */
      public function actionSync()
      {
          echo "\nSyncing Craft products with Shopify.\n";
  
          // [Automated Task Processes]
  
          echo "\nSuccess.\n";
          return;
      }
  }
  
  ```
  
  Since the name of the controller is *CronController*, I can run the code in the **actionSync()** public method through the terminal with a command like:
  
  ```
  php craft shopify/cron/sync;
  
  ```
  
  [PluginFactory.io](https://pluginfactory.io/) is a terrific resource for exploring and learning more, which is how I set up my plugin's console commands.
  
  
  ---
  
  