Controllers

The framework’s controllers are extensions of the Ascmvc\Mvc\Controller or the Ascmvc\EventSourcing\AggregateRootController classes, which both implement the Ascmvc\AscmvcEventManagerListenerInterface interface. The AggregateRootController also implements the Ascmvc\EventSourcing\AggregateEventListenerInterface. Within the LightMVC Framework, controllers are considered to be the Aggregate Root (main command) of the each and every event sourcing aggregate.

Note

For more information on configuring an application’s event sourcing aggregates and the application’s event log, please see the Event Sourcing Configuration section.

Note

For more information on the framework’s event sourcing aggregates in general, please see the Event Sourcing section.

Controller Methods

Every controller has the following basic concrete definition:

class Controller extends AbstractController implements AscmvcEventManagerListenerInterface
{
    public function __construct(array $baseConfig)
    {
        $this->baseConfig = $baseConfig;

        $this->view = $this->baseConfig['view'];
    }

    public static function onBootstrap(AscmvcEvent $event)
    {
    }

    public function onDispatch(AscmvcEvent $event)
    {
    }

    public function onRender(AscmvcEvent $event)
    {
    }

    public function onFinish(AscmvcEvent $event)
    {
    }

    public function indexAction($vars = null)
    {
    }
}

Thus, every controller has an indexAction request handler by default, and every controller has the ability to tap into any of the framework’s major events, except the AscmvcEvent::EVENT_ROUTE event. Upon instantiation of the required controller by the controller manager (dispatcher), a minimal version of the application’s $baseConfig array will be injected into the controller. Upon execution of the controller’s request handler method, all the global server variables are injected into the handler through the $vars variable.

Note

One should avoid as much as possible to use the onBootstrap() method within the controller classes, as this would not scale very well if there is a large number of controllers.

For more information on the event manager and the main MVC events, please see the Event Manager section.

When extending the Ascmvc\EventSourcing\AggregateRootController class instead of the Ascmvc\Mvc\Controller class, a controller has the following additional concrete definition:

class AggregateRootController extends Controller
{
    /**
     * Contains the name of the Aggregate Root.
     *
     * @var string
     */
    protected $aggregateRootName;

    /**
     * Contains a list of listeners for this aggregate, where the key is the name of the event
     * and the value is the FQCN of the class that is to become a listener of the specified event.
     *
     * @var array
     */
    protected $aggregateListenerNames = [];

    public function __construct(array $baseConfig, EventDispatcher $eventDispatcher)
    {
        parent::__construct($baseConfig, $eventDispatcher);

        $this->aggregateRootName = static::class;

        $aggregateIdentifiers[] = $this->aggregateRootName;

        if (isset($baseConfig['eventlog']) && $baseConfig['eventlog']['enabled'] === true) {
            $aggregateIdentifiers[] = EventLogger::class;
        }

        $eventDispatcher->setIdentifiers($aggregateIdentifiers);

        if (!empty($this->aggregateListenerNames)) {
            foreach ($this->aggregateListenerNames as $key => $listenerName) {
                if (is_string($key) && is_string($listenerName)) {
                    $eventDispatcher->attach(
                        $key,
                        $listenerName::getInstance($eventDispatcher)
                    );
                }
            }
        }

        $sharedEventManager = $eventDispatcher->getSharedManager();

        if (!is_null($sharedEventManager)) {
            $sharedEventManager->attach(
                $this->aggregateRootName,
                '*',
                [$this, 'onAggregateEvent']
            );
        }
    }

    /**
     * Runs before the controller's default action.
     *
     * @param null $vars
     *
     * @return mixed|void
     */
    public function preIndexAction($vars = null)
    {
    }
}

Essentially, these additional facilities allow for automatic configuration of the Aggregate Root, by setting the name of the Aggregate Root, by setting the Event Dispatcher’s identifiers accordingly, by attaching all listeners found in the $aggregateListenerNames property to the specified events, and by attaching the Aggregate Root controller as a listener to all events dispatched within its own event sourcing aggregate. If event logging is enabled, it will also add the Event Logger’s Aggregate Root name as an aggregate identifier in order to dispatch all of the current aggregate’s events to this other aggregate. Adding more identifiers might also prove useful in case one needs to also dispatch all events from one aggregate to another.

Additionally, the ‘pre’ action methods allow for the dispatching of events before the actual call to the main action method. The naming convention for ‘pre’ methods is to capitalize the first letter of the name of the action method and to add the prefix ‘pre’ in front of the name. Thus, the indexAction() method would have a ‘pre’ action method with the name preIndexAction(). The ‘pre’ method has access to the same environment variables as the controller’s main request handler method, through the injection of the $vars variable.

Note

For further reading on the framework’s event sourcing aggregates in general, please see the Event Sourcing section.

Controller Factories

Any controller can implement the Ascmvc\AscmvcControllerFactoryInterface interface and become a factory that can store a factory of itself in the service manager (Pimple container) and/or return an instance of itself to the controller manager, after completing some specific logic.

This is useful if you need to set up some specific service or resource before injecting it into an instance of the controller, or if you need to customize the way the event sourcing aggregates and listeners are configured and set up in general by overriding the Ascmvc\EventSourcing\AggregateRootController class’ automatic configuration.

For a working example, please see the section on the LightMVC Skeleton Application.

For information on how to deal with other types of factories, please see the Service Manager section.