Data Mapper Pattern: Complexe query from Service Layer
Asked Answered
B

2

10

I'm sing the Data Mapper Pattern in Zend Framework. This works well so far, but now I got to a point where I need your help/opinion. So let's start with the Code:

We got a table with several Persons:

CREATE TABLE `persons` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `age` int(3) NOT NULL,
  `haircolor` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id``),
);

Now I try to select all people, that have brown hair. I use the following method in the ServiceLayer

public function getPeopleByHaircolor($hair) {
  return $this->getMapper()->fetch('haircolor = ?', $hair);
}

The method in the Mapper looks like this:

public function fetch($condition, $value) {
  $resultSet = $this->getTable()->fetchAll($this->getTable()->select()->where($cond, $value));
  $entries = array();

  foreach($resultSet as $row) {
    $entry = new Default_Model_Person();
    $entry->id   = $row->id;
    $entry->name = $row->name;
    [...]
  }
  return $entries;
}

I think I follow the Data Mapper Pattern with this methods...


Now the Problem:

I want to select Persons, that have brown hair AND that are younger than 20 yrs. So how can I do that? My try:

public function getTeens($hair) {
  $rows = $this->getMapper()->fetch('haircolor = ?', $hair);
  $return = array();
  foreach($rows as $row) {
    if((int)$row->age < 20) $return[] = $row;
  }
  return $return;
}

But if you get more variables, like "people with brown hair, younger than 20yrs and with the name 'Foo Bar'", I need more and more methods and/or foreach loops.


My Question:

How would you do this in the Data Mapper Pattern? If I do a native SQL query like $serviceLayer->mapper->table->qry('SELECT ...'), is this breaking the Data Mapper Pattern? I don't like those additional foreach loops and it feels like I'm doing something wrong, so I wrote this question.

Barquentine answered 8/7, 2011 at 12:53 Comment(0)
T
7

Indeed, for two conditions, it inadvisable to query on one condition and then filter on your other while you iterate. It leaves you no clear approach for more than two conditions. And implementing pagination adapters becomes pretty messy.

Seems to me that the issue is that your mapper fetch() method supports permits only a single condition. So:

  1. Modify the signature to support an array of conditions.

  2. Alternatively, you could create a separate mapper method for each enhanced fetch method, ex: fetchByHairAndAge($hair, $age), etc.

Tithonus answered 8/7, 2011 at 14:33 Comment(2)
the 2nd options is preferable, because allowing to search for any conditions causes leaking of domain logic into controllers and requires knowledge of db structure outside of the class. 2nd option also clearly defines the API.Juttajutty
@Tomas: Indeed. Nicely explained. +1Tithonus
S
1

In my limited experience with data mappers I have found the following approach to work quite well for the scenarios I have encountered so far:

public function getPeopleByHaircolorAndAge($haircolor, $age, $limit=null, $offset=null)
{
    $people = new PersonCollection;
    $people->filterByHaircolor($haircolor);
    $people->filterByAge($age);
    $people->setLimit($limit);
    $people->setOffset($offset);

    $personCollectionMapper = new PersonCollectionMapper;
    $personCollectionMapper->fetch($people);

    return $people;
}

By instantiating the domain object first I have the option to set filters and other variables that the data mapper can read from the object when it is injected into the mapper.

To me this has been the superior approach so far compared to having multiple mapper methods that returns a domain object.

Saiff answered 24/12, 2013 at 8:34 Comment(1)
if i need add more condition,i will have to add a new get..... method,with many parameters,it is not well readable,have duplicate body.Dogeatdog

© 2022 - 2024 — McMap. All rights reserved.