<p>Check out the example <a href="https://github.com/pixelandtonic/PowerNap" rel="nofollow">PowerNapTask</a> provided by Pixel &amp; Tonic — It implements a Task (and Subtask) with multiple steps. It should be fairly straightforward to modify that sample code to run your own custom logic.</p>

CC BY-SA 3.0
2016-04-05T21:47:38.117Z
I looked at the powernap plugin, but couldnt take that and turn it into what i needed, e.g. i have to send out an email to over a thousand users. Do i have the 1000 users loop within there being the email send in my run step with step set to 1?
BillyMedia
CC BY-SA 3.0
2016-04-05T21:49:34.940Z
And after each iteration, return true to trigger the next step?
BillyMedia
CC BY-SA 3.0
2016-04-05T21:57:24.873Z
@BillyMedia Yes, you would return true. You can structure it however you'd like.. Ideally, you would make every user a step within your task. That way you have some visual feedback as to how far along in the task process you are.
Chase Giunta
CC BY-SA 3.0
2016-04-05T22:00:37.190Z
If you're wanting to fire all these off at once in sequence (emails, query 3rd party api, etc.) you'll want to look into subtasks, also shown in the PowerNap plugin.
Chase Giunta
CC BY-SA 3.0
2016-04-05T22:52:48.097Z
Still a little confused as to how to send over 1000 emails? do I set getTotalSteps to a high number to allow the sending in chunks, then have a database query in runStep that sets a limit and an offset that sends out the emails?
BillyMedia
CC BY-SA 3.0
2016-08-02T09:42:31.800Z
You don't need steps, just create one task for each email.
MISC
CC BY-SA 3.0
2016-04-05T21:41:54.770Z
2016-04-05T21:41:54.770Z
Michael Rog
<p>I'm following Brandon's example here but am having trouble passing the variable that is set from the first subtask to the second subtask. Here's my code:</p>

<p><strong>Primary Task</strong></p>



<pre class="lang-php prettyprint-override"><code>&lt;?php
namespace Craft;

class Products_TestPrimaryTask extends BaseTask
{
    public $myVar;

    public function getDescription()
    {
        return 'Running Primary Task';
    }

    public function getTotalSteps()
    {
        return 2;
    }

    public function runStep($step)
    {
        switch ($step)
        {
            case 0:
            {
                return $this-&gt;runSubTask('Products_TestSub1', null, array(
                    'primaryTaskId' =&gt; $this-&gt;model-&gt;id
                ));
            }
            case 1:
            {
                return $this-&gt;runSubTask('Products_TestSub2', null, array(
                    'subVar' =&gt; $this-&gt;myVar,
                ));
            }
        }

        return true;
    }

    public function setPrimaryTaskVar($var)
    {
        $this-&gt;myVar = $var;

        ProductsPlugin::log('Variable in Primary Task: ' . print_r($this-&gt;myVar, true));
    }
}
</code></pre>

<p><strong>Sub Task 1</strong></p>

<pre class="lang-php prettyprint-override"><code>&lt;?php
namespace Craft;

class Products_TestSub1Task extends BaseTask
{
    protected function defineSettings()
    {
        return [
            'primaryTaskId' =&gt; AttributeType::Number
        ];
    }

    public function getDescription()
    {
        return 'Running Sub Task 1';
    }

    public function getTotalSteps()
    {
        return 1;
    }

    public function runStep($step)
    {
        $primaryTask = craft()-&gt;tasks-&gt;getTaskById($this-&gt;getSettings()-&gt;primaryTaskId);
        $primaryTask-&gt;getTaskType()-&gt;setPrimaryTaskVar('hello');

        return true;
    }
}
</code></pre>

<p><strong>Sub Task 2</strong></p>

<pre class="lang-php prettyprint-override"><code>&lt;?php
namespace Craft;

class Products_TestSub2Task extends BaseTask
{
    protected function defineSettings()
    {
        return [
            'subVar' =&gt; AttributeType::String
        ];
    }

    public function getDescription()
    {
        return 'Running Sub Task 2';
    }

    public function getTotalSteps()
    {
        return 1;
    }

    public function runStep($step)
    {
        ProductsPlugin::log('Variable in Sub Task 2: ' . print_r($this-&gt;getSettings()-&gt;subVar, true));

        return true;
    }
}
</code></pre>

<p>So Sub Task 1 calls the <code>setPrimaryTaskVar</code> method in the Primary Task, which sets the variable that then gets passed to Sub Task 2 in step 2 of the Primary Task. However, when I try to log that variable in Sub Task 2 it is showing as null.</p>

<p>Am I missing something obvious here?</p>

<p><strong>WORKAROUND</strong></p>

<p>After playing around with this a bit more, the only way I could persist the data between steps in the Primary Task was to save the variable in question to a SESSION var. So the Primary Task class has been amended to the following. Hope this helps someone else:</p>

<pre class="lang-php prettyprint-override"><code>&lt;?php
namespace Craft;

class Products_TestPrimaryTask extends BaseTask
{
    /**
     * Returns the default description for this task.
     *
     * @return string
     */
    public function getDescription()
    {
        return 'Running Primary Task';
    }

    /**
     * Gets the total number of steps for this task.
     *
     * @return int
     */
    public function getTotalSteps()
    {
        return 2;
    }

    /**
     * Runs a task step.
     *
     * @param int $step
     * @return bool
     */
    public function runStep($step)
    {
        switch ($step)
        {
            case 0:
            {
                return $this-&gt;runSubTask('Products_TestSub1', null, array(
                    'primaryTaskId' =&gt; $this-&gt;model-&gt;id
                ));
            }
            case 1:
            {
                return $this-&gt;runSubTask('Products_TestSub2', null, array(
                    'subVar' =&gt; $_SESSION['subVar']
                ));
            }
        }

        return true;
    }

    public function setPrimaryTaskVar($var)
    {
        $_SESSION['subVar'] = $var;

        ProductsPlugin::log('Variable in Primary Task: ' . print_r($_SESSION['subVar'], true));
    }
}
</code></pre>

CC BY-SA 3.0
2017-09-11T12:21:28.417Z
2017-09-11T13:03:00.853Z
2017-09-11T13:03:00.853Z
Paul Goodfield
<p>You can implement your entire Task with a single step if you want. The only point of breaking it into multiple steps is so the task progress bar has an idea of how far along the task is.</p>

<p>In your case, since your task is broken up into multiple unrelated processes, you might want to implement it with subtasks. So the top-level task only has a couple of steps based on the different high-level aspects of the task, and create separate task classes for those individual steps.</p>

<p>So let's say you have two main steps:</p>

<ul>
<li>call an external API</li>
<li>send emails</li>
</ul>

<p>and (I’m guessing, just to illustrate how this could work), let’s say that the email recipients are being determined by that API call, so you need a little bit of cross-communication between those processes.</p>

<p>Here’s the gist of how you’d set those classes up:</p>

<p><strong>Primary task class</strong></p>



<pre class="lang-php prettyprint-override"><code>class MyPlugin_PrimaryTask extends BaseTask
{
    private $_emails;

    public function getDescription()
    {
        return 'Blasting emails';
    }

    public function getTotalSteps()
    {
        return 2;
    }

    public function runStep($step)
    {
        switch ($step)
        {
            case 0:
            {
                return $this-&gt;runSubTask('MyPlugin_ApiTask', null, array(
                    'primaryTaskId' =&gt; $this-&gt;model-&gt;id
                ));
            }
            case 1:
            {
                return $this-&gt;runSubTask('MyPlugin_EmailTask', null, array(
                    'emails' =&gt; $this-&gt;_emails;
                ));
            }
        }
    }

    public function setEmails($emails)
    {
        $this-&gt;_emails = $emails;
    }
}
</code></pre>

<p><strong>API-calling task</strong></p>

<pre class="lang-php prettyprint-override"><code>class MyPlugin_ApiTask extends BaseTask
{
    private $_parentTask;
    private $_emails;

    public function getDescription()
    {
        return 'Calling the external API';
    }

    public function getTotalSteps()
    {
        return 1;
    }

    public function runStep($step)
    {
        // Call the API and get a list of emails
        // ...
        // $emails = ...

        // Pass the emails along to the primary task
        $primaryTask = craft()-&gt;tasks-&gt;getTaskById($this-&gt;getSettings()-&gt;primaryTaskId);
        $primaryTask-&gt;getTaskType()-&gt;setEmails($emails);

        return true;
    }

    protected function defineSettings()
    {
        return array(
            'primaryTaskId' =&gt; AttributeType::Number
        );
    }
}
</code></pre>

<p><strong>Email-sending task</strong></p>

<pre class="lang-php prettyprint-override"><code>class MyPlugin_EmailTask extends BaseTask
{
    public function getDescription()
    {
        return 'Sending the emails';
    }

    public function getTotalSteps()
    {
        return count($this-&gt;getSettings()-&gt;emails);
    }

    public function runStep($step)
    {
        $email = new EmailModel();
        $email-&gt;toEmail = $this-&gt;getSettings()-&gt;emails[$step];
        $email-&gt;subject = '...';
        $email-&gt;body = '...';
        craft()-&gt;email-&gt;sendEmail($email);

        return true;
    }

    protected function defineSettings()
    {
        return array(
            'emails' =&gt; AttributeType::Mixed
        );
    }
}
</code></pre>

CC BY-SA 3.0
2016-04-06T07:58:41.090Z
And taking my scenario, would i use sql limit and offset using the $step argument to break it into steps?
BillyMedia
CC BY-SA 3.0
2016-04-06T10:14:28.653Z
@BillyMedia Just updated my answer with an example of how you might set this up, based on your question.
Brandon Kelly
CC BY-SA 3.0
2016-04-05T23:31:52.707Z
2016-04-06T10:13:55.637Z
2016-04-06T10:13:55.637Z
Brandon Kelly
<p>I need to implement a bunch of background processes that do things like send emails, query 3rd pary api etc. </p>

<p>I have the task in place, but i dont fully understand how steps work, docs are a little thin on this, can someone explain how this works?</p>

<plugin-development><plugins>
Tasks and Steps
# Tasks and Steps

(Asked by: username_0 on 2016-04-05)

I need to implement a bunch of background processes that do things like send emails, query 3rd pary api etc.

I have the task in place, but i dont fully understand how steps work, docs are a little thin on this, can someone explain how this works?

**Tags:** plugin-development, plugins


---

## 3 answers

### Answer by username_5 on 2016-04-05. Score: 11 | Accepted answer

You can implement your entire Task with a single step if you want. The only point of breaking it into multiple steps is so the task progress bar has an idea of how far along the task is.
In your case, since your task is broken up into multiple unrelated processes, you might want to implement it with subtasks. So the top-level task only has a couple of steps based on the different high-level aspects of the task, and create separate task classes for those individual steps.
So let's say you have two main steps:

call an external API
send emails

and (I’m guessing, just to illustrate how this could work), let’s say that the email recipients are being determined by that API call, so you need a little bit of cross-communication between those processes.
Here’s the gist of how you’d set those classes up:
Primary task class
class MyPlugin_PrimaryTask extends BaseTask
{
    private $_emails;

    public function getDescription()
    {
        return 'Blasting emails';
    }

    public function getTotalSteps()
    {
        return 2;
    }

    public function runStep($step)
    {
        switch ($step)
        {
            case 0:
            {
                return $this->runSubTask('MyPlugin_ApiTask', null, array(
                    'primaryTaskId' => $this->model->id
                ));
            }
            case 1:
            {
                return $this->runSubTask('MyPlugin_EmailTask', null, array(
                    'emails' => $this->_emails;
                ));
            }
        }
    }

    public function setEmails($emails)
    {
        $this->_emails = $emails;
    }
}

API-calling task
class MyPlugin_ApiTask extends BaseTask
{
    private $_parentTask;
    private $_emails;

    public function getDescription()
    {
        return 'Calling the external API';
    }

    public function getTotalSteps()
    {
        return 1;
    }

    public function runStep($step)
    {
        // Call the API and get a list of emails
        // ...
        // $emails = ...

        // Pass the emails along to the primary task
        $primaryTask = craft()->tasks->getTaskById($this->getSettings()->primaryTaskId);
        $primaryTask->getTaskType()->setEmails($emails);

        return true;
    }

    protected function defineSettings()
    {
        return array(
            'primaryTaskId' => AttributeType::Number
        );
    }
}

Email-sending task
class MyPlugin_EmailTask extends BaseTask
{
    public function getDescription()
    {
        return 'Sending the emails';
    }

    public function getTotalSteps()
    {
        return count($this->getSettings()->emails);
    }

    public function runStep($step)
    {
        $email = new EmailModel();
        $email->toEmail = $this->getSettings()->emails[$step];
        $email->subject = '...';
        $email->body = '...';
        craft()->email->sendEmail($email);

        return true;
    }

    protected function defineSettings()
    {
        return array(
            'emails' => AttributeType::Mixed
        );
    }
}

> **Comments:**
> **username_0**: And taking my scenario, would i use sql limit and offset using the $step argument to break it into steps?  
> **username_5**: @username_0 Just updated my answer with an example of how you might set this up, based on your question.  

---

### Answer by username_1 on 2016-04-05. Score: 2

Check out the example [PowerNapTask](https://github.com/pixelandtonic/PowerNap) provided by Pixel & Tonic — It implements a Task (and Subtask) with multiple steps. It should be fairly straightforward to modify that sample code to run your own custom logic.

> **Comments:**
> **username_0**: I looked at the powernap plugin, but couldnt take that and turn it into what i needed, e.g. i have to send out an email to over a thousand users. Do i have the 1000 users loop within there being the email send in my run step with step set to 1?  
> **username_0**: And after each iteration, return true to trigger the next step?  
> **username_2**: @username_0 Yes, you would return true. You can structure it however you'd like.. Ideally, you would make every user a step within your task. That way you have some visual feedback as to how far along in the task process you are.  
> **username_2**: If you're wanting to fire all these off at once in sequence (emails, query 3rd party api, etc.) you'll want to look into subtasks, also shown in the PowerNap plugin.  
> **username_0**: Still a little confused as to how to send over 1000 emails? do I set getTotalSteps to a high number to allow the sending in chunks, then have a database query in runStep that sets a limit and an offset that sends out the emails?  
> **username_3**: You don't need steps, just create one task for each email.  

---

### Answer by username_4 on 2017-09-11. Score: 1

I'm following Brandon's example here but am having trouble passing the variable that is set from the first subtask to the second subtask. Here's my code:
Primary Task
runSubTask('Products_TestSub1', null, array(
                    'primaryTaskId' => $this->model->id
                ));
            }
            case 1:
            {
                return $this->runSubTask('Products_TestSub2', null, array(
                    'subVar' => $this->myVar,
                ));
            }
        }

        return true;
    }

    public function setPrimaryTaskVar($var)
    {
        $this->myVar = $var;

        ProductsPlugin::log('Variable in Primary Task: ' . print_r($this->myVar, true));
    }
}

Sub Task 1
 AttributeType::Number
        ];
    }

    public function getDescription()
    {
        return 'Running Sub Task 1';
    }

    public function getTotalSteps()
    {
        return 1;
    }

    public function runStep($step)
    {
        $primaryTask = craft()->tasks->getTaskById($this->getSettings()->primaryTaskId);
        $primaryTask->getTaskType()->setPrimaryTaskVar('hello');

        return true;
    }
}

Sub Task 2
 AttributeType::String
        ];
    }

    public function getDescription()
    {
        return 'Running Sub Task 2';
    }

    public function getTotalSteps()
    {
        return 1;
    }

    public function runStep($step)
    {
        ProductsPlugin::log('Variable in Sub Task 2: ' . print_r($this->getSettings()->subVar, true));

        return true;
    }
}

So Sub Task 1 calls the setPrimaryTaskVar method in the Primary Task, which sets the variable that then gets passed to Sub Task 2 in step 2 of the Primary Task. However, when I try to log that variable in Sub Task 2 it is showing as null.
Am I missing something obvious here?
WORKAROUND
After playing around with this a bit more, the only way I could persist the data between steps in the Primary Task was to save the variable in question to a SESSION var. So the Primary Task class has been amended to the following. Hope this helps someone else:
runSubTask('Products_TestSub1', null, array(
                    'primaryTaskId' => $this->model->id
                ));
            }
            case 1:
            {
                return $this->runSubTask('Products_TestSub2', null, array(
                    'subVar' => $_SESSION['subVar']
                ));
            }
        }

        return true;
    }

    public function setPrimaryTaskVar($var)
    {
        $_SESSION['subVar'] = $var;

        ProductsPlugin::log('Variable in Primary Task: ' . print_r($_SESSION['subVar'], true));
    }
}


---

