|
|
Now that you know the basics of how applications and modules are structured, we'll show you the easy way to get started.
The easiest way to get started is to grab the sample application and module repositories. This can be done in the following ways.
Simply clone the ZendSkeletonApplication
repository, using the --recursive option, which
will also grab ZF.
prompt> git clone --recursive git://github.com/zendframework/ZendSkeletonApplication.git my-application
Download a tarball of the
ZendSkeletonApplication repository:
Deflate the archive you selected and rename the parent directory according to your project needs; we use "my-application" throughout this document.
Install Zend Framework, and either have its library on your PHP include_path, symlink the library into your project's "library", or install it directly into your application using Pyrus.
By default, one module is provided with the
ZendSkeletonApplication, named
"Application". It provides the following:
A view listener that will render views based on the selected controller, and inject it into a site layout.
Typically, you will not need to touch this other than to provide an alternate entry page for your site and/or alternate error page.
Additional functionality will be provided by creating new modules.
To get you started with modules, we recommend using the
ZendSkeletonModule as a base. Download it from
here:
Zip: » https://github.com/zendframework/ZendSkeletonModule/zipball/master
Tarball: » https://github.com/zendframework/ZendSkeletonModule/tarball/master
Deflate the package, and rename the directory "ZendSkeletonModule" to reflect the name of the new module you want to create; when done, move the module into your new project's modules/ directory.
At this point, it's time to create some functionality.
Let's update the module class. We'll want to make sure the
namespace is correct, configuration is enabled and returned, and
that we setup autoloading on initialization. Since we're actively
working on this module, the class list will be in flux, we probably
want to be pretty lenient in our autoloading approach, so let's
keep it flexible by using the
StandardAutoloader. Let's begin.
First, let's have autoload_classmap.php return an empty array:
We'll also edit our config/module.config.php
file to read as follows:
return array(
'routes' => array(
),
'di' => array(
'instance' => array(
'alias' => array(
),
'Zend\View\TemplatePathStack' => array('parameters' => array(
'paths' => array(
'' => __DIR__ . '/../views',
),
)),
),
),
);
Fill in "module-name" with a lowercased, dash-separated
version of your module name -- e.g., "ZendUser" would
become "zend-user".
Next, edit the Module.php file to read as
follows:
namespace ;
use Zend\Module\Consumer\AutoloaderProvider;
class Module implements AutoloaderProvider
{
public function getAutoloaderConfig()
{
return array(
'Zend\Loader\ClassMapAutoloader' => array(
__DIR__ . '/autoload_classmap.php',
),
'Zend\Loader\StandardAutoloader' => array(
'namespaces' => array(
__NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
),
),
);
}
public function getConfig()
{
return include __DIR__ . '/config/module.config.php';
}
}
At this point, you now have your module configured properly. Let's
create a controller!
Controllers are simply objects that implement
Zend\Stdlib\Dispatchable. This means they simply
need to implement a dispatch() method that takes
minimally a Response object as an argument.
In practice, though, this would mean writing logic to branch based on matched routing within every controller. As such, we've created two base controller classes for you to start with:
Zend\Mvc\Controller\ActionController allows
routes to match an "action". When matched, a method named
after the action will be called by the controller. As an example,
if you had a route that returned "foo" for the
"action" key, the "fooAction" method would be
invoked.
Zend\Mvc\Controller\RestfulController
introspects the Request to determine what HTTP method was used, and
calls a method based on that accordingly.
GET will call either the getList() method, or, if an "id" was matched during routing, the get() method (with that identifer value).
POST will call the create() method, passing in the $_POST values.
PUT expects an "id" to be matched during routing, and will call the update() method, passing in the identifier, and any data found in the raw post body.
DELETE expects an "id" to be matched during routing, and will call the delete() method.
To get started, we'll simply create a "hello world" style controller, with a single action. First, create the directory src/<module name>/Controller, and then create the file HelloController.php inside it. Edit it in your favorite text editor or IDE, and insert the following contents:
\Controller;
use Zend\Mvc\Controller\ActionController;
class HelloController extends ActionController
{
public function worldAction()
{
$message = $this->getRequest()->query()->get('message', 'foo');
return array('message' => $message);
}
}
So, what are we doing here?
We're creating an action controller.
We're defining an action, "world".
We're pulling a message from the query parameters (yes, this is a superbly bad idea in production!).
We're returning an array of values that will get processed later.
You'll notice that we're not doing anything with the view. This is by design; the controller is simply an object martialling input for the model, and returning some data. This keeps a solid separation of concerns.
If you explore the ZendSkeletonApplication
repository, you'll find a file
module/Application/src/Application/View/Listener.php,
containing a class Application\View\Listener.
This listener aggregate is registered in the bootstrap (class
Application\Bootstrap in
module/Application/src/Application/Bootstrap.php),
and listens on the application's dispatch event
in order to first render an action's view script, and then inject
that into the layout. It uses the return value from actions to seed
the variables of the view prior to rendering.
So, with that in mind, let's create a view script.
Create the directory
views/<module name>-hello, where
<module name> is a lowercase,
dash-separated version of the module's namespace -- e.g., namespace
ZendUser would translate to
zend-user, and we'd create the directory
zend-user-hello.
Inside that directory, create a file named world.phtml. Inside that, paste in the following:
Greetings!
You said "$foo".
That's it. Save the file.
Note:
Those of you who are security conscious may be worried about the above view script, as it will appear that we have unfiltered input being returned. However, by default, the Zend class will return escaped variables; you will need to purposely request a raw value if you wish to use it. The above usage should be secure from XSS attacks.
Now that we have a controller and a view script, we need to create a route to it.
Note:
ZendSkeletonApplicationships with a "default route" that will likely get you to this action. That route basically expects "/{controller}/{action}", which allows you to specify this: "/zend-user-hello/world". We're going to create a route here mainly for illustration purposes, as creating explicit routes is a recommended practice.
Open your configs/module.config.php file, and modify it to add to the "routes" array so it reads as follows:
return array(
'routes' => array(
'-hello-world' => array(
'type' => `Zend\Mvc\Router\Http\Literal`,
'options' => array(
'route' => '/hello/world',
'defaults' => array(
'controller' => '-hello',
'action' => 'world',
),
),
),
),
// ... di configuration ...
);
As before, <module-name> should be the lowercased, dash-separated version of the module namespace.
We now have a route to our controller. However, how will it know which controller class to load? <module name>-hello is descriptive, but it's not the class name. The answer is via a dependency alias.
Remember the Locator we discussed when talking about the
Application class? Well, by default, controllers
are pulled from it. When your router returns matches, the
assumption is that we have a "controller" key in those
matches, and we use that to retrieve the controller class.
However, how does this work when we have descriptive names that aren't class names? Simple: aliasing.
Open your config/module.config.php file, and modify it to add to the DI configuration's "instance" array so it reads as follows:
return array(
// ... routes configuration ...
'di' => array(
'instance' => array(
'alias' => array(
'-hello' => '\Controller\HelloController',
),
'Zend\View\TemplatePathStack' => array('parameters' => array(
'paths' => array(
'' => __DIR__ . '/../views',
),
)),
),
),
);
The part to pay attention to above is the "alias" key and values within it. With that one configuration, we now can be assured that when our route is matched, we'll load this controller.
Okay, we're about ready to test!
Note:
While we've only create an alias here, it's important to note again that the controllers are pulled from the DI container by default. This is an incredibly powerful paradigm. What it means is that you can simply create setter methods for your controller dependencies, and rely on the DI container to inject them prior to use.
While the default controller implementations (the
ActionControllerandRestfulController) also implement the LocatorAware interface, which will inject the Locator (usually the DI container) attached to the Application into your controllers, we recommend not relying on this, and instead adding explicit dependencies via constructor or setter injection. This will make your code self-documenting and easier to debug.
One problem: we haven't told our application about our new module!
By default, modules are not parsed unless we tell the module manager about them. As such, we need to notify the application about them.
Remember the config/application.config.php file? Let's modify it to add our new module. Once done, it should read as follows:
array(
'Application',
'',
),
'module_listener_options' => array(
'module_paths' => array(
'./module',
'./vendor',
),
),
);
Replace <module namespace> with the namespace of your module.
Now we can test things out! Create a new vhost pointing its document root to the public directory of your application, and fire it up in a browser. You should see a simple page with these details:
Module: Application Controller: Index Action: index
Now alter the location in your URL to append the path "hello/world", and load the page. You should now get the following content:
Greetings!
You said "foo".
Now alter the location to append "?message=bar" and load the page. You should now get:
Greetings!
You said "bar".
Congratulations! You've created your first ZF2 MVC module!
|
|
Copyright © 2005-2011 Zend Technologies Inc (compiled by mikaelkael with ZFDocumentor - GIT c517eb0).

