E-Mail Notifications Controller

Apex offers a built-in trigger based e-mail notification system so when certain actions are performed, e-mails will automaticallly go out to the appropriate recipients. Within terminal run the following opus email-controller CLI command:

apex opus email-controller Disaster/Controllers/DisasterNotificationController

Open the newly generated file at /src/Disaster/Controllers/DisasterNotificationController.php and replace it with the following contents:

<?php
declare(strict_types = 1);

namespace App\Disaster\Controllers;

use Apex\Svc\Convert;
use Apex\App\Sys\Utils\Hashes;
use Apex\App\Base\Web\Utils\FormBuilder;
use Apex\App\Interfaces\EmailNotificationControllerInterface;
use Apex\App\Interfaces\UserInterface;
use Apex\Mercury\Email\{EmailContact, EmailContactCollection};
use Apex\App\Base\Model\ModelIterator;
use App\Webapp\Models\EmailQueueRecipient;

/**
 * ~alias.title~ E-Mail Notification Controller
 */
class DisasterNotificationController implements EmailNotificationControllerInterface
{

    /**
     * The title of the e-mail notification controller.
     */
    public string $title = 'Disaster Relief Project Notifications';

    #[Inject(Convert::class)]
    private Convert $convert;

    #[Inject(Hashes::class)]
    private Hashes $hashes;


    /**
     * Get conditional form fields.
     *
     * These form fields are displayed when the administrator is creating a new 
     * e-mail notification with this controller, and allow the 
     * administrator to define the conditionals of the e-mail.
     */
    public function getConditionFormFields(FormBuilder $builder):array
    {

        // Generate form fields
        $fields = [
            'status' => $builder->select()->dataSource('hash.disaster.project_status')->label('Status')
        ];

        // Return
        return $fields;
    }

    /**
     * Get available senders
     *
     * When the administrator is creating an e-mail notification, they can select 
     * who appears as the sender.  If any senders are available aside from 'user' and 'admin', 
     * return them here in an associative array.  Otherwise, return null.
     */
    public function getAvailableSenders():?array
    {
        return null;
    }

    /**
     * Get available recipients
     *
     * When the administrator is creating an e-mail notification, they can select 
     * who is the recipient.  If any recipients are available aside from 'user' and 'admin', 
     * return them here in an associative array.  Otherwise, return null.
     */
    public function getAvailableRecipients():?array
    {
        return null;
    }

    /**
     * Get merge fields
     *
     * Define the additional merge fields available within this notification controller 
     * that may be used to personalize e-mail messages aside 
     * from standard user profile information.
     */
    public function getMergeFields():array
    {

        $fields = [];
        $fields['Project'] = [
            'project-id' => 'ID#',
            'project-status' => 'Status',
            'project-location' => 'Location',
            'project-created_at' => 'Date Created',
            'project-updated_at' => 'Last Updated',
            'project-title' => 'Title',
            'project-description' => 'Description'
        ];

        // Return
        return $fields;
    }

    /**
     * Get merge vars
     *
     * Obtain the personalized information to send an individual e-mail.
     * This should generate the necessary array of personalized information 
     * for all fields defined within the getMergeFields() method of this class.
     */
    public function getMergeVars(string $uuid, array $data = []):array
    {

        // Get project
        $project_id = $data['project_id'] ?? 0;
        if (!$proj = Project::whereId($project_id)) {
            throw new \Exception("No project exists with id# $project_id");
        }

        // Format vars
        $vars = $proj->toArray();
        $vars['status'] = $this->hashes->getVar('disaster.project_status', $vars['status']);
        $vars['created_at'] = $this->convert->date($vars['created_at'], true);
        $vars['updated_at'] = $vars['updated_at'] === null ? '' : $this->convert->date($row['updated_at'], true);

        // Get res
        $res = [];
        foreach ($vars as $key => $value) {
            $res['project-' . $key] = $value;
        }

        // Return
        return $res; 
    }

    /**
     * Get sender
     *
     * If additional senders are available other than 'admin' and 'user', 
     * This should return the sender name and e-mail address as a 
     * EmailContact object.  Otherwise, return null.
     */
    public function getSender(string $sender, ?UserInterface $user, array $data = []):?EmailContact
    {
        return null;
    }

    /**
     * Get recipient
     *
     * If additional recipients are available other than 'admin' and 'user', 
     * This should return the recipient name and e-mail address as a 
     * EmailContact object.  Otherwise, return null.
     */
    public function getRecipients(string $recipient, ?UserInterface $user, array $data = []):?EmailContactCollection
    {
        return null;
    }

    /**
     * Get reply-to
     *
     * If necessary, you can return the Reply-TO e-mail address of the message here.  
     * Otherwise, return null.
     */
    public function getReplyTo(array $data = []):?string
    {
        return null;
    }

    /**
     * Get cc
     *
     * If necessary, you can return the Cc e-mail address of the message here.  
     * Otherwise, return null.
     */
    public function getCc(array $data = []):?array
    {
        return null;
    }

    /**
     * Get bcc
     *
     * If necessary, you can return the bcc e-mail address of the message here.  
     * Otherwise, return null.
     */
    public function getBcc(array $data = []):?array
    {
        return null;
    }

    /**
     * Get broadcast iterator
     */
    public function getBroadcastIterator(int $offset, array $condition = []):?ModelIterator
    {
        return null;
    }

    /**
     * Get broadcast recipient
     */
    public function getBroadcastRecipient(object $model):?EmailQueueRecipient
    {
        return null;
    }


}

Once the above class is in place, when you visit the Settings->Notifications menu of the administration panel, within the select box when creating a new notification there will be a new "Disaster Relief Notifcations" option. Upon selecting the option and submitting the form, the next page will contain a status select box allowing the administrator to select the project status as to when the notification will be sent. Plus the merge vars select list also contains various project variables that the e-mail message can be personalized with.

For full information, please visit the Create E-Mail Notification Contrller page of the developer documentation.

Modify ProjectController.php

With the new e-mail notification controller in place, the project controller needs to be modified to send out e-mails when new projects are created. Open the file at /src/Disaster/Controllers/ProjectController.php, and at the top within the use declarations section add the line:

[data-line=9]
use App\Webapp\Notifications\Notifications;

Within the property declaration section where other injection attributes are, add the following injection attribute:

[data-line=23]
#[Inject(Notifications::class)]
private Notifications $notifications;

At the bottom of the create() method just before the return statement add the lines:

[data-line=40]
// Send notifications
$this->notifications->process(DisasterNotificationController::class, $this->app->getUuid(), ['status' => $this->app->post('status')]);

The entirety of the ProjectController.php file should now be:

<?php
declare(strict_types=1);

namespace App\Disaster\Controllers;

use Apex\Svc\{App, Db};
use App\Disaster\Models\Project;
use Apex\App\Base\Lists\CountryList;
use App\Webapp\Notifications\Notifications;

/**
 * Project controller
 */
class ProjectController
{

    #[Inject(App::class)]
    private App $app;

    #[Inject(Db::class)]
    private Db $db;

    #[Inject(Notifications::class)]
    private Notifications $notifications;

    /**
        * Create project
     */
    public function create():?Project
    {

        // Create project
        $proj = Project::insert([
            'location_id' => $this->app->post('location_id'),
            'status' => $this->app->post('status'),
            'title' => $this->app->post('title'),
            'description' => $this->app->post('description')
        ]);

        // Send notifications
        $this->notifications->process(DisasterNotificationController::class, $this->app->getUuid(), ['status' => $this->app->post('status')]);

        // Return
        return $proj;
    }

    /**
     * Create location options
     */
    public function createLocationOptions(string $selected = ''):string
    {

        // Go through locations
        $options = '';
        $rows = $this->db->query("SELECT id,country,city FROM disaster_locations WHERE is_active = %b ORDER BY id", true);
        foreach ($rows as $row) {
            $chk = $selected == $row['id'] ? 'selected="selected"' : '';
            $name = $row['city'] . ', ' . CountryList::$opt[$row['country']]['name'];
            $options .= "<option value=\"$row[id]\" $chk>$name</option>";
        }

        // Return
        return $options;
    }

}

Conclusion

That's it. Now every time a project is created any e-mail notifications created with its status will be automatically sent out. For full details on e-mail notification controllers, please consult the Create E-Mail Notification Controller or Send E-Mail pages of the developer documentation.