Previous Next

Quick Start

Now that you know the basics of how applications and modules are structured, we'll show you the easy way to get started.

Install the Zend Skeleton Application

The easiest way to get started is to grab the sample application and module repositories. This can be done in the following ways.

Using Git

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

Manual installation

Create a new module

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:

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.

Update the Module class

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!

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 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.

Create a route

Now that we have a controller and a view script, we need to create a route to it.

Note:

ZendSkeletonApplication ships 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.

Create a DI 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 ActionController and RestfulController) 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.

Tell the application about our module

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.

Test it out!

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!

Previous Next