|
|
Zend\Mvc is a brand new MVC implementation
designed from the ground up for Zend Framework 2.0. The focus of
this implementation is performance and flexibility.
The MVC layer is built on top of the following components:
Zend\Di, specifically its
Locator interface, but by default the dependency
injection container
Zend\EventManager
Zend\Http, specifically the request and response
objects, which are used with:
Zend\Stdlib\Dispatchable; all
"controllers" are simply dispatchable objects
Within the MVC layer, several subcomponents are exposed:
Zend\Mvc\Router contains classes pertaining to
routing a request (the act of matching a request to a controller,
or dispatchable)
Zend\Mvc\PhpEnvironment, a set of decorators for
the HTTP Request and Response
objects that ensure the request is injected with the current
environment (including query parameters, POST parameters, HTTP
headers, etc.)
Zend\Mvc\Controller, a set of abstract
"controller" classes with basic responsibilities such as
event wiring, action dispatching, etc.
The gateway to the MVC is the
Zend\Mvc\Application object (referred to simply
as Application from this point forward). Its
primary responsibilities are to route the
request, and to retrieve and dispatch the
controller discovered. Once accomplished, it returns a response,
which can then be sent.
The basic structure of an application is as follows:
application_root/
config/
application.config.php
data/
module/
vendor/
public/
.htaccess
index.php
The public/index.php performs the basic work of
martialling configuration and configuring the
Application. Once done, it
run()s the Application and
send()s the response returned.
The config directory will typically contain
configuration used by Zend\Module\Manager in
order to load modules and merge configuration; we will detail this
more later.
The vendor subdirectory should contain any third-party modules or libraries on which your application depends. This might include Zend Framework, custom libraries from your organization, or other third-party libraries from other projects. Libraries and modules placed in the vendor subdirectory should not be modified from their original, distributed state.
Finally, the module directory will contain one or more modules delivering your application's functionality.
Let's now turn to modules, as they are the basic units of a web application.
A module may contain just about anything: PHP code, including MVC
functionality; library code; view scripts; and/or or public assets
such as images, CSS, and JavaScript. The one requirement -- and
even this is optional -- is that a module acts as a PHP namespace
and that it contains a Module class under that
namespace. This class will then be consumed by
Zend\Module\Manager in order to perform a number
of tasks.
The recommended structure of a module is as follows:
module_root/
Module.php
autoload_classmap.php
autoload_function.php
autoload_register.php
config/
module.config.php
public/
images/
css/
js/
src/
<module_namespace>/
<code files>
tests/
phpunit.xml
bootstrap.php
<module_namespace>/
<test code files>
views/
<dir-named-after-a-controller/
<.phtml files>
Since a module acts as a namespace, the module root directory should be that namespace. Typically, this namespace will also include a vendor prefix of sorts. As an example a module centered around "User" functionality delivered by Zend might be named "ZendUser", and this is also what the module root directory will be named.
The Module.php file directly under the module root directory will be in the module namespace.
namespace ZendUser;
class Module
{
}
By default, if an init() method is defined, this
method will be triggered by a Zend\Module\Manager
listener when it loads the module class, and passed an instance of the
manager. This allows you to perform tasks such as setting up
module-specific event listeners. The init()
method is called for every module on
every page request and should
only be used for performing
lightweight tasks such as registering event
listeners.
The three autoload_*.php files are not required, but recommended. They provide the following:
autoload_classmap.php should return an array classmap of class name/filename pairs (with the filenames resolved via the __DIR__ magic constant).
autoload_function.php should return a PHP callback that can be passed to spl_autoload_register(). Typically, this callback should utilize the map returned by autoload_filemap.php.
autoload_register.php should register a PHP callback (typically that returned by autoload_function.php with spl_autoload_register().
The point of these three files is to provide reasonable default
mechanisms for autoloading the classes contained in the module,
thus providing a trivial way to consume the module without
requiring Zend\Module (e.g., for use outside a
ZF2 application).
The config directory should contain any
module-specific configuration. These files may be in any format
Zend\Config supports. We recommend naming the main
configuration "module.format", and for PHP-based configuration,
"module.config.php". Typically, you will create configuration for the
router as well as for the dependency injector.
The src directory should be a » PSR-0 compliant directory structure with your module's source code. Typically, you should at least have one subdirectory named after your module namespace; however, you can ship code from multiple namespaces if desired.
The tests directory should contain your unit tests. Typically, these will be written using » PHPUnit, and contain artifacts related to its configuration (e.g., phpunit.xml, bootstrap.php).
The public directory can be used for assets that you may want to expose in your application's document root. These might include images, CSS files, JavaScript files, etc. How these are exposed is left to the developer.
The views contains view scripts related to your controllers.
An Application composes several objects, but the
ones of particular interest to the developer are the Router and the
Locator. These will always need to be configured and injected in
order for the Application to run. Thus,
bootstrapping consists of configuring and injecting the router, as
well as the locator.
Zend Framework ships a default bootstrap implementation in
Zend\Mvc\Bootstrap. This class accepts an
instance of Zend\Config\Config to its constructor;
once you have an instance, you then call its
bootstrap() method, passing it the
Application instance. It will then configure your
locator (using Zend\Di\Di by default) and your
router (using Zend\Mvc\Router\Http\TreeRouteStack
by default).
Once those two tasks are accomplished, it then triggers a "bootstrap"
event on its attached EventManager instance. This
allows your modules to attach listeners, and thus perform additional,
module-specific, bootstrapping tasks (which might include registering
ACLs, setting up cache or log listeners, etc.).
Usage would then be as follows:
// Assuming $config is the merged config from all modules $bootstrap = new Bootstrap($config); $application = new Application(); $bootstrap->bootstrap($application);
At this point, your Application would be ready
to run:
$response = $application->run(); $response->send();
The run() method does basically four things:
It martials the Request object, ensuring that it
is fully configured based on the current request environment (this
includes injecting headers, query and POST parameters, and more).
It triggers a "route" event. By default, the
route() method of the
Application is registered as a listener, but you
can provide your own listeners to either replace it or intercept
before or after it executes.
It triggers a "dispatch" event. By default, the
dispatch() method of the
Application is registered as a listener, but you
can provide your own listeners to either replace it or intercept
before or after it executes.
It martials a Response object capable of sending
itself from the return values of the "dispatch" event.
You'll note that you have a great amount of control over the workflow. Using the EventManager's priority system, you can intercept the "route" and "dispatch" events anywhere during execution, allowing you to craft your own application workflows as needed.
While the previous approach largely works, where does the configuration come from? When we create a modular application, the assumption will be that it's from the modules themselves. How do we get that information and aggregate it, then?
The answer is via Zend\Module\Manager. This
component allows you to specify where modules exist, and it will
then locate each module and initialize it. If a module's
Module class has a
getConfig() method, it will retrieve its
configuration and merge it with the application configuration.
Sound complicated? It's not.
The first step is configuring the module manager. The easiest way to
do this is to utilize the provided
Zend\Module\Listener\DefaultListenerAggregate.
Then you simply inform the module manager which modules to load and
attach the listener aggregate.
Remember the application.config.php from earlier? We're going to provide some configuration.
array(
/* ... */
),
'module_listener_options' => array(
'module_paths' => array(
'./module',
'./vendor',
),
),
);
As we add modules to the system, we'll add items to the modules array.
Now, within our public/index.php, we can setup
the DefaultListenerAggregate:
use Zend\Module\Listener; $moduleConfig = include __DIR__ . '/../configs/application.config.php'; $listenerOptions = new Listener\ListenerOptions($moduleConfig['module_listener_options']); $defaultListeners = new Listener\DefaultListenerAggregate($listenerOptions);
Once we have that out of the way, we can instantiate our module manager:
use Zend\Module\Manager as ModuleManager;
$moduleManager = new ModuleManager(
$moduleConfig['modules']
);
$moduleManager->events()->attachAggregate($defaultListeners);
Each Module class that has configuration it wants
the Application to know about should define a
getConfig() method. That method should return
an array or Traversable object such as
Zend\Config\Config. As an example:
namespace ZendUser;
class Module
{
public function getConfig()
{
return include __DIR__ . '/config/module.config.php'
}
}
The ZF2 MVC layer is incredibly flexible, offering an opt-in, easy to create modular infrastructure, as well as the ability to craft your own application workflows via the EventManager system. Bootstrapping, while largely left to the developer, is straight-forward and provides a simple methodology for configuring the application and informing it of the various routes and services in play. The module manager is a lightweight and simple approach to enforcing a modular architecture that encourages clean separation of concerns and code re-use.
|
|
Copyright © 2005-2011 Zend Technologies Inc (compiled by mikaelkael with ZFDocumentor - GIT c517eb0).

