Zend Quick Start Guide - Help on understanding Data Mapper implementation

Hello all,

On the Zend Quick Start Guide that I’m more or less following, here: Zend Framework: Documentation: Create a Model and Database Table - Zend Framework Manual

They advice, among others, the following steps:

  1. build a data source class that will access our data source by using Table Data Gateway pattern)

  2. build a data mapper - that will connect our datasource to our model

  3. build a model

Let’s suppose our Table Name is Foo.

Inside this Data Mapper we have this method:

class Application_Model_FooMapper
{
    protected $_dbTable;

    public function setDbTable ($dbTable)
    {
      if (is_string($dbTable)) {
        $dbTable = new dbTable();
      }

      if (!$dbTable instanceof Zend_Db_Table_Abstract) {
        throw new Exception('Invalid Table Data Gateway Provided');
      }

      $this->_dbTable = $dbTable;

      return $this;
    }
}

What I’m not getting is:

If the Table Data Gateway tell us to have one class per Table, and if the Mapper should point our Data Source to our Model, we may suppose we should have a Data Mapper per table as well, correct ?

If we should have a Data Mapper per table, then I don’t understand why do we need, on the method above, to have $dbTable instead of $foo ? I know that we may have whatever name we want on our variables. I’m aware of that but, for some reason, the quick start guide author have choose a more broader name and I’m not understanding the logic.

Can we put other table names on this Mapper? If we can, then, the class name makes no sense…

Kind of lost here, and I hope someone could clarify this for me.

Thanks a lot.

The gateway provides a standard set of methods to perform generic update, insert and find actions for a single table that are compatible across vendors.

The mapper provides methods that specific to domain level operations. It may use the gateway to perform actions. Think of the gateway as a helper – it can be used to help the move data from the domain to the database via a standard interface.

Without the gateway you would essentially need to hard-code the queries, resulting in an application that is tightly coupled to a specific vendors db. Using the gateway provides a seamless of means of moving from one db to another and eliminating hard-coded queries. Without the mapper domain level mapping would be introduced into the controller and reduce reuse across the application.

The gateway is not a mapper because the they are two very different concepts. The gateway is a direct mapping to a table and mapper represents a domain level concept, not necessarily a 1:1 mapping with a table.

That is a misconception. I mean you can but its not always necessary. Create mappers based on the domain. For example, the look-up table in standard m:n relationship will not warrant a mapper or even a model.

A true data mapper is specific to domain goals. I think were many people get confused is with projects like phpdatamapper and that is actually more a active record than it is a data mapper. Yes, it “separates” the two layers, but at its basic structure it does nothing really except map a single table directly to a a class for the mapping and data object. All attempts to “automate” the domain end up behaving more like active records than anything else – at least without customization.

I would say yes, but the way it looks is that composition should be used ie. passing other data mapper needed to perform update across several tables.

Hello Oddz,

Why is delete excluded ?

By domain level, do you mean something that is not related to a specific object, but related to a domain of objects (sort of speak?). If I’m building a Dog Crud application, I can have a lot of objects there, but the domain level are those who are generally dog related?
I believe this isn’t that simple. :frowning:

So we should use a gateway on those applications that we know we may need at same point, to allow that application to work over several systems. If that’s not the case, and we just need to separate the code on a nice way to later retrieve and change the application under the same requirements then, a mapper should be enough ?

But we still can have a mapper to represent not “ALL” but “all that we want to use”, tables - yes ? I’m insisting here because I found really clean the fact that we have some model data (no mater on what level) to be organized by table (even if not all).

True. I was using one DAO/VO per table, and it’s been really nice to retrieve the data and quickly find what I need, but there as well, I add some m:n tables that didn’t fit. But that never concerned me, because I see this design patterns as principles that we should “naturally” follow but, not to be blinded ruled by them. Convention over configuration has been my friend (or excuse) on those cases.

So if we have a class of books.
Then we can add comments to those books.
And we can add authors to those books.

When we want to insert / update or find, a book, that mapper is the book mapper, BUT it should be able to deal with comments object and author object ?

Thanks a lot.

Delete is not excluded. I’m sure there is a method to delete rows as well create tables, modify tables, etc. The short list I provided was merely to provide a context for “action” – whatever that action might be.

Think of the domain as the mappers interface: method a signatures. The names of those methods are not going to be: SelectFromTableWhereX=Y, obviously. The names of those methods are going to based on specific goals that need to be achieved within the application such as; fetching all dogs of a certain breed or something.

The gateway is merely a helper Zend provides to build queries that are compatible across several database vendors. In theory its not “necessary” to use and there may be cases were it can’t be used: building complex queries. However, for straight forward operations it seems like it would be best to use it since that is what it is for.

Depends. Relationships are were things get a little tricky with the mapper pattern. Say your dealing with dogs and breeds. You could have a mapper w/ models for both dogs and breeds. Now lets say you wanted to get all dogs with their breeds. One way would be fetch all dogs first than call a method on the dog model to fetch its breed data. However, that would likely result in hitting the db once per dog to grab its breed (putting aside caching, another issue entirely). So a more optimized way to do that would be to join against the table in the initial query to grab all dogs and load the breed in a eager manor. In that case a single query will only be run vs. several based on number of dogs fetched, what is referred to as lazy loading. Using eager loading means that somewhere in the dog mapper the breed table is used.

Yeah, you could. I mean you could have a mapper named Book and have it manage several models. It depends on the situation really.

In all this you just need to remember that the goal is make the controller and view stupid. Stupid in the sense both need to know very little about the data model to carry out their own goals. Generally the less arguments a mapper method accepts the more specific the domain is and in-turn the more stupid controllers and views can be. The down-side of making a domain to specific is repeating similar code. However, the downside of having some type of generic findAll method that accepts a bunch of parameters is that the smarter views and controllers need to be.

Thanks a lot for the clarifications!! :slight_smile:

Zend Framework provides a “out of the box” gateway functionality, but it doesn’t provide a mapper one. I will try this gateway approach, and, in order to deal with cross database table data, I will create those mappers. Since those mappers seemed to need to extend (I believe) Zend_Db, this could be heavy if I have 200 mappers.
So, I will probably try to abstract a mapper, and with this, perhaps only one Zend_Db call is needed.

I’m not really understand what I’m saying :D, but at a first look, it makes some sense, anyway, while doing so, I will try to be careful regarding the rules we you have provided at the end, because they do make sense.

Thanks again for your time and expertise on those matters.

ps - I’ve just read here:
P of EAA: Table Data Gateway
(and it seems to be a nice reference among general opinion collected from the web) - And on this example, the author seems to point the gateway, not to a “domain specific level”, but on specific sql level.

Many developers aren’t comfortable with SQL, and many who are comfortable may not write it well. Database administrators need to be able to find SQL easily so they can figure out how to tune and evolve the database.
. Indeed. A Zend gateway can look like this:


class Application_Model_DbTable_Guestbook extends Zend_Db_Table_Abstract
{
    /** Table name */
    protected $_name    = 'guestbook';
}

It extends Zend_Db_Table_Abstract and it seems that we will never found SQL again.

I must well organize my code, but I also must allow myself (or one sql guru) to work on the application - the last seems to be impossible if we opt for such architecture.

I can’t find the middle term. :frowning:

Ok… after a while, and reading here and there, here’s my resume so far:

Table Data Gateway Objects – Those are object copies of our tables and they should contain table related generic queries. On Zend, we will use them to perform generic queries that will work across different database vendors via Zend_Db_Table_Abstract extension. What will those gateway objects do? They will connect (via an adapter) to our data source (ex: a MySQL database), on a generic (non-database-specific) way;

Data Mappers Objects - Those objects will work between our data source and our domain object models. They may, or may not, use the Gateway to have access to the data source. Their job is to, while referring NOT to a specific table BUT to a domain (that may need/have access to different tables), it provides a way to better organize the data and the related behaviour. On this Zend example, we will use the mapper to move data back and forward between Domain Objects and Gateway Objects;

If the above is correct, then I’m still missing this:

Domain Objects (a.k.a Business Objects) – Those objects … I don’t get here… what is their relation with the others ?

Can anyone give me a push ?