-- Xavier Vidal <[email protected]> wrote
(on Monday, 11 May 2009, 10:40 AM +0200):
> Sorry, i've forgot to put an example of my data mapper.
>
> It's quite similar as the quickstart's mapper, but with static methods and
> some useful methods to fill an object from an array and another method to
> get the table.
Any time you introduce statics, you're basically introducing singletons
-- and singletons are a pain to (a) test against, and (b) extend.
While the quick start does not show it, my recommendation would be to
create an abstract base mapper class that provides the common
functionality you show below, and then extend that for each model that
needs a mapper. In such a situation, you would commonly have several
mappers in place -- and this is precisely the situation where a static
member would introduce problems.
> <?php
>
> class Citiers_Model_PeopleMapper
> {
> protected static $_table;
>
> protected static function getTable()
> {
> if(self::$_table == null) {
> self::$_table = new Citiers_Table_People;
> }
>
> return self::$_table;
> }
>
> protected static function fill(array $data)
> {
> $p = new Citiers_Model_People;
>
> $p->setId($data['id']);
> $p->setFirstname($data['firstname']);
> $p->setActive($data['active']);
> $p->setEmail($data['email']);
>
> return $p;
> }
>
> public static function getById($id)
> {
> $id = intval($id);
>
> $rs = self::getTable()->find($id);
>
> if($rs->count() != 1) {
> throw new Exception("Cannot find people#{$id}");
> }
>
> return self::fill($rs->current()->toArray());
> }
>
> public static function getActivePeople()
> {
> $rs = self::getTable()->fetchAll('active = 1');
>
> $items = array();
>
> foreach($rs as $row) {
> $items[] = self::fill($row->toArray());
> }
>
> return $items;
> }
>
> public static function getByEmail($email)
> {
> $rs =
> self::getTable()->fetchAll($tPeople->getAdapter()->quoteInto('email = ?',
> $email));
>
> if($rs->count() != 1) {
> throw new Exception("Cannot find people by email: {$email}");
> }
>
> return self::fill($rs->current()->toArray());
> }
>
> public static function save(Citiers_Model_People $people)
> {
> $tPeople = self::getTable();
>
> $data['firstname'] = $people->getFirstname();
> $data['email'] = $people->getEmail();
> $data['active'] = $people->isActive();
>
> if($people->getId() > 0) {
> $where = $tPeople->getAdapter()->quoteInto('id = ?',
> $people->getId());
> $tPeople->update($data, $where);
> } else {
> $people->setId($tPeople->insert($data));
> }
> }
>
> public static function delete($id)
> {
> $where = self::getTable()->getAdapter()->quoteInto('id = ?',
> intval($id));
>
> $tPeople->delete($where);
> }
>
> // ETC ETC ETC...
> }
>
> -----Mensaje original-----
> De: Matthew Weier O'Phinney [mailto:[email protected]]
> Enviado el: viernes, 08 de mayo de 2009 20:35
> Para: [email protected]
> Asunto: Re: [fw-general] Zend Model and quickstart
>
> -- remataklan <[email protected]> wrote (on Friday, 08 May 2009, 05:36 AM
> -0700):
> > I was trying to understand the quickstart guide on the ZF site.
> >
> > My question is about the model part.
> > First of all a little check to see if I understand the stuff. As I
> > understand Zend_Db_Table handles the stuff with database. The mapper
> > maps this to the model and the model handles everything about getting
> > and setting data. Is this correct?
>
> That's how we setup the quick start, yes. It's using three patterns:
>
> * Domain Model (the Default_Model_Guestbook class)
> * Data Mapper (the Default_Model_GuestbookMapper class)
> * Table Data Gateway (the Default_Model_DbTable_Guestbook class)
>
> One slight difference to what you wrote: the domain model is an object that
> has properties and behavior. The data mapper maps those to a persistence
> layer (the database, in this case).
>
> > Now the question, is it me or in the end the data we passed to the
> > view is a little bit large. I mean the whole model object is passed to
> > the final view. Is this the correct way to do it?
>
> It's one way to do it. When it comes down to it, the size of an item passed
> to the view is not really an issue -- and, in fact, passing objects is
> typically less expensive than other data as the objects are passed by
> reference.
>
> > And finally if I wanted to find to all posts from a certain user do I
> > need a new function in the mapper? Something like
>
> So, another person responded already, and he was suggesting using
> Zend_Db_Table as your model.
>
> This is a perfectly valid approach -- Martin Fowler notes in his section on
> Domain Models that it's often expedient to use ActiveRecord or table data
> gateway directly as your models, particularly for prototyping or when the
> model has a basically 1:1 mapping to the database.
>
> Problems arise, however, when you decide you want to persist your data
> differently. For instance, if you decide to move to a document storage
> system, or offload the direct data access to a middle-ware tier accessed via
> web services, or even want to abstract access so that you can use memcached
> unless the data is not cached. It was for these reasons that we chose to go
> with a slightly more abstract approach in the current iteration of the Quick
> Start.
>
> So, back to your example; that's one way to do it. The drawback is you'll
> find yourself writing methods in your mapper for every behaviour in your
> model, which quickly eliminates some of the benefits of having a data mapper
> in the first place.
>
> Another approach is to build and use a Query Object
> (http://www.martinfowler.com/eaaCatalog/queryObject.html), and then have the
> data mapper figure out how to map the query object into a request for the
> data access layer. In such a case, you might do something like
> this:
>
> public function query(Model_QueryObject $query)
> {
> $table = $this->getDbTable();
> $select = $table->select();
>
> foreach ($query->getCriteria() as $criteria) {
> $method = 'where';
> if ($criteria->isOr()) {
> $method = 'orWhere';
> }
> $statement = $criteria->getField() . ' '
> . $criteria->getOperator() . ' ?';
> $select->$method($statement, $criteria->getValue());
> }
>
> $resultSet = $table->fetchAll($select);
> $entries = array();
> foreach ($resultSet as $row) {
> $entry = new Model_List($row->toArray());
> $entry->setMapper($this);
> $entries[] = $entry;
> }
> return $entries;
> }
>
> This affords you the ability to have a fairly generic data mapper that can
> handle arbitrary criteria. It's not quite as flexible as Zend_Db_Select (no
> support for joins, order, having, etc.), but for most purposes it would be
> adequate.
>
> Your model, then, would have a postsByUser() method, and would look
> something like this:
>
> public function postsByUser($userId)
> {
> $query = new Model_QueryObject();
> $query->addCriteria(array(
> 'field' => 'user_id',
> 'operator' => '=',
> 'value' => $userId,
> ));
> return $this->getMapper()->query($query);
> }
>
> I won't go into the query object definition for now -- but it's basically
> just able to add criteria objects and return them. The criteria objects
> themselves simply are value objects, with properties for field, operator,
> and value.
>
> > public function postsByUser($userId)
> > {
> > $resultSet =
> > $this->getDbTable()->fetchAll($this->getDbTable()->select()->where('us
> > er_id
> > = ?', $userId));
> >
> > $entries = array();
> > foreach ($resultSet as $row) {
> > $entry = new Model_List();
> > $entry->setId($row->id)
> > ->setEmail($row->email)
> > ->setComment($row->comment)
> > ->setCreated($row->created)
> > ->setMapper($this);
> >
> > $entries[] = $entry;
> > }
> > return $entries;
> > }
> >
> > or is there better way.
> >
> >
> > --
> > View this message in context:
> > http://www.nabble.com/Zend-Model-and-quickstart-tp23445168p23445168.ht
> > ml Sent from the Zend Framework mailing list archive at Nabble.com.
> >
>
> --
> Matthew Weier O'Phinney
> Project Lead | [email protected]
> Zend Framework | http://framework.zend.com/
>
--
Matthew Weier O'Phinney
Project Lead | [email protected]
Zend Framework | http://framework.zend.com/