Streamflyer Streamflyer - 1 year ago 68
PHP Question

How to extend the ZF2 skeleton application - entities with foreign keys

My ZF2 skeleton application works fine as per the Zend user guide. But now I'm running in circles trying to extend the application so that an album's artist isn't a string anymore but a foreign key of the artist table in my db.

I've created all the necessary models, controllers, views to create, edit and view artists, which also works fine. But how do I combine the two so that my list of albums shows the artist's name?

Do I use a hydrator strategy as suggested here? If so, how do I implement it? The Zend\Stdlib\Hydrator\Strategy manual doesn't tell me where to register a hydrator strategy. Yes there is a short implementation code:

$foo = new Foo();

$hydrator = new ClassMethods();
$hydrator->addStrategy("foo", new Rot13Strategy());

But where does this go? In my AlbumController? And why keeps everybody saying hydrators are "mostly used for forms"?

Or isn't a hydrator what I'm looking for after all? Another thing I keep bumping into in my quest for a solution is dependency injection. Again I found this kind of tutorial, but in using classes A, B and C and not telling me where all those code fragments go it isn't much help grasping the concept.

I think what I want to do is pretty common, and I'm staggered that I can't find any real world tutorials. After hours of research I'm sure I'm very close to a break through - but I can't see it anymore and need a push. Thanks :-)

Answer Source

Hydrators are used when ever you need to get data in or out of an object/entity, which can be when using a form, or also when saving or loading an entity from storage.

I wouldn't use the Hydrator to populate your objects with related objects (e.g. for adding an antist object to your album). I would leave this upto your services / mappers.

Here's an example how I'd do it:


class Album
     // .. properties / getters/setters etc ..

     public function setArtist(ArtistInterface $artist)
         $this->artist = $artist;

     public function getArtist()
          return $this->artist;


this can vary, you would use getters/setters or properties, take a look at the base hydrators to decide which route you want to take..

Your Hydrator will take the Assembled objects, and give back the data you would need to either show a form, or save to persistence etc.

class AlbumHydrator // extends Zend\Stdlib\Hydrator\ClassMethods
    public function extract($object)
         return array(
             'artist_id'   => $object->getArtist()->getId(),
             'albumn_name' => $object->getAlbumName()

    // .. etc


use Zend\ServiceManager\ServiceManagerAwareInterface;
use Zend\ServiceManager\ServiceManager; 

class AlbumService  implements ServiceManagerAwareInterface
     * Get an album, we'll also load any related objects here
     * @param int
    public function find($id)
        $album = $this->_getAlbumMapper()->find($id);
        $artist = $this->_getArtistMapper()->findByAlbum($id);

        return $album;

     * Get the Mapper
     * @return  Application\Mappers\AlbumMapper
    protected function _getAlbumMapper()
        return $this->getServiceManager()

    // ..

You would setup your service in the Service Manager config like this:

'AlbumService' => function($sm) {
    $mapper = $sm->get('AlbumMapper');
    $service = new Application\Service\AlbumService();

    return $service;

You then use your service to create the object graph you need. You would have separate mappers for each of the entities, and the services can use the mappers to generate the objects you require.

You would usually have a Hydrator for each of the entities, and the mappers would use those to populate the objects for you. This would depend on your implementation, you may well be using TableGateway or something instead of the mappers, which would still work ok.

An example now of accessing your objects when inside your controller


public function albumAction()
    $id = (int) $this->params()->fromRoute('id', FALSE);
    if( ! $id) {
        // do something with errors etc ..
        return $this->redirect()->toRoute('default_route', array(
            'controller'    => 'index',
            'action'        => 'index'
    $request = $this->getRequest();

    $album = $this->getServiceLocator()->get('AlbumService')->find($id);
    $artistName = $album->getArtist()->getName();

    return new ViewMdoel(
       'album'      => $ablum,
       'artistName' => $artistName

Populating object graphs when using collections is a little more tricky, but I would tend to use lazy loading and virtual proxies for that kind of setup, which will basically defer loading the objects you need until you require them, check these out:

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download