简体   繁体   中英

How to use the InputFilter in a nested mapper model class in Zend Framework 2?

Everyone, who started ZF2 learning with the "Get started" tutorial, will know the model class Album (s. below).

Now I want to extend my model with songs. One album can have 0 or more songs. The songs will get a new talbe songs ( id , title , album_id ) and the mapper Album\\Model\\Song . The mapper Album\\Model\\Song will be built similar to Album\\Model\\Album . The mapper Album\\Model\\Album will get a new property songCollection (array of Album\\Model\\Song objects or maybe something like Album\\Model\\SongCollection object).

  • Does it make sence to use the InputFilter for "nested" (mapper) classes?
  • How should the getInputFilter() be modified?
  • How should the setInputFilter() be modified? OK, now it is not implemented at all. But it's approximately clear how to do it for a shallow class structure -- and not clear how to implement it for a mapper, that references another mapper(-s).

Album\\Model\\Album

<?php
namespace Album\Model;

use Zend\Stdlib\ArraySerializableInterface;

use Zend\InputFilter\Factory as InputFactory;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilterInterface;

class Album implements InputFilterAwareInterface, ArraySerializableInterface {

    public $id;
    public $artist;
    public $title;

    protected $inputFilter;

    public function exchangeArray(array $data) {
        $this->id     = (isset($data['id'])) ? $data['id'] : null;
        $this->artist = (isset($data['artist'])) ? $data['artist'] : null;
        $this->title  = (isset($data['title'])) ? $data['title'] : null;
    }

    public function toArray() {
        return $this->getArrayCopy();
    }

    public function getArrayCopy() {
        return get_object_vars($this);
    }

    public function setInputFilter(InputFilterInterface $inputFilter) {
        throw new \Exception('Not used');
    }

    public function getInputFilter() {
        if (!$this->inputFilter) {
            $inputFilter = new InputFilter();
            $factory = new InputFactory();

            $inputFilter->add($factory->createInput(array(
                'name' => 'id',
                'required' => true,
                'filters' => array(
                    array('name' => 'Int')
                )
            )));

            $inputFilter->add($factory->createInput(array(
                'name' => 'artist',
                'required' => true,
                'filters' => array(
                    array('name' => 'StripTags'),
                    array('name' => 'StringTrim')
                ),
                'validarots' => array(
                    array(
                        'name' => 'StringLength',
                        'options' => array(
                            'encoding' => 'UTF-8',
                            'min' => 1,
                            'max' => 100
                        )
                    )
                )
            )));

            $inputFilter->add($factory->createInput(array(
                'name' => 'title',
                'required' => true,
                'filters' => array(
                    array('name' => 'StripTags'),
                    array('name' => 'StringTrim')
                ),
                'validarots' => array(
                    array(
                        'name' => 'StringLength',
                        'options' => array(
                            'encoding' => 'UTF-8',
                            'min' => 1,
                            'max' => 100
                        )
                    )
                )
            )));

            $this->inputFilter = $inputFilter;
        }
        return $this->inputFilter;
    }

}

I think you are a little confused with the relationship with the models and mappers set out in this example.

The 'mappers' would be the TableGateway objects, such as AlbumTable, SongTable etc. The Album and Song classes yo would call models, or Domain Objects, these are what represent the actual entities in your application. The Mappers just take care of persisting them in your database etc.

When using the TableGateway implementation, I would let each Domain Object (such as Ablum) handle the InputFilter for the attributes it's TableGateway is going to persist (such as AlbumTable).

For the example you stated, I would not change the Album Models InputFilter at all. The reason is the relationship with Songs is this:

Album HAS many songs, Song Belongs to Album (the Song would have the link back to the Album)

Add a new Song Object and Gateway:

<?php
namespace Album\Model;

use Zend\Stdlib\ArraySerializableInterface;

use Zend\InputFilter\Factory as InputFactory;
use Zend\InputFilter\InputFilter;
use Zend\InputFilter\InputFilterAwareInterface;
use Zend\InputFilter\InputFilterInterface;

class Song implements InputFilterAwareInterface, ArraySerializableInterface {

    protected $id;
    protected $album;
    protected $title;

    protected $inputFilter;

    // Added Getters / Setters for the attributes rather than
    // having public scope ...

    public function setAlbum(Album $album)
    {
         $this->album = $album;
    }

    public function getAlbum()
    {
        return $this->album;
    }

    public function exchangeArray(array $data) {
        $this->id     = (isset($data['id'])) ? $data['id'] : null;
        $this->title  = (isset($data['title'])) ? $data['title'] : null;

        if(isset($data['album_id'])) {
             $album = new Album();
             $album->exchangeArray($data['album_id']);
             $this->setAlbum($album);
        }
    }

    public function toArray() {
        return $this->getArrayCopy();
    }

    public function getArrayCopy() {
        return array(
             'id'       => $this->id,
             'album_id' => $this->getAlbum()->id,
             'title'    => $this->title,
        );
    }

    public function setInputFilter(InputFilterInterface $inputFilter) {
        throw new \Exception('Not used');
    }

    public function getInputFilter() {
        if (!$this->inputFilter) {
            $inputFilter = new InputFilter();
            $factory = new InputFactory();

            $inputFilter->add($factory->createInput(array(
                'name' => 'id',
                'required' => true,
                'filters' => array(
                    array('name' => 'Int')
                )
            )));

            $inputFilter->add($factory->createInput(array(
                'name' => 'album_id',
                'required' => true,
                'filters' => array(
                    array('name' => 'Int')
                )
            )));

            $inputFilter->add($factory->createInput(array(
                'name' => 'title',
                'required' => true,
                'filters' => array(
                    array('name' => 'StripTags'),
                    array('name' => 'StringTrim')
                ),
                'validarots' => array(
                    array(
                        'name' => 'StringLength',
                        'options' => array(
                            'encoding' => 'UTF-8',
                            'min' => 1,
                            'max' => 100
                        )
                    )
                )
            )));

            $this->inputFilter = $inputFilter;
        }
        return $this->inputFilter;
    }
}

Notice no need to change the Album Model as the relationship is 'Song Belongs to Album'.

When you object relationships get more complex you will want to look at using Hydrators to build the objects for you ( http://framework.zend.com/manual/2.0/en/modules/zend.stdlib.hydrator.html )

Now you would create a SongTable to persist this new Object for you:

<?php
namespace Album\Model;

use Zend\Db\TableGateway\TableGateway;

class SongTable
{
    protected $tableGateway;

    public function __construct(TableGateway $tableGateway)
    {
        $this->tableGateway = $tableGateway;
    }

    public function fetchAll()
    {
        $resultSet = $this->tableGateway->select();
        return $resultSet;
    }

    public function getSong($id)
    {
        $id  = (int) $id;
        $rowset = $this->tableGateway->select(array('id' => $id));
        $row = $rowset->current();
        if (!$row) {
            throw new \Exception("Could not find row $id");
        }
        return $row;
    }

    public function saveSong(Song $song)
    {
        $data = array(
            'album_id' => $song->getAlbum()->id,
            'title'    => $song->title,
        );

        $id = (int)$song->id;
        if ($id == 0) {
            $this->tableGateway->insert($data);
        } else {
            if ($this->getSong($id)) {
                $this->tableGateway->update($data, array('id' => $id));
            } else {
                throw new \Exception('Form id does not exist');
            }
        }
    }

    public function fetchAlbumSongs(Album $album)
    {
        $resultSet = $this->tableGateway->select(array(
            'album_id' => $album->id
        ));

        return $resultSet;
    }

    public function addSongsToAlbum(Album $album)
    {
        foreach($this->fetchAlbumSongs($album) as $song) {
            $album->addSong($song);
        }
    }
}

You Could then Modify you Album model to allow Songs to be added:

class Album implements InputFilterAwareInterface, ArraySerializableInterface {

    // Other stuff here

    /**
     * @var array
     */
    protected $songs = array();

    public function addSong(Song $song)
    {
        $this->songs[] = $song;
    }

    public function getSongs()
    {
         return $this->songs;
    }
}

You can then build your object graph easily, I would usually make a server to do do this kind of thing:

AlbumService.php

public function getAlumbWithSongs(int $id)
{
    $album = $this->getAlbumTable()->getAlbum($id);

    if($album) {
        $this->getSongTable()->addSongsToAlbum($album);
    }

    return $album;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM