Are TableGateways too hard to implement in Zend Framework 2? Are they too hard to justify the effort? That's what I was asked recently during a Twitter conversation. For me, they're not. For me, they're well worth the effort. So I've written this post to show why they're not, and how they bring great flexibility, when implemented correctly.
Here's how it all started.
@zwelibanzi360: TableGateway pattern is tedious. I just pass an adapter to a model class and use Db\Select #zf2 @maltblue, your thoughts…
In a follow up tweet he wrote:
3 things 4 TableGateway:
- TableGateway class
- class implementing exchangeArray
- Configure TableGateway via ServiceManager
In the conversation which ensued, zwelibanzi said he felt that, for the size of his project, it was overkill yet was curious nonetheless about what others, myself and Samsonasik thought. So in this follow up to a recent TableGateway post here on Malt Blue, I'll set out why they can be a good solution, as well as why not.
TableGateways, as the name implies, stand between a table and your application. To quote Martin Fowler:
I find that Table Data Gateway is probably the simplest database interface pattern to use as it maps so nicely onto a database table or record type. It also makes a natural point to encapsulate the precise access logic of the data source.
Patterns of Enterprise Application Architecture - Martin Fowler, pages 2793, 2794.
I'm sure all of us, who've been around PHP long enough, remember a time when a PHP application was simply a collection of files, interspersed with a mixture of PHP and HTML.
Our applications started out modestly, consisting of only a few .php or .inc files. Naturally, you could argue there was no harm in having direct database calls in the files. There's not too many, they're easy to maintain. Yeah, ok, granted.
But then the complexity grew, and more calls were added in more files. Potentially you started to see duplicate, or near duplicate, code appearing. Now it begins to get harder to maintain.
You may have been astute, or organised enough, to collate all of these calls into a single include file. You may have built a class which encapsulated them even. If you had, you'd have had the basis of a TableGateway. Through this central point, if you enforced its usage, without knowing the pattern, you were already using it.
If however, this collation was avoided, or missed, the code would, in time, become a tangled, near unworkable, mess. If you disagree, try doing the following with your application:
- Migrate to another vendor
- Support multiple vendors (PostgreSQL, Oracle, MSSQL Server, MySQL, SQLite)
- Have reads from one database and writes to another, transparently
- Enforce DRY
- Stay on top of potential bugs
Implementing Them in Zend Framework
As mentioned earlier, there are 3 steps for implementing them in Zend Framework. Let's look at an example of how to do so, in the following 3 gists.
In the gists, we have 2 classes, one a TableGateway, the other a Model. The TableGateway class contains table-aware logic, such as queries to fetch all and fetch by specific criteria. The model class is table agnostic. It only knows about the properties that it needs data for.
Then, we have the module configuration, which binds the two together. Firstly, we have a TableGateway configuration, so we know which table we're binding to the TableGateway. Then we have the table configuration. I've taken this configuration further by using a HydratingResultset object. What this does, is to also implement the RowGateway pattern.
What it does is use our model class for every record in the resultset. This makes it that much simpler to work with the records retrieved. You'll notice a very simple mapper too. It's not the greatest implementation in the world - one of the first ones that I wrote and I'm phasing out.
But, what it does is allow for automatic persistence of record information in our model class, mapping from table columns to model properties. As you can see, that is a bit of code, just to interact with a table. You need to be discerning in your needs assessment, both now and into the future.
Ask yourself the following two questions:
- What if the amount of tables, and views, grows. How many classes will you need to build and maintain?
- Is your application really bit enough to warrant this level of effort?
But before you get too concerned, I'd suggest that past a point you'd either have a more sophisticated solution, which is beyond the scope of this post.
Some More Advanced Cases
If you do choose this path though, here's two potential advantages to think about:
- Access views instead of tables, and base that access on user privileges. Now you reuse the same objects, and the same calls, but reflect the logged in user. This helps keep code to a minimum.
- Have a common set of base classes which store a lot of the common functionality, such as findById, creating, updating and deleting records. Then build on them in other classes
- Reuse the common base class by passing in different TableGateway configurations, thereby minimising code creation
These are just a few examples, I'm sure you can think of more. But the show how taking this approach opens up a raft of potential. For me, in all but the most trivial of cases, a TableGateway is a good choice to make, based on the flexibility it brings. I hope that through this short post, you can see why implementing a TableGateway can make development more efficient.
Yes, for small projects it can be, or seem like, overkill. But as project complexity grows, as it invariably does, you won't miss the time not spent maintaining your application.
What other ways could you use a TableGateway? Share your thoughts in the comment.
Want to Master Zend\Db\Sql?
I've created a course all about taking you from beginner to near guru with Zend\Db\Sql, called the Zend\Db\Sql Deep Dive. You'll learn how to generate SQL for manipulating schemas and extracting data in an object-oriented, nearly hassle-free manner using the Zend\Db library. If you're ready to improve your game, this course is for you!