<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title type="text">Blog entries tagged mvc :: mwop.net</title>
  <updated>2014-06-09T12:00:00-05:00</updated>
  <generator uri="https://getlaminas.org" version="2">Laminas_Feed_Writer</generator>
  <link rel="alternate" type="text/html" href="https://mwop.net/blog/tag/mvc"/>
  <link rel="self" type="application/atom+xml" href="https://mwop.net/blog/tag/mvc/atom.xml"/>
  <id>https://mwop.net/blog/tag/mvc</id>
  <entry xmlns:xhtml="http://www.w3.org/1999/xhtml">
    <title type="html"><![CDATA[Better Understanding Controllers Through Basic Patterns]]></title>
    <published>2014-06-09T12:00:00-05:00</published>
    <updated>2014-06-09T12:00:00-05:00</updated>
    <link rel="alternate" type="text/html" href="https://mwop.net/blog/2014-06-09-controllers-as-facades.html"/>
    <id>https://mwop.net/blog/2014-06-09-controllers-as-facades.html</id>
    <author>
      <name>Matthew Weier O'Phinney</name>
      <email>contact@mwop.net</email>
      <uri>https://mwop.net</uri>
    </author>
    <content xmlns:xhtml="http://www.w3.org/1999/xhtml" type="xhtml">
      <xhtml:div xmlns:xhtml="http://www.w3.org/1999/xhtml"><xhtml:p><xhtml:a href="http://paul-m-jones.com/">Paul M. Jones</xhtml:a> has started
an interesting discussion rethinking the <xhtml:a href="http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller">
MVC</xhtml:a> pattern as applied to the web, which he has dubbed <xhtml:a href="https://github.com/pmjones/mvc-refinement">Action-Domain-Responder
(ADR)</xhtml:a>. If you haven't given it a read yet, click the link and
do that; this page will still be sitting here waiting when you
return.</xhtml:p>
<xhtml:p>I agree with a ton of it — heck, I've contributed to it a fair
bit via conversations with Paul. But there's been one thing nagging
at me for a bit now, and I was finally able to put it into words
recently.</xhtml:p>
<xhtml:p>Controllers — Actions in ADR — can be explained as
<xhtml:em>facades</xhtml:em>.</xhtml:p>
<xhtml:h2>Definitions</xhtml:h2>
<xhtml:p>The design pattern <xhtml:em>Facade</xhtml:em> originates in the book
"Design Patterns: Elements of Reusable Object Oriented Software,"
written by Erich Gamma, Ralph Johnson, Richard Helm, and John
Vlissides. Over the years, that book has come to be referred to as
the "Gang of Four", referring to the four authors, and often
abbreviated as "GoF".</xhtml:p>
<xhtml:p>The Facade pattern itself is one of the seven structural design
patterns defined in the GoF. Structural design patterns are those
that provide a mechanism for defining the <xhtml:em>relationships</xhtml:em>
between classes or objects in a system. Specifically:</xhtml:p>
<xhtml:blockquote>
<xhtml:p>Facade defines a simplifed interface to a complex system.</xhtml:p>
</xhtml:blockquote>
<xhtml:p><xhtml:a href="http://en.wikipedia.org/wiki/Facade_pattern">Wikipedia
has a general entry on the pattern</xhtml:a> as well, and provides some
other general characteristics of a Facade:</xhtml:p>
<xhtml:ul>
<xhtml:li>A Facade creates a convenience method around a set of
operations, thus reducing the complexity of operations.</xhtml:li>
<xhtml:li>A Facade reduces the immediate dependencies of the calling code
(they call the Facade, not the underlying code).</xhtml:li>
</xhtml:ul>
<xhtml:h2>Facade Example</xhtml:h2>
<xhtml:p>As an example, let's consider the following workflow:</xhtml:p>
<xhtml:ul>
<xhtml:li>Marshal some objects</xhtml:li>
<xhtml:li>Munge some incoming data</xhtml:li>
<xhtml:li>Call a validator</xhtml:li>
<xhtml:li>If the data does not validate, raise an error</xhtml:li>
<xhtml:li>Start a transaction</xhtml:li>
<xhtml:li>Pass data to several different tables</xhtml:li>
<xhtml:li>Commit the transaction</xhtml:li>
<xhtml:li>Log the changes</xhtml:li>
<xhtml:li>Email notifications</xhtml:li>
</xhtml:ul>
<xhtml:p>Now, we could just write the code:</xhtml:p>
<xhtml:pre><xhtml:code class="language-php hljs php" data-lang="php">$db     = <xhtml:span class="hljs-keyword">new</xhtml:span> Db($connectionConfig);
$log    = <xhtml:span class="hljs-keyword">new</xhtml:span> Logger($loggerConfig);
$mailer = <xhtml:span class="hljs-keyword">new</xhtml:span> Mailer($mailerConfig);
$data   = array_merge_recursive($_POST, $_FILES);

$inputFilter = <xhtml:span class="hljs-keyword">new</xhtml:span> InputFilter();
$inputFilter-&gt;setData($data);
<xhtml:span class="hljs-keyword">if</xhtml:span> (! $inputFilter-&gt;isValid()) {
    <xhtml:span class="hljs-keyword">throw</xhtml:span> <xhtml:span class="hljs-keyword">new</xhtml:span> DomainException();
}

$db-&gt;transactionStart();
$db-&gt;insertInto(<xhtml:span class="hljs-comment">/* ... */</xhtml:span>);
$db-&gt;insertInto(<xhtml:span class="hljs-comment">/* ... */</xhtml:span>);
$db-&gt;insertInto(<xhtml:span class="hljs-comment">/* ... */</xhtml:span>);
$db-&gt;transactionStop();

$log-&gt;info(<xhtml:span class="hljs-string">'Finished a transaction'</xhtml:span>);
$mailer-&gt;send(<xhtml:span class="hljs-string">'New transaction'</xhtml:span>)
</xhtml:code></xhtml:pre>
<xhtml:p>Straight-forward. But imagine if you needed to do this more than
once. Or if you wanted to re-use this logic in multiple places in
your application. This is a situation just waiting to go
out-of-sync — and one where developers will come to rely on
cut-and-paste for doing it correctly.</xhtml:p>
<xhtml:p>A facade would wrap this logic:</xhtml:p>
<xhtml:pre><xhtml:code class="language-php hljs php" data-lang="php"><xhtml:span class="hljs-class"><xhtml:span class="hljs-keyword">class</xhtml:span> <xhtml:span class="hljs-title">DataTransaction</xhtml:span>
</xhtml:span>{
    <xhtml:span class="hljs-keyword">protected</xhtml:span> $db;
    <xhtml:span class="hljs-keyword">protected</xhtml:span> $logger;
    <xhtml:span class="hljs-keyword">protected</xhtml:span> $mailer;

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">__construct</xhtml:span><xhtml:span class="hljs-params">(Db $db, Logger $logger, Mailer $mailer)</xhtml:span>
    </xhtml:span>{
        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;db     = $db;
        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;logger = $logger;
        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;mailer = $mailer;
    }

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">execute</xhtml:span><xhtml:span class="hljs-params">($data)</xhtml:span>
    </xhtml:span>{
        $inputFilter = <xhtml:span class="hljs-keyword">new</xhtml:span> InputFilter();
        $inputFilter-&gt;setData($data);
        <xhtml:span class="hljs-keyword">if</xhtml:span> (! $inputFilter-&gt;isValid()) {
            <xhtml:span class="hljs-keyword">throw</xhtml:span> <xhtml:span class="hljs-keyword">new</xhtml:span> DomainException();
        }

        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;db-&gt;transactionStart();
        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;db-&gt;insertInto(<xhtml:span class="hljs-comment">/* ... */</xhtml:span>);
        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;db-&gt;insertInto(<xhtml:span class="hljs-comment">/* ... */</xhtml:span>);
        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;db-&gt;insertInto(<xhtml:span class="hljs-comment">/* ... */</xhtml:span>);
        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;db-&gt;transactionStop();

        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;logger-&gt;info(<xhtml:span class="hljs-string">'Finished a transaction'</xhtml:span>);
        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;mailer-&gt;send(<xhtml:span class="hljs-string">'New transaction'</xhtml:span>)
    }
}
</xhtml:code></xhtml:pre>
<xhtml:p>You would then instantiate the facade — likely using an <xhtml:a href="http://en.wikipedia.org/wiki/Inversion_of_control">Inversion of
Control</xhtml:a> container to inject the dependencies — and then invoke
it:</xhtml:p>
<xhtml:pre><xhtml:code class="language-php hljs php" data-lang="php">$dataTransaction-&gt;execute(array_merge_recursive($_POST, $_FILES));
</xhtml:code></xhtml:pre>
<xhtml:p>This code fulfills the Facade pattern: we're no longer directly
manipulating dependencies, and we've simplified a complex set of
operations to a single, unified API.</xhtml:p>
<xhtml:h2>Controllers and Actions</xhtml:h2>
<xhtml:p>Hopefully you can see where I'm going with this.</xhtml:p>
<xhtml:blockquote>
<xhtml:p>Controllers in MVC, and Actions in ADR, are best characterized
as Facades.</xhtml:p>
</xhtml:blockquote>
<xhtml:p>You can define Controllers or Actions as Facades for the
following operations:</xhtml:p>
<xhtml:ul>
<xhtml:li>Marshaling arguments from the request.</xhtml:li>
<xhtml:li>Invoking any domain/model logic, using arguments marshaled from
the request.</xhtml:li>
<xhtml:li>Marshaling and returning a response/responder.</xhtml:li>
</xhtml:ul>
<xhtml:p>I think characterizing Controllers and Actions as Facades has
some huge benefits. In both <xhtml:a href="http://blog.astrumfutura.com/archives/373-The-M-in-MVC-Why-Models-are-Misunderstood-and-Unappreciated.html">
PHP</xhtml:a> and <xhtml:a href="https://www.google.com/search?q=fat+controllers+rails">Rails</xhtml:a>,
we've witnessed the problems that arise from so-called "Fat
Controllers" — controllers that do a ton of work, making them
untestable, unreadable, non-reusable nightmares. If we think of
them as Facades, specifically for the three items noted above, we
focus on the specific purpose they fulfill within the system,
giving us:</xhtml:p>
<xhtml:ul>
<xhtml:li>Adherence to the <xhtml:a href="http://en.wikipedia.org/wiki/Single_responsibility_principle">Single
Responsibility Principle</xhtml:a></xhtml:li>
<xhtml:li>Adherence to the <xhtml:a href="http://en.wikipedia.org/wiki/Dependency_inversion_principle">Dependency
Inversion Priniciple</xhtml:a></xhtml:li>
<xhtml:li>Adherence to the <xhtml:a href="http://en.wikipedia.org/wiki/Law_Of_Demeter">Law of
Demeter</xhtml:a></xhtml:li>
<xhtml:li>Ability to unit test our Controllers and Actions (instead of
requiring integration tests with complex configuration and
setup)</xhtml:li>
<xhtml:li>The possibility of <xhtml:a href="http://en.wikipedia.org/wiki/Hierarchical_model%E2%80%93view%E2%80%93controller">
hierarchical MVC</xhtml:a> (usually tacked on, or poorly
implemented)</xhtml:li>
<xhtml:li>Clarity of purpose when creating Controllers and Actions (do
only those three things)</xhtml:li>
</xhtml:ul>
<xhtml:p>Defining them as Facades for these three specific operations
means we push logic into specific domains, achieving a proper
<xhtml:a href="http://en.wikipedia.org/wiki/Separation_of_concerns">separation of
concerns</xhtml:a>. Anything that falls outside those three operations
gets pushed elsewhere:</xhtml:p>
<xhtml:ul>
<xhtml:li>Models/Domains are invoked with the arguments marshaled from
the request. If you find yourself calling many models, or
manipulating the results returned by models, you need to create
Facades in your model/domain layer.</xhtml:li>
<xhtml:li>If you find yourself doing lots of work in creating your
response, you need to create a Facade for marshaling the response
(in ADR, that would mean encapsulating more logic in your
Responder).</xhtml:li>
</xhtml:ul>
<xhtml:p>For me, thinking of Controllers and Actions as Facades has an
additional benefit: it describes rather complex
<xhtml:em>architectural</xhtml:em> patterns in terms of <xhtml:em>basic design
patterns</xhtml:em>. I find the more I can reduce the complexity of a
definition, the more likely I will understand and use it
correctly.</xhtml:p>
<xhtml:h3>Epilogue</xhtml:h3>
<xhtml:p>Consider this post a <xhtml:em>refinement</xhtml:em> of the MVC and ADR
architectural patterns — a way of describing them in terms of more
fundamental design patterns.</xhtml:p>
<xhtml:p>Also, this article is littered with links. Click them. Read
them. Digest them. Read the books they reference. Design and
architectural patterns exist because developers observed the
patterns and gave them names; learn to recognize them and apply
them, at all levels of your application.</xhtml:p>
<xhtml:div class="h-entry"><xhtml:img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&amp;u=79dd2ea1d4d8855944715d09ee4c86215027fa80&amp;s=140" alt="matthew"/> <xhtml:a class="u-url u-uid p-name" href="https://mwop.net/blog/2014-06-09-controllers-as-facades.html">Better
Understanding Controllers Through Basic Patterns</xhtml:a> was originally
published <xhtml:time class="dt-published" datetime="2014-06-09T12:00:00-05:00">9 June 2014</xhtml:time> on <xhtml:a href="https://mwop.net">https://mwop.net</xhtml:a> by <xhtml:a rel="author" class="p-author" href="https://mwop.net">Matthew Weier
O'Phinney</xhtml:a>.</xhtml:div>
</xhtml:div>
    </content>
  </entry>
  <entry xmlns:xhtml="http://www.w3.org/1999/xhtml">
    <title type="html"><![CDATA[Model Infrastructure]]></title>
    <published>2008-12-30T07:35:01-06:00</published>
    <updated>2009-01-05T11:51:07-06:00</updated>
    <link rel="alternate" type="text/html" href="https://mwop.net/blog/202-Model-Infrastructure.html"/>
    <id>https://mwop.net/blog/202-Model-Infrastructure.html</id>
    <author>
      <name>Matthew Weier O'Phinney</name>
      <email>contact@mwop.net</email>
      <uri>https://mwop.net</uri>
    </author>
    <content xmlns:xhtml="http://www.w3.org/1999/xhtml" type="xhtml">
      <xhtml:div xmlns:xhtml="http://www.w3.org/1999/xhtml"><xhtml:p>In the last two entries in this series on models, I covered
<xhtml:a href="/blog/200-Using-Zend_Form-in-Your-Models.html">using forms
as input filters</xhtml:a> and <xhtml:a href="/blog/201-Applying-ACLs-to-Models.html">integrating ACLs into
models</xhtml:a>. In this entry, I tackle some potential infrastructure
for your models.</xhtml:p>
<xhtml:p>The Model is a complex subject. However, it is often boiled down
to either a single model class or a full object relational mapping
(ORM). I personally have never been much of a fan of ORMs as they
tie models to the underlying database structure; I don't always use
a database, nor do I want to rely on an ORM solution too heavily on
the off-chance that I later need to refactor to use services or
another type of persistence store. On the other hand, the model as
a single class is typically too simplistic.</xhtml:p>
<xhtml:p>I <xhtml:em>am</xhtml:em>, however, a fan of the <xhtml:a href="http://en.wikipedia.org/wiki/Domain_model">Domain Model</xhtml:a>. To
quote wikipedia,</xhtml:p>
<xhtml:blockquote>
<xhtml:p>[The] domain model can be thought of as a conceptual model of a
system which describes the various entities involved in that system
and their relationships.</xhtml:p>
</xhtml:blockquote>
<xhtml:p>When you think in these terms, you start breaking your system
into discrete pieces that you need to manipulate, as well as
consider how each piece relates to the others. This type of
exercise also helps you stop thinking of your model in terms of
database tables; instead, your database becomes the container in
which data is persisted from one use of your model to the next.
Your model instead is an object that can <xhtml:em>do</xhtml:em> things with
either incoming or stored data — or even completely
autonomously.</xhtml:p>
<xhtml:p>As an example, when starting with Zend Framework, it's tempting
to use <xhtml:code>Zend_Db_Table</xhtml:code> and
<xhtml:code>Zend_Db_Table_Row</xhtml:code> as models. However, there's one big
argument against doing so: when using a Table Data Gateway (TDG) or
a Row Data Gateway (RDG), you're returning an object that is tied
to the data storage implementation. You're basically putting on
blinders and thinking of your model as simply the database table or
an individual row, and the returned objects reflect this narrow
view point. Furthermore, if you want to re-use your models with
service layers, many web services do not work with objects, and of
those that do, you likely do not want to expose <xhtml:em>all</xhtml:em> the
properties and methods of the objects returned by your data
provider. A row object in ZF, for instance, actually stores the
data in protected members, effectively hiding it from services, and
also includes methods for deleting the row, ArrayAccess methods,
and access to the table object — which gives you full control over
the table! The security implications of exposing this directly over
a service should be obvious.</xhtml:p>
<xhtml:p>Additionally, if in the future you wish to refactor your
application to utilize <xhtml:a href="http://www.danga.com/memcached/">memcached</xhtml:a> or a web service,
you now not only need to completely rewrite your models, but also
all <xhtml:em>consumer</xhtml:em> code, because the return values from your
model have changed.</xhtml:p>
<xhtml:p>So, if you're not going to use an ORM or a Table Data Gateway
directly, how should you architect your model infrastructure?</xhtml:p>
<xhtml:h2>What are you modelling?</xhtml:h2>
<xhtml:p>The principal question to ask is, "What am I modelling?"</xhtml:p>
<xhtml:p>Let's look at a rather standard website issue: user management.
Typically, you'll get a requirement such as, "Users should be able
to register for an account on the site. Once registered, they
should be able to login with the credentials they provided.
Administrators should be able to ban accounts or grant users higher
levels of privileges." That's assuming you actually get good
requirement documents, of course.</xhtml:p>
<xhtml:p>Most developers will immediately setup a database with a few
fields that represent a user — full name, username, email,
password, etc — create a form for registration and another for
login, write a routine to validate each, create a page to list
users for the administration screen… you know the drill. But
<xhtml:em>what are you modelling?</xhtml:em></xhtml:p>
<xhtml:p>The answer is: <xhtml:em>users</xhtml:em>. So, now it's time to define what
a user is, and what a user can do. We have to decide what
constitutes a new user, and what constitutes an authenticated user.
We have an additional modelling consideration that's often
overlooked: user <xhtml:em>roles</xhtml:em>. There's also the matter of what a
<xhtml:em>group</xhtml:em> of users might look like (since the administrator
needs to be able to <xhtml:em>list</xhtml:em> users), and how we might want to
work with groups.</xhtml:p>
<xhtml:p>Let's start with narrowing down the definition of a user:</xhtml:p>
<xhtml:ul>
<xhtml:li>A user consists of the following metadata:
<xhtml:ul>
<xhtml:li>Unique username</xhtml:li>
<xhtml:li>Full name</xhtml:li>
<xhtml:li>Email address</xhtml:li>
<xhtml:li>Hashed password</xhtml:li>
<xhtml:li>A role within the site</xhtml:li>
</xhtml:ul>
</xhtml:li>
<xhtml:li>A <xhtml:em>new user</xhtml:em> must provide a unique username, their full
name, a valid email address, and a password and password
verification.</xhtml:li>
<xhtml:li>An <xhtml:em>authenticated</xhtml:em> user is one who has provided a
matching combination of <xhtml:em>username</xhtml:em> and
<xhtml:em>password</xhtml:em>.</xhtml:li>
<xhtml:li>A user may <xhtml:em>logout</xhtml:em> of the site.</xhtml:li>
<xhtml:li>A user may be granted a new role.</xhtml:li>
<xhtml:li>A user may be marked as banned.</xhtml:li>
</xhtml:ul>
<xhtml:p>Notice the fifth piece of metadata? It mentions a "role"? That's
something to do with our ACLs — which means that ACLs are part of
our user domain. I'll touch on this later.</xhtml:p>
<xhtml:p>If you look at the remaining points carefully, you'll note that
there's talk of validation, authentication, and user and session
persistence. Validation rules are part of our model — and we'll use
<xhtml:code>Zend_Form</xhtml:code> to fulfill that role. Authentication on the
web usually consists of both <xhtml:em>validating</xhtml:em> submitted
credentials against <xhtml:em>stored</xhtml:em> credentials, as well as
<xhtml:em>persisting</xhtml:em> a verified identity in the <xhtml:em>session</xhtml:em>.
This means that other parts of our model include <xhtml:em>data
persistence</xhtml:em> and <xhtml:em>session management</xhtml:em>. We'll use
<xhtml:code>Zend_Db_Table</xhtml:code> for data persistence, and
<xhtml:code>Zend_Auth</xhtml:code>/<xhtml:code>Zend_Session</xhtml:code> for identity
persistence.</xhtml:p>
<xhtml:p>Now, let's turn to defining <xhtml:em>lists</xhtml:em> of users:</xhtml:p>
<xhtml:ul>
<xhtml:li>Administrators should be able to pull lists of users. These
lists should allow for:
<xhtml:ul>
<xhtml:li>Sorting by username, full name, email address, or role</xhtml:li>
<xhtml:li>Pagination (i.e., pulling a set number of users from a given
offset)</xhtml:li>
<xhtml:li>Iteration</xhtml:li>
</xhtml:ul>
</xhtml:li>
<xhtml:li>Administrators should be able to specify criteria for selecting
users to list.</xhtml:li>
</xhtml:ul>
<xhtml:p>These criteria indicate that a <xhtml:em>list</xhtml:em> of users should be
an object. This list will likely implement the SPL class
<xhtml:code>Traversable</xhtml:code> in some fashion. Looking at this criteria,
another aspect of our model becomes clear: we are modelling
<xhtml:em>user selection</xhtml:em> — which includes the ability to specify
sorting and selection criteria. The <xhtml:em>user selection</xhtml:em> object
would return a <xhtml:em>user list</xhtml:em> object, which would consist of
<xhtml:em>user</xhtml:em> objects. User objects define ACL roles and can
authenticate users.</xhtml:p>
<xhtml:p>We started this article by discussing the Domain Model, and
defined it as a system, its entities, and the relations between
those entities. We've now identified our domain: user management.
The various entities include users, lists of users, ACL roles, a
user persistence layer (database), and session persistence layer
(web server sessions).</xhtml:p>
<xhtml:p>Now that we know what we're modelling, let's look at some of the
objects in our model.</xhtml:p>
<xhtml:h2>Gateway to the Domain</xhtml:h2>
<xhtml:p>We've identified "user management" as the purpose of our model.
This will include retrieving and saving individual users, as well
as selecting groups of users.</xhtml:p>
<xhtml:p>It's clear that we'll need an object to represent a user, as
well as another to represent a selection or group of users. But
what may not be entirely clear is that we should likely have an
object that is used to create new user objects, create selections
of users, and basically coordinate several of the related objects —
the root ACL and data access, in particular.</xhtml:p>
<xhtml:p>This object will be what I'll term our domain <xhtml:em>gateway</xhtml:em>.
It will be used to fetch other objects in our model, and will
inject various dependencies into them when doing so, such as the
data access and ACLs. The various dependencies may themselves be
injected into the gateway — or it can lazy-load them.</xhtml:p>
<xhtml:p>The API of this gateway might look something like the
following.</xhtml:p>
<xhtml:pre><xhtml:code class="language-php hljs php" data-lang="php"><xhtml:span class="hljs-comment">// Instantiate the gateway</xhtml:span>
$userGateway = <xhtml:span class="hljs-keyword">new</xhtml:span> Spindle_Model_UserGateway();

<xhtml:span class="hljs-comment">// configure the gateway:</xhtml:span>
$userGateway-&gt;setAcl(<xhtml:span class="hljs-keyword">new</xhtml:span> Spindle_Acl_Spindle())
            -&gt;setDbAdapter(Zend_Registry::get(<xhtml:span class="hljs-string">'db'</xhtml:span>));

<xhtml:span class="hljs-comment">// Alternately, do it all at instantiation:</xhtml:span>
$userGateway = <xhtml:span class="hljs-keyword">new</xhtml:span> Spindle_Model_UserGateway(<xhtml:span class="hljs-keyword">array</xhtml:span>(
    <xhtml:span class="hljs-string">'acl'</xhtml:span>       =&gt; <xhtml:span class="hljs-keyword">new</xhtml:span> Spindle_Acl_Spindle(),
    <xhtml:span class="hljs-string">'dbAdapter'</xhtml:span> =&gt; Zend_Registry::get(<xhtml:span class="hljs-string">'db'</xhtml:span>),
));

<xhtml:span class="hljs-comment">// Grab a single user</xhtml:span>
$user = $userGateway-&gt;retrieve(<xhtml:span class="hljs-string">'matthew'</xhtml:span>);

<xhtml:span class="hljs-comment">// Grab many users</xhtml:span>
$users = $userGateway-&gt;sort(<xhtml:span class="hljs-string">'email'</xhtml:span>, <xhtml:span class="hljs-string">'ASC'</xhtml:span>)
                     -&gt;criteria(<xhtml:span class="hljs-keyword">array</xhtml:span>(<xhtml:span class="hljs-string">'banned'</xhtml:span> =&gt; <xhtml:span class="hljs-keyword">true</xhtml:span>))
                     -&gt;fetch(<xhtml:span class="hljs-keyword">array</xhtml:span>(<xhtml:span class="hljs-string">'offset'</xhtml:span> =&gt; <xhtml:span class="hljs-number">20</xhtml:span>, <xhtml:span class="hljs-string">'limit'</xhtml:span> =&gt; <xhtml:span class="hljs-number">20</xhtml:span>));

<xhtml:span class="hljs-comment">// Better yet, add some transaction script methods with preset criteria:</xhtml:span>
$users = $userGateway-&gt;fetchBannedUsers(<xhtml:span class="hljs-keyword">array</xhtml:span>(
    <xhtml:span class="hljs-string">'offset'</xhtml:span> =&gt; <xhtml:span class="hljs-number">20</xhtml:span>, 
    <xhtml:span class="hljs-string">'limit'</xhtml:span>  =&gt; <xhtml:span class="hljs-number">20</xhtml:span>,
    <xhtml:span class="hljs-string">'sort'</xhtml:span>   = <xhtml:span class="hljs-keyword">array</xhtml:span>(<xhtml:span class="hljs-string">'email'</xhtml:span>, <xhtml:span class="hljs-string">'ASC'</xhtml:span>),
));

<xhtml:span class="hljs-comment">// Create a new user:</xhtml:span>
$user = $userGateway-&gt;createUser(<xhtml:span class="hljs-keyword">array</xhtml:span>(
    <xhtml:span class="hljs-string">'username'</xhtml:span> =&gt; <xhtml:span class="hljs-string">'matthew'</xhtml:span>,
    <xhtml:span class="hljs-string">'fullname'</xhtml:span> =&gt; <xhtml:span class="hljs-string">"Matthew Weier O'Phinney"</xhtml:span>,
    <xhtml:span class="hljs-string">'password'</xhtml:span> =&gt; <xhtml:span class="hljs-string">'secret'</xhtml:span>,
    <xhtml:span class="hljs-string">'email'</xhtml:span>    =&gt; <xhtml:span class="hljs-string">'matthew@local'</xhtml:span>,
));
</xhtml:code></xhtml:pre>
<xhtml:p>The basic idea is to provide a scaffold for lazyloading
necessary objects, methods for specifying options (such as sort
order, criteria, limits, etc), and transaction methods for
retrieving individual users and groups of users.</xhtml:p>
<xhtml:h2>Of Value Objects and Record Sets</xhtml:h2>
<xhtml:p>To other objects we've identified in our model are
<xhtml:em>users</xhtml:em> and <xhtml:em>user lists</xhtml:em>. How should we define
these?</xhtml:p>
<xhtml:p>The traditional answer is as <xhtml:em>value</xhtml:em> or <xhtml:em>data
transfer</xhtml:em> objects and <xhtml:em>record sets</xhtml:em>. The Value Object is
a standard design pattern used to aggregate all metadata that
defines a single value. The Record Set is an aggregation of Value
Objects.</xhtml:p>
<xhtml:h3>Value Objects</xhtml:h3>
<xhtml:p>Martin Fowler makes a differentiation between value objects and
data transfer objects in his book "Patterns of Enterprise
Application Architecture" (PoEAA). In it, he associates value
objects with language variable types (i.e., Value Objects act as
custom variable types), while defining data transfer objects as
aggregating related values for the purpose of serialization and
data transfer between objects.</xhtml:p>
<xhtml:p>In Java, however, value objects are arbitrary objects used to
store a specific set of attributes — very similar to the data
transfer object. For the purposes of this discussion, I'll use the
term "value object," as it will be familiar to those with a Java
background, and to indicate that we are aggregating a unique
<xhtml:em>value</xhtml:em> that is the sum of a number of
<xhtml:em>attributes</xhtml:em>.</xhtml:p>
<xhtml:p>Basically, all of this verbiage describes something incredibly
simple in implementation: an object with a specific set of
attributes or properties. If you've been doing any OOP programming
in PHP, this is the most natural and fundamental thing you can
do.</xhtml:p>
<xhtml:pre><xhtml:code class="language-php hljs php" data-lang="php"><xhtml:span class="hljs-class"><xhtml:span class="hljs-keyword">class</xhtml:span> <xhtml:span class="hljs-title">Spindle_Model_User</xhtml:span>
</xhtml:span>{
    <xhtml:span class="hljs-keyword">protected</xhtml:span> $_data = <xhtml:span class="hljs-keyword">array</xhtml:span>(
        <xhtml:span class="hljs-string">'username'</xhtml:span> =&gt; <xhtml:span class="hljs-keyword">null</xhtml:span>,
        <xhtml:span class="hljs-string">'email'</xhtml:span>    =&gt; <xhtml:span class="hljs-keyword">null</xhtml:span>,
        <xhtml:span class="hljs-string">'fullname'</xhtml:span> =&gt; <xhtml:span class="hljs-string">''</xhtml:span>,
        <xhtml:span class="hljs-string">'role'</xhtml:span>     =&gt; <xhtml:span class="hljs-string">'guest'</xhtml:span>,
    );

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">__construct</xhtml:span><xhtml:span class="hljs-params">($data)</xhtml:span>
    </xhtml:span>{
        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;populate($data);

        <xhtml:span class="hljs-keyword">if</xhtml:span> (!<xhtml:span class="hljs-keyword">isset</xhtml:span>(<xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;username)) {
            <xhtml:span class="hljs-keyword">throw</xhtml:span> <xhtml:span class="hljs-keyword">new</xhtml:span> <xhtml:span class="hljs-keyword">Exception</xhtml:span>(<xhtml:span class="hljs-string">'Initial data must contain an id'</xhtml:span>);
        }
    }

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">populate</xhtml:span><xhtml:span class="hljs-params">($data)</xhtml:span>
    </xhtml:span>{
        <xhtml:span class="hljs-keyword">if</xhtml:span> (is_object($data)) {
            $data = (<xhtml:span class="hljs-keyword">array</xhtml:span>) $data;
        }
        <xhtml:span class="hljs-keyword">if</xhtml:span> (!is_array($data)) {
            <xhtml:span class="hljs-keyword">throw</xhtml:span> <xhtml:span class="hljs-keyword">new</xhtml:span> <xhtml:span class="hljs-keyword">Exception</xhtml:span>(<xhtml:span class="hljs-string">'Initial data must be an array or object'</xhtml:span>);
        }

        <xhtml:span class="hljs-keyword">foreach</xhtml:span> ($data <xhtml:span class="hljs-keyword">as</xhtml:span> $key =&gt; $value) {
            <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;$key = $value;
        }
        <xhtml:span class="hljs-keyword">return</xhtml:span> <xhtml:span class="hljs-keyword">$this</xhtml:span>;
    }

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">__set</xhtml:span><xhtml:span class="hljs-params">($name, $value)</xhtml:span>
    </xhtml:span>{
        <xhtml:span class="hljs-keyword">if</xhtml:span> (!array_key_exists($name, <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;_data)) {
            <xhtml:span class="hljs-keyword">throw</xhtml:span> <xhtml:span class="hljs-keyword">new</xhtml:span> <xhtml:span class="hljs-keyword">Exception</xhtml:span>(<xhtml:span class="hljs-string">'Invalid property "'</xhtml:span> . $name . <xhtml:span class="hljs-string">'"'</xhtml:span>);
        }
        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;_data[$name] = $value;
    }

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">__get</xhtml:span><xhtml:span class="hljs-params">($name)</xhtml:span>
    </xhtml:span>{
        <xhtml:span class="hljs-keyword">if</xhtml:span> (array_key_exists($name, <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;_data)) {
            <xhtml:span class="hljs-keyword">return</xhtml:span> <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;_data[$name];
        }
        <xhtml:span class="hljs-keyword">return</xhtml:span> <xhtml:span class="hljs-keyword">null</xhtml:span>;
    }

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">__isset</xhtml:span><xhtml:span class="hljs-params">($name)</xhtml:span>
    </xhtml:span>{
        <xhtml:span class="hljs-keyword">return</xhtml:span> <xhtml:span class="hljs-keyword">isset</xhtml:span>(<xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;_data[$name]);
    }

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">__unset</xhtml:span><xhtml:span class="hljs-params">($name)</xhtml:span>
    </xhtml:span>{
        <xhtml:span class="hljs-keyword">if</xhtml:span> (<xhtml:span class="hljs-keyword">isset</xhtml:span>(<xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;$name)) {
            <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;_data[$name] = <xhtml:span class="hljs-keyword">null</xhtml:span>;
        }
    }
}
</xhtml:code></xhtml:pre>
<xhtml:p>The above example is fairly simplistic, but it gets the idea
across: the object defines a limited range of valid values, and
enforces that only these values may be set — as well as which
values are required. You could certainly add accessor and mutator
methods to enforce consistent access to member data, but the above
will certainly suffice for many use cases. (I'll look at data
integrity momentarily.)</xhtml:p>
<xhtml:p>One addition you might make to the class definition is to add
some conversions from different types of objects. For instance, if
you know that you'll be using <xhtml:code>Zend_Db_Table</xhtml:code> within
your model, you might want to add the ability for your value object
to accept a <xhtml:code>Zend_Db_Table_Row</xhtml:code> object, and pull its
values from there:</xhtml:p>
<xhtml:pre><xhtml:code class="language-php hljs php" data-lang="php"><xhtml:span class="hljs-class"><xhtml:span class="hljs-keyword">class</xhtml:span> <xhtml:span class="hljs-title">Spindle_Model_User</xhtml:span>
</xhtml:span>{
    <xhtml:span class="hljs-comment">/* ... */</xhtml:span>

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">populate</xhtml:span><xhtml:span class="hljs-params">($data)</xhtml:span>
    </xhtml:span>{
        <xhtml:span class="hljs-keyword">if</xhtml:span> ($data <xhtml:span class="hljs-keyword">instanceof</xhtml:span> Zend_Db_Table_Row_Abstract) {
            $data = $data-&gt;toArray();
        } <xhtml:span class="hljs-keyword">elseif</xhtml:span> (is_object($data)) {
            $data = (<xhtml:span class="hljs-keyword">array</xhtml:span>) $data;
        }

        <xhtml:span class="hljs-keyword">if</xhtml:span> (!is_array($data)) {
            <xhtml:span class="hljs-keyword">throw</xhtml:span> <xhtml:span class="hljs-keyword">new</xhtml:span> <xhtml:span class="hljs-keyword">Exception</xhtml:span>(<xhtml:span class="hljs-string">'Initial data must be an array or object'</xhtml:span>);
        }

        <xhtml:span class="hljs-keyword">foreach</xhtml:span> ($data <xhtml:span class="hljs-keyword">as</xhtml:span> $key =&gt; $value) {
            <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;$key = $value;
        }
        <xhtml:span class="hljs-keyword">return</xhtml:span> <xhtml:span class="hljs-keyword">$this</xhtml:span>;
    }

    <xhtml:span class="hljs-comment">/* ... */</xhtml:span>
}
</xhtml:code></xhtml:pre>
<xhtml:p>This will help keep your model code clean, as you can
potentially take the results of data storage operations and push
them directly into your value object — resulting in less re-working
of code.</xhtml:p>
<xhtml:p>Now, what about data integrity? This is where
<xhtml:code>Zend_Form</xhtml:code> comes into play. Don't think of
<xhtml:code>Zend_Form</xhtml:code> as a web form; think of it as an input
filter that has the ability to render itself as a form if so
desired. If we think of it as an input filter, we can use it for
data integrity:</xhtml:p>
<xhtml:pre><xhtml:code class="language-php hljs php" data-lang="php"><xhtml:span class="hljs-class"><xhtml:span class="hljs-keyword">class</xhtml:span> <xhtml:span class="hljs-title">Spindle_Model_User</xhtml:span>
</xhtml:span>{
    <xhtml:span class="hljs-comment">/* ... */</xhtml:span>

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">__set</xhtml:span><xhtml:span class="hljs-params">($name, $value)</xhtml:span>
    </xhtml:span>{
        <xhtml:span class="hljs-keyword">if</xhtml:span> (!array_key_exists($name, <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;_data)) {
            <xhtml:span class="hljs-keyword">throw</xhtml:span> <xhtml:span class="hljs-keyword">new</xhtml:span> <xhtml:span class="hljs-keyword">Exception</xhtml:span>(<xhtml:span class="hljs-string">'Invalid property "'</xhtml:span> . $name . <xhtml:span class="hljs-string">'"'</xhtml:span>);
        }

        $inputFilter = <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;getForm();
        <xhtml:span class="hljs-keyword">if</xhtml:span> ($element = $inputFilter-&gt;getElement($name)) {
            <xhtml:span class="hljs-keyword">if</xhtml:span> (!$element-&gt;isValid($value)) {
                <xhtml:span class="hljs-keyword">throw</xhtml:span> <xhtml:span class="hljs-keyword">new</xhtml:span> <xhtml:span class="hljs-keyword">Exception</xhtml:span>(sprintf(
                    <xhtml:span class="hljs-string">'Invalid value provided for "%s": %s'</xhtml:span>, 
                    $name, 
                    implode(<xhtml:span class="hljs-string">', '</xhtml:span>, $element-&gt;getMessages())
                );
            }
        }

        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;_data[$name] = $value;
    }

    <xhtml:span class="hljs-comment">/* ... */</xhtml:span>

    <xhtml:span class="hljs-keyword">protected</xhtml:span> $_form;

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">getForm</xhtml:span><xhtml:span class="hljs-params">()</xhtml:span>
    </xhtml:span>{
        <xhtml:span class="hljs-keyword">if</xhtml:span> (<xhtml:span class="hljs-keyword">null</xhtml:span> === <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;_form) {
            <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;_form = <xhtml:span class="hljs-keyword">new</xhtml:span> Spindle_Form_User();
        }
        <xhtml:span class="hljs-keyword">return</xhtml:span> <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;_form;
    }

    <xhtml:span class="hljs-comment">/* ... */</xhtml:span>
}
</xhtml:code></xhtml:pre>
<xhtml:p>One note: if your model contains metadata that will never be
represented as part of a form, you shoould look into using
<xhtml:code>Zend_Filter_Input</xhtml:code> or custom validation chains instead
of <xhtml:code>Zend_Form</xhtml:code>. That's outside the scope of this
article, however.</xhtml:p>
<xhtml:p>Now that we have input filtering out of the way, how shall we
address saving a user? Recall in our discussion of the domain
gateway that one of its responsibilities is injecting other
dependencies into our objects. I find it's often easier to inject
the <xhtml:em>gateway</xhtml:em> into objects, and then pull what I need from
it. Let's look at how that might work for saving the user.</xhtml:p>
<xhtml:pre><xhtml:code class="language-php hljs php" data-lang="php"><xhtml:span class="hljs-class"><xhtml:span class="hljs-keyword">class</xhtml:span> <xhtml:span class="hljs-title">Spindle_Model_User</xhtml:span>
</xhtml:span>{
    <xhtml:span class="hljs-comment">/* ... */</xhtml:span>

    <xhtml:span class="hljs-keyword">protected</xhtml:span> $_gateway;

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">__construct</xhtml:span><xhtml:span class="hljs-params">($data, $gateway)</xhtml:span>
    </xhtml:span>{
        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;setGateway($gateway);

        <xhtml:span class="hljs-comment">/* ... */</xhtml:span>
    }

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">setGateway</xhtml:span><xhtml:span class="hljs-params">(Spindle_Model_UserGateway $gateway)</xhtml:span>
    </xhtml:span>{
        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;_gateway = $gateway;
        <xhtml:span class="hljs-keyword">return</xhtml:span> <xhtml:span class="hljs-keyword">$this</xhtml:span>;
    }

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">getGateway</xhtml:span><xhtml:span class="hljs-params">()</xhtml:span>
    </xhtml:span>{
        <xhtml:span class="hljs-keyword">return</xhtml:span> <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;_gateway;
    }

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">save</xhtml:span><xhtml:span class="hljs-params">()</xhtml:span>
    </xhtml:span>{
        $gateway = <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;getGateway();
        $dbTable = $gateway-&gt;getDbTable(<xhtml:span class="hljs-string">'user'</xhtml:span>);

        <xhtml:span class="hljs-keyword">if</xhtml:span> ($row = $dbTable-&gt;find(<xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;username)) {
            <xhtml:span class="hljs-keyword">foreach</xhtml:span> (<xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;_data <xhtml:span class="hljs-keyword">as</xhtml:span> $key =&gt; $value) {
                $row-&gt;$key = $value;
            }
            $row-&gt;save();
        } <xhtml:span class="hljs-keyword">else</xhtml:span> {
            $dbTable-&gt;insert(<xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;_data);
        }
    }

    <xhtml:span class="hljs-comment">/* ... */</xhtml:span>
}
</xhtml:code></xhtml:pre>
<xhtml:p>Note that the constructor now has a second argument — the
gateway. This ensures that the user always has a gateway instance,
which further ensures that operations like the one listed —
retrieving the <xhtml:code>Zend_Db_Table</xhtml:code> instance from the gateway
— will always work. In this example, we simply check to see if a
row already exists, and then save the record accordingly.</xhtml:p>
<xhtml:p>Another requirement we identified was that a user be able to
authenticate itself. This can be done trivially by implementing
<xhtml:code>Zend_Auth_Adapter_Interface</xhtml:code>:</xhtml:p>
<xhtml:pre><xhtml:code class="language-php hljs php" data-lang="php"><xhtml:span class="hljs-class"><xhtml:span class="hljs-keyword">class</xhtml:span> <xhtml:span class="hljs-title">Spindle_Model_User</xhtml:span> <xhtml:span class="hljs-keyword">implements</xhtml:span> <xhtml:span class="hljs-title">Zend_Auth_Adapter_Interface</xhtml:span>
</xhtml:span>{
    <xhtml:span class="hljs-comment">/* ... */</xhtml:span>

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">authenticate</xhtml:span><xhtml:span class="hljs-params">()</xhtml:span>
    </xhtml:span>{
        $gateway = <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;getGateway();
        $table   = $manager-&gt;getDbTable(<xhtml:span class="hljs-string">'user'</xhtml:span>);
        $select  = $table-&gt;select();
        $select-&gt;where(<xhtml:span class="hljs-string">'username = ?'</xhtml:span>, <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;username)
               -&gt;where(<xhtml:span class="hljs-string">'password = ?'</xhtml:span>, <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;password)
               -&gt;where(<xhtml:span class="hljs-string">'date_banned IS NULL'</xhtml:span>);
        $user = $table-&gt;fetchRow($select);
        <xhtml:span class="hljs-keyword">if</xhtml:span> (<xhtml:span class="hljs-keyword">null</xhtml:span> === $user) {
            <xhtml:span class="hljs-comment">// failed</xhtml:span>
            $result = <xhtml:span class="hljs-keyword">new</xhtml:span> Zend_Auth_Result(
                Zend_Auth_Result::FAILURE_UNCATEGORIZED,
                <xhtml:span class="hljs-keyword">null</xhtml:span>
            );
        } <xhtml:span class="hljs-keyword">else</xhtml:span> {
            <xhtml:span class="hljs-comment">// passed</xhtml:span>
            <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;populate($user);
            <xhtml:span class="hljs-keyword">unset</xhtml:span>(<xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;password);
            $result = <xhtml:span class="hljs-keyword">new</xhtml:span> Zend_Auth_Result(Zend_Auth_Result::SUCCESS, <xhtml:span class="hljs-keyword">$this</xhtml:span>);
        }
        <xhtml:span class="hljs-keyword">return</xhtml:span> $result;
    }

    <xhtml:span class="hljs-comment">/* ... */</xhtml:span>
}
</xhtml:code></xhtml:pre>
<xhtml:p>To authenticate a user, you would create a new user object with
the username and password, and then attempt to authenticate it:</xhtml:p>
<xhtml:pre><xhtml:code class="language-php hljs php" data-lang="php">$auth = Zend_Auth::getInstance();
$user = $gateway-&gt;createUser(<xhtml:span class="hljs-keyword">array</xhtml:span>(
    <xhtml:span class="hljs-string">'username'</xhtml:span> =&gt; $username,
    <xhtml:span class="hljs-string">'password'</xhtml:span> =&gt; $password,
));
<xhtml:span class="hljs-keyword">if</xhtml:span> ($auth-&gt;authenticate($user)) {
    <xhtml:span class="hljs-comment">// AUTHENTICATED!</xhtml:span>
}
</xhtml:code></xhtml:pre>
<xhtml:p>This also has the effect of populating the user from the
persistence store, as well as storing the identity in the
session.</xhtml:p>
<xhtml:p>I covered ACL roles <xhtml:a href="/blog/201-Applying-ACLs-to-Models.html">previously</xhtml:a>, so I won't
go into that here. However, you should now be getting a pretty
clear understanding of how this object works, and how it
coordinates with the user gateway. It should also illustrate that
this aspect of our model is much, much more than simply data
access: we're coordinating authentication, input filtering, and
ACLs — and providing a fairly simple API for manipulating the user
itself.</xhtml:p>
<xhtml:h3>Record Sets</xhtml:h3>
<xhtml:p>A Record Set is similarly easy to create. Typically, you will
merely want the object to be iterable and countable. Like the user
object, we'll require a gateway instance in the constructor.</xhtml:p>
<xhtml:pre><xhtml:code class="language-php hljs php" data-lang="php"><xhtml:span class="hljs-class"><xhtml:span class="hljs-keyword">class</xhtml:span> <xhtml:span class="hljs-title">Spindle_Model_Users</xhtml:span> <xhtml:span class="hljs-keyword">implements</xhtml:span> <xhtml:span class="hljs-title">Iterator</xhtml:span>,<xhtml:span class="hljs-title">Countable</xhtml:span>
</xhtml:span>{
    <xhtml:span class="hljs-keyword">protected</xhtml:span> $_count;
    <xhtml:span class="hljs-keyword">protected</xhtml:span> $_gateway;
    <xhtml:span class="hljs-keyword">protected</xhtml:span> $_resultSet;

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">__construct</xhtml:span><xhtml:span class="hljs-params">($results, $gateway)</xhtml:span>
    </xhtml:span>{
        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;setGateway($gateway);
        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;_resultSet = $results;
    }

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">setGateway</xhtml:span><xhtml:span class="hljs-params">(Spindle_Model_UserGateway $gateway)</xhtml:span>
    </xhtml:span>{
        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;_gateway = $gateway;
        <xhtml:span class="hljs-keyword">return</xhtml:span> <xhtml:span class="hljs-keyword">$this</xhtml:span>;
    }

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">getGateway</xhtml:span><xhtml:span class="hljs-params">()</xhtml:span>
    </xhtml:span>{
        <xhtml:span class="hljs-keyword">return</xhtml:span> <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;_gateway;
    }

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">count</xhtml:span><xhtml:span class="hljs-params">()</xhtml:span>
    </xhtml:span>{
        <xhtml:span class="hljs-keyword">if</xhtml:span> (<xhtml:span class="hljs-keyword">null</xhtml:span> === <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;_count) {
            <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;_count = count(<xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;_resultSet);
        }
        <xhtml:span class="hljs-keyword">return</xhtml:span> <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;_count;
    }

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">current</xhtml:span><xhtml:span class="hljs-params">()</xhtml:span>
    </xhtml:span>{
        <xhtml:span class="hljs-keyword">if</xhtml:span> (<xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;_resultSet <xhtml:span class="hljs-keyword">instanceof</xhtml:span> Iterator) {
            $key = <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;_resultSet-&gt;key();
        } <xhtml:span class="hljs-keyword">else</xhtml:span> {
            $key = key(<xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;_resultSet);
        }
        $result  = <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;_resultSet[$key];
        <xhtml:span class="hljs-keyword">if</xhtml:span> (!$result <xhtml:span class="hljs-keyword">instanceof</xhtml:span> Spindle_Model_User) {
            $gateway = <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;getGateway();
            $result  = $gateway-&gt;createUser($result);
            <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;_resultSet[$key] = $result;
        }
        <xhtml:span class="hljs-keyword">return</xhtml:span> $result;
    }

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">key</xhtml:span><xhtml:span class="hljs-params">()</xhtml:span>
    </xhtml:span>{
        <xhtml:span class="hljs-keyword">return</xhtml:span> key(<xhtml:span class="hljs-keyword">$this</xhtml:span>-_resultSet);
    }

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">next</xhtml:span><xhtml:span class="hljs-params">()</xhtml:span>
    </xhtml:span>{
        <xhtml:span class="hljs-keyword">return</xhtml:span> next(<xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;_resultSet);
    }

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">rewind</xhtml:span><xhtml:span class="hljs-params">()</xhtml:span>
    </xhtml:span>{
        <xhtml:span class="hljs-keyword">return</xhtml:span> reset(<xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;_resultSet);
    }

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">valid</xhtml:span><xhtml:span class="hljs-params">()</xhtml:span>
    </xhtml:span>{
        <xhtml:span class="hljs-keyword">return</xhtml:span> (bool) <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;current();
    }
}
</xhtml:code></xhtml:pre>
<xhtml:p>The logic here is incredibly simple. The main benefit from using
a Record Set over an array is that it allows you to ensure the
types of each item in the set, as well as allow your consuming code
to perform type hinting on the Record Set class.</xhtml:p>
<xhtml:h2>Using Value Objects and Record Sets in Your Gateway</xhtml:h2>
<xhtml:p>Within your gateway class, it is then your responsibility to
ensure that instances of your new classes are returned. As an
example, let's look at some easy <xhtml:code>fetch()</xhtml:code> and
<xhtml:code>fetchAll()</xhtml:code> methods:</xhtml:p>
<xhtml:pre><xhtml:code class="language-php hljs php" data-lang="php"><xhtml:span class="hljs-class"><xhtml:span class="hljs-keyword">class</xhtml:span> <xhtml:span class="hljs-title">Spindle_Model_UserGateway</xhtml:span>
</xhtml:span>{
    <xhtml:span class="hljs-comment">/* ... */</xhtml:span>

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">fetch</xhtml:span><xhtml:span class="hljs-params">($id)</xhtml:span>
    </xhtml:span>{
        $dbTable = <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;getDbTable();
        $select  = $dbTable-&gt;select();
        $select-&gt;where(<xhtml:span class="hljs-string">'id = ?'</xhtml:span>, $id);
        $result = $dbTable-&gt;fetchRow($select);
        <xhtml:span class="hljs-keyword">if</xhtml:span> (<xhtml:span class="hljs-keyword">null</xhtml:span> !== $result) {
            $result = <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;createUser($result);
        }
        <xhtml:span class="hljs-keyword">return</xhtml:span> $result;
    }

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">fetchAll</xhtml:span><xhtml:span class="hljs-params">()</xhtml:span>
    </xhtml:span>{
        $result = <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;getDbTable()-&gt;fetchAll();
        <xhtml:span class="hljs-keyword">return</xhtml:span> <xhtml:span class="hljs-keyword">new</xhtml:span> Spindle_Model_Users($result, <xhtml:span class="hljs-keyword">$this</xhtml:span>);
    }

    <xhtml:span class="hljs-comment">/* ... */</xhtml:span>
}
</xhtml:code></xhtml:pre>
<xhtml:p>You'll notice the downside immediately: you have to introduce
new objects, and that means re-casting of data. But let's look at
it from a consumer viewpoint: the consuming code is looking for
return types of <xhtml:code>Spindle_Model_User</xhtml:code> and
<xhtml:code>Spindle_Model_Users</xhtml:code>.</xhtml:p>
<xhtml:p>But what's the point of the gateway, really? Couldn't both the
value object and result set object simply inherit from a common
base? Certainly they could. However, one common use case I have for
gateways is providing pre-defined methods encapsulating common
selection criteria. For instance, let's say you wanted to retrieve
all <xhtml:em>banned</xhtml:em> users, and that this will be a common task.
Define a method for it:</xhtml:p>
<xhtml:pre><xhtml:code class="language-php hljs php" data-lang="php"><xhtml:span class="hljs-class"><xhtml:span class="hljs-keyword">class</xhtml:span> <xhtml:span class="hljs-title">Spindle_Model_UserGateway</xhtml:span>
</xhtml:span>{
    <xhtml:span class="hljs-comment">/* ... */</xhtml:span>

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">fetchBannedUsers</xhtml:span><xhtml:span class="hljs-params">()</xhtml:span>
    </xhtml:span>{
        $dbTable = <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;getDbTable();
        $select  = $dbTable-&gt;select()-&gt;where(<xhtml:span class="hljs-string">'date_banned IS NOT NULL'</xhtml:span>);
        $result  = $dbTable-&gt;fetchAll($select);
        <xhtml:span class="hljs-keyword">return</xhtml:span> <xhtml:span class="hljs-keyword">new</xhtml:span> Spindle_Model_Users($result, <xhtml:span class="hljs-keyword">$this</xhtml:span>);
    }

    <xhtml:span class="hljs-comment">/* ... */</xhtml:span>
}
</xhtml:code></xhtml:pre>
<xhtml:p>This is admittedly a trivial example, but it clearly illustrates
the benefits: we now have an API method that tells us, in plain
English, what operation we are performing, and provides a
repeatable way to do it. The user consuming the model needs not
know anything about how it works under the hood, only that they can
expect to get a list of banned users when they call it.</xhtml:p>
<xhtml:p>Another key benefit to creating a gateway is for those times
when we need to replace our data access layer with something else.
Let's refactor our code to use a service instead:</xhtml:p>
<xhtml:pre><xhtml:code class="language-php hljs php" data-lang="php"><xhtml:span class="hljs-class"><xhtml:span class="hljs-keyword">class</xhtml:span> <xhtml:span class="hljs-title">Spindle_Model_UserGateway</xhtml:span>
</xhtml:span>{
    <xhtml:span class="hljs-comment">/* ... */</xhtml:span>

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">fetch</xhtml:span><xhtml:span class="hljs-params">($id)</xhtml:span>
    </xhtml:span>{
        $result  = <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;getService()-&gt;fetchUser($id);
        <xhtml:span class="hljs-keyword">return</xhtml:span> <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;createUser($result);
    }

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">fetchAll</xhtml:span><xhtml:span class="hljs-params">()</xhtml:span>
    </xhtml:span>{
        $result = <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;getService()-&gt;fetchAll();
        <xhtml:span class="hljs-keyword">return</xhtml:span> <xhtml:span class="hljs-keyword">new</xhtml:span> Spindle_Model_Users($result, <xhtml:span class="hljs-keyword">$this</xhtml:span>);
    }

    <xhtml:span class="hljs-comment">/* ... */</xhtml:span>
}
</xhtml:code></xhtml:pre>
<xhtml:p>From a consumer standpoint, <xhtml:em>nothing has changed</xhtml:em>; they
are still calling the same methods, and receiving the same
responses. This is absolutely key in creating maintainable, future
proof code.</xhtml:p>
<xhtml:h2>Summary</xhtml:h2>
<xhtml:p>The solutions presented here are by no means canonical. You may
find that your own models do not need a gateway class, or that you
never work with lists of objects. Hopefully, however, I've
illustrated that a model should cleanly provide a separation of
concerns and consist of discrete objects — whether they are
directly related to your model, or related to aspects of how your
model <xhtml:em>does stuff</xhtml:em>, like validation and data persistence.
You should strive to make your models as simple as possible, while
still meeting each of your requirements. The end result should be a
re-usable, testable suite of functionality, and careful
architecture of your solution should make it robust and easy to
refactor in the future.</xhtml:p>
<xhtml:p><xhtml:strong>Updates:</xhtml:strong></xhtml:p>
<xhtml:ul>
<xhtml:li><xhtml:em>2009-01-04:</xhtml:em> Updated <xhtml:code>__unset()</xhtml:code> per
Gabriel's feedback (comment <xhtml:a href="/search?q=tag:14">#14</xhtml:a>)</xhtml:li>
<xhtml:li><xhtml:em>2009-01-05:</xhtml:em> Updated <xhtml:code>current()</xhtml:code>
implementation per Falk's feedback (comment <xhtml:a href="/search?q=tag:15">#15</xhtml:a>)</xhtml:li>
<xhtml:li><xhtml:em>2009-01-05:</xhtml:em> Updated <xhtml:code>current()</xhtml:code>
implementation per Martin's feedback (comment <xhtml:a href="/search?q=tag:15">#15</xhtml:a>.1.1)</xhtml:li>
</xhtml:ul>
<xhtml:div class="h-entry"><xhtml:img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&amp;u=79dd2ea1d4d8855944715d09ee4c86215027fa80&amp;s=140" alt="matthew"/> <xhtml:a class="u-url u-uid p-name" href="https://mwop.net/blog/202-Model-Infrastructure.html">Model
Infrastructure</xhtml:a> was originally published <xhtml:time class="dt-published" datetime="2008-12-30T07:35:01-06:00">30 December
2008</xhtml:time> on <xhtml:a href="https://mwop.net">https://mwop.net</xhtml:a> by
<xhtml:a rel="author" class="p-author" href="https://mwop.net">Matthew
Weier O'Phinney</xhtml:a>.</xhtml:div>
</xhtml:div>
    </content>
  </entry>
  <entry xmlns:xhtml="http://www.w3.org/1999/xhtml">
    <title type="html"><![CDATA[Setting up your Zend_Test test suites]]></title>
    <published>2008-09-11T15:00:00-05:00</published>
    <updated>2008-09-13T09:37:40-05:00</updated>
    <link rel="alternate" type="text/html" href="https://mwop.net/blog/190-Setting-up-your-Zend_Test-test-suites.html"/>
    <id>https://mwop.net/blog/190-Setting-up-your-Zend_Test-test-suites.html</id>
    <author>
      <name>Matthew Weier O'Phinney</name>
      <email>contact@mwop.net</email>
      <uri>https://mwop.net</uri>
    </author>
    <content xmlns:xhtml="http://www.w3.org/1999/xhtml" type="xhtml">
      <xhtml:div xmlns:xhtml="http://www.w3.org/1999/xhtml"><xhtml:p>Now that <xhtml:a href="http://framework.zend.com/manual/en/zend.test.html">Zend_Test</xhtml:a>
has shipped, developers are of course asking, "How do I setup my
test suite?" Fortunately, after some discussion with my colleagues
and a little experimenting on my one, I can answer that now.</xhtml:p>
<xhtml:p><xhtml:a href="http://phpunit.de">PHPUnit</xhtml:a> offers a variety of
methods for setting up test suites, some trivial and some complex.
The Zend Framework test suite, for instance, goes for a more
complex route, adding component-level suites that require a fair
amount of initial setup, but which allow us fairly fine-grained
control.</xhtml:p>
<xhtml:p>However, testing and test automation should be easy and the
complex approach is overkill for most of our applications.
Fortunately, PHPUnit offers some other methods that make doing so
relatively simple. The easiest method is to use an <xhtml:a href="http://www.phpunit.de/pocket_guide/3.2/en/appendixes.configuration.html">
XML configuration file</xhtml:a>.</xhtml:p>
<xhtml:p>As an example, consider the following:</xhtml:p>
<xhtml:pre><xhtml:code class="language-xml hljs xml" data-lang="xml"><xhtml:span class="hljs-tag">&lt;<xhtml:span class="hljs-name">phpunit</xhtml:span>&gt;</xhtml:span>
    <xhtml:span class="hljs-tag">&lt;<xhtml:span class="hljs-name">testsuite</xhtml:span> <xhtml:span class="hljs-attr">name</xhtml:span>=<xhtml:span class="hljs-string">"My Test Suite"</xhtml:span>&gt;</xhtml:span>
        <xhtml:span class="hljs-tag">&lt;<xhtml:span class="hljs-name">directory</xhtml:span>&gt;</xhtml:span>./<xhtml:span class="hljs-tag">&lt;/<xhtml:span class="hljs-name">directory</xhtml:span>&gt;</xhtml:span>
    <xhtml:span class="hljs-tag">&lt;/<xhtml:span class="hljs-name">testsuite</xhtml:span>&gt;</xhtml:span>

    <xhtml:span class="hljs-tag">&lt;<xhtml:span class="hljs-name">filter</xhtml:span>&gt;</xhtml:span>
        <xhtml:span class="hljs-tag">&lt;<xhtml:span class="hljs-name">whitelist</xhtml:span>&gt;</xhtml:span>
            <xhtml:span class="hljs-tag">&lt;<xhtml:span class="hljs-name">directory</xhtml:span> <xhtml:span class="hljs-attr">suffix</xhtml:span>=<xhtml:span class="hljs-string">".php"</xhtml:span>&gt;</xhtml:span>../library/<xhtml:span class="hljs-tag">&lt;/<xhtml:span class="hljs-name">directory</xhtml:span>&gt;</xhtml:span>
            <xhtml:span class="hljs-tag">&lt;<xhtml:span class="hljs-name">directory</xhtml:span> <xhtml:span class="hljs-attr">suffix</xhtml:span>=<xhtml:span class="hljs-string">".php"</xhtml:span>&gt;</xhtml:span>../application/<xhtml:span class="hljs-tag">&lt;/<xhtml:span class="hljs-name">directory</xhtml:span>&gt;</xhtml:span>
            <xhtml:span class="hljs-tag">&lt;<xhtml:span class="hljs-name">exclude</xhtml:span>&gt;</xhtml:span>
                <xhtml:span class="hljs-tag">&lt;<xhtml:span class="hljs-name">directory</xhtml:span> <xhtml:span class="hljs-attr">suffix</xhtml:span>=<xhtml:span class="hljs-string">".phtml"</xhtml:span>&gt;</xhtml:span>../application/<xhtml:span class="hljs-tag">&lt;/<xhtml:span class="hljs-name">directory</xhtml:span>&gt;</xhtml:span>
            <xhtml:span class="hljs-tag">&lt;/<xhtml:span class="hljs-name">exclude</xhtml:span>&gt;</xhtml:span>
        <xhtml:span class="hljs-tag">&lt;/<xhtml:span class="hljs-name">whitelist</xhtml:span>&gt;</xhtml:span>
    <xhtml:span class="hljs-tag">&lt;/<xhtml:span class="hljs-name">filter</xhtml:span>&gt;</xhtml:span>

    <xhtml:span class="hljs-tag">&lt;<xhtml:span class="hljs-name">logging</xhtml:span>&gt;</xhtml:span>
        <xhtml:span class="hljs-tag">&lt;<xhtml:span class="hljs-name">log</xhtml:span> <xhtml:span class="hljs-attr">type</xhtml:span>=<xhtml:span class="hljs-string">"coverage-html"</xhtml:span> <xhtml:span class="hljs-attr">target</xhtml:span>=<xhtml:span class="hljs-string">"./log/report"</xhtml:span> <xhtml:span class="hljs-attr">charset</xhtml:span>=<xhtml:span class="hljs-string">"UTF-8"</xhtml:span>
            <xhtml:span class="hljs-attr">yui</xhtml:span>=<xhtml:span class="hljs-string">"true"</xhtml:span> <xhtml:span class="hljs-attr">highlight</xhtml:span>=<xhtml:span class="hljs-string">"true"</xhtml:span>
            <xhtml:span class="hljs-attr">lowUpperBound</xhtml:span>=<xhtml:span class="hljs-string">"50"</xhtml:span> <xhtml:span class="hljs-attr">highLowerBound</xhtml:span>=<xhtml:span class="hljs-string">"80"</xhtml:span>/&gt;</xhtml:span>
        <xhtml:span class="hljs-tag">&lt;<xhtml:span class="hljs-name">log</xhtml:span> <xhtml:span class="hljs-attr">type</xhtml:span>=<xhtml:span class="hljs-string">"testdox-html"</xhtml:span> <xhtml:span class="hljs-attr">target</xhtml:span>=<xhtml:span class="hljs-string">"./log/testdox.html"</xhtml:span> /&gt;</xhtml:span>
    <xhtml:span class="hljs-tag">&lt;/<xhtml:span class="hljs-name">logging</xhtml:span>&gt;</xhtml:span>
<xhtml:span class="hljs-tag">&lt;/<xhtml:span class="hljs-name">phpunit</xhtml:span>&gt;</xhtml:span>
</xhtml:code></xhtml:pre>
<xhtml:p>First thing to note, relative paths are relative to the
configuration file. This allows you to run your tests from anywhere
in your tests tree. Second, providing a <xhtml:code>directory</xhtml:code>
directive to the <xhtml:code>testsuite</xhtml:code> directive scans for all
files ending in <xhtml:code>Test.php</xhtml:code> in that directory, meaning
you don't have to keep a list of your test cases manually. It's a
great way to automate the suite. Third, the filter directive allows
us to determine what classes to include and/or exclude from
coverage reports. Finally, the <xhtml:code>logging</xhtml:code> directive lets
us specify what kinds of logs to create and where.</xhtml:p>
<xhtml:p>Drop the above into <xhtml:code>tests/phpunit.xml</xhtml:code> in your
application, and you can start writing test cases and running the
suite immediately, using the following command:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ phpunit --configuration phpunit.xml
</xhtml:code></xhtml:pre>
<xhtml:p>I like to group my test cases by type. I have controllers,
models, and often library code, and need to keep the tests
organized both on the filesystem as well as for running the actual
tests. There are two things I do to facilitate this.</xhtml:p>
<xhtml:p>First, I create directories. For instance, I have the following
hierarchy in my test suite:</xhtml:p>
<xhtml:pre><xhtml:code class="language- hljs" data-lang="">tests/
    phpunit.xml
    TestHelper.php
    controllers/
        IndexControllerTest.php (contains IndexControllerTest)
        ErrorControllerTest.php (contains ErrorControllerTest)
        ...
    models/
        PasteTest.php           (contains PasteTest)
        DbTable/
            PasteTest.php       (contains DbTable_PasteTest)
        ...
    My/
        Form/
            Element/
                SimpleTextareaTest.php
</xhtml:code></xhtml:pre>
<xhtml:p><xhtml:code>controllers/</xhtml:code> contains my controllers,
<xhtml:code>models/</xhtml:code> contains my models. If I were developing a
modular application, I'd have something like
<xhtml:code>blog/controllers/</xhtml:code> instead. Library code is given the
same hierarchy as is found in my <xhtml:code>library/</xhtml:code>
directory.</xhtml:p>
<xhtml:p>Second, I use docblock annotations to group my tests. I add the
following to my class-level docblock in my controller test
cases:</xhtml:p>
<xhtml:pre><xhtml:code class="language-php hljs php" data-lang="php"><xhtml:span class="hljs-comment">/**
 * <xhtml:span class="hljs-doctag">@group</xhtml:span> Controllers
 */</xhtml:span>
</xhtml:code></xhtml:pre>
<xhtml:p>Models get the annotation <xhtml:code>@group Models</xhtml:code>, etc. This
allows me to run individual sets of tests on demand:</xhtml:p>
<xhtml:pre><xhtml:code class="language-bash hljs bash" data-lang="bash">$ phpunit --configuration phpunit.xml --group=Controllers
</xhtml:code></xhtml:pre>
<xhtml:p>You can specify multiple <xhtml:code>@group</xhtml:code> annotations, which
means you can separate tests into modules, issue report
identifiers, etc; additionally, you can add the annotations to
individual test methods themselves to have really fine-grained test
running capabilities.</xhtml:p>
<xhtml:p>Astute readers will have noticed the <xhtml:code>TestHelper.php</xhtml:code>
file in that directory listing earlier, and will be wondering what
that's all about.</xhtml:p>
<xhtml:p>A test suite needs some environmental information, just like
your application does. It may need a default database adapter,
altered <xhtml:code>include_path</xhtml:code>s, autoloading set up, and more.
Here's what my <xhtml:code>TestHelper.php</xhtml:code> looks like:</xhtml:p>
<xhtml:pre><xhtml:code class="language-php hljs php" data-lang="php"><xhtml:span class="hljs-meta">&lt;?php</xhtml:span>
<xhtml:span class="hljs-comment">/*
 * Start output buffering
 */</xhtml:span>
ob_start();

<xhtml:span class="hljs-comment">/*
 * Set error reporting to the level to which code must comply.
 */</xhtml:span>
error_reporting( E_ALL | E_STRICT );

<xhtml:span class="hljs-comment">/*
 * Set default timezone
 */</xhtml:span>
date_default_timezone_set(<xhtml:span class="hljs-string">'GMT'</xhtml:span>);

<xhtml:span class="hljs-comment">/*
 * Testing environment
 */</xhtml:span>
define(<xhtml:span class="hljs-string">'APPLICATION_ENV'</xhtml:span>, <xhtml:span class="hljs-string">'testing'</xhtml:span>);

<xhtml:span class="hljs-comment">/*
 * Determine the root, library, tests, and models directories
 */</xhtml:span>
$root        = realpath(dirname(<xhtml:span class="hljs-keyword">__FILE__</xhtml:span>) . <xhtml:span class="hljs-string">'/../'</xhtml:span>);
$library     = $root . <xhtml:span class="hljs-string">'/library'</xhtml:span>;
$tests       = $root . <xhtml:span class="hljs-string">'/tests'</xhtml:span>;
$models      = $root . <xhtml:span class="hljs-string">'/application/models'</xhtml:span>;
$controllers = $root . <xhtml:span class="hljs-string">'/application/controllers'</xhtml:span>;

<xhtml:span class="hljs-comment">/*
 * Prepend the library/, tests/, and models/ directories to the
 * include_path. This allows the tests to run out of the box.
 */</xhtml:span>
$path = <xhtml:span class="hljs-keyword">array</xhtml:span>(
    $models,
    $library,
    $tests,
    get_include_path()
);
set_include_path(implode(PATH_SEPARATOR, $path));

<xhtml:span class="hljs-comment">/**
 * Register autoloader
 */</xhtml:span>
<xhtml:span class="hljs-keyword">require_once</xhtml:span> <xhtml:span class="hljs-string">'Zend/Loader.php'</xhtml:span>;
Zend_Loader::registerAutoload();

<xhtml:span class="hljs-comment">/**
 * Store application root in registry
 */</xhtml:span>
Zend_Registry::set(<xhtml:span class="hljs-string">'testRoot'</xhtml:span>, $root);
Zend_Registry::set(<xhtml:span class="hljs-string">'testBootstrap'</xhtml:span>, $root . <xhtml:span class="hljs-string">'/application/bootstrap.php'</xhtml:span>);

<xhtml:span class="hljs-comment">/*
 * Unset global variables that are no longer needed.
 */</xhtml:span>
<xhtml:span class="hljs-keyword">unset</xhtml:span>($root, $library, $models, $controllers, $tests, $path);
</xhtml:code></xhtml:pre>
<xhtml:p>The above ensures that my <xhtml:code>APPLICATION_ENV</xhtml:code> constant
is set appropriately, that error reporting is appropriate for tests
(i.e., I want to see <xhtml:em>all</xhtml:em> errors), and that autoloading is
enabled. Additionally, I place a couple items in my registry — the
bootstrap and test root directory.</xhtml:p>
<xhtml:p>In each test case file, I then do a <xhtml:code>require_once</xhtml:code> on
this file. In future versions of PHPUnit, you'll be able to specify
a bootstrap file in your configuration XML that gets pulled in for
each test case, and you'll be able to even further automate your
testing environment setup.</xhtml:p>
<xhtml:p>Hopefully this will get you started with your application
testing; what are you waiting for?</xhtml:p>
<xhtml:div class="h-entry"><xhtml:img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&amp;u=79dd2ea1d4d8855944715d09ee4c86215027fa80&amp;s=140" alt="matthew"/> <xhtml:a class="u-url u-uid p-name" href="https://mwop.net/blog/190-Setting-up-your-Zend_Test-test-suites.html">
Setting up your Zend_Test test suites</xhtml:a> was originally published
<xhtml:time class="dt-published" datetime="2008-09-11T15:00:00-05:00">11
September 2008</xhtml:time> on <xhtml:a href="https://mwop.net">https://mwop.net</xhtml:a> by <xhtml:a rel="author" class="p-author" href="https://mwop.net">Matthew Weier
O'Phinney</xhtml:a>.</xhtml:div>
</xhtml:div>
    </content>
  </entry>
  <entry xmlns:xhtml="http://www.w3.org/1999/xhtml">
    <title type="html"><![CDATA[Testing Zend Framework MVC Applications]]></title>
    <published>2008-06-30T12:00:00-05:00</published>
    <updated>2008-07-03T04:47:17-05:00</updated>
    <link rel="alternate" type="text/html" href="https://mwop.net/blog/182-Testing-Zend-Framework-MVC-Applications.html"/>
    <id>https://mwop.net/blog/182-Testing-Zend-Framework-MVC-Applications.html</id>
    <author>
      <name>Matthew Weier O'Phinney</name>
      <email>contact@mwop.net</email>
      <uri>https://mwop.net</uri>
    </author>
    <content xmlns:xhtml="http://www.w3.org/1999/xhtml" type="xhtml">
      <xhtml:div xmlns:xhtml="http://www.w3.org/1999/xhtml"><xhtml:p>Since I originally started hacking on the <xhtml:a href="http://framework.zend.com/">Zend Framework</xhtml:a> MVC in the fall of
2006, I've been touting the fact that you can test ZF MVC projects
by utilizing the Request and Response objects; indeed, this is what
I actually did to test the Front Controller and Dispatcher.
However, until recently, there was never an easy way to do so in
your userland projects; the default request and response objects
make it difficult to easily and quickly setup tests, and the
methods introduced into the front controller to make it testable
are largely undocumented.</xhtml:p>
<xhtml:p>So, one of my ongoing projects the past few months has been to
create an infrastructure for functional testing of ZF projects
using <xhtml:a href="http://phpunit.de/">PHPUnit</xhtml:a>. This past weekend,
I made the final commits that make this functionality feature
complete.</xhtml:p>
<xhtml:p>The new functionality provides several facets:</xhtml:p>
<xhtml:ul>
<xhtml:li>Stub test case classes for the HTTP versions of our Request and
Response objects, containing methods for setting up the request
environment (including setting GET, POST, and COOKIE parameters,
HTTP request headers, etc).</xhtml:li>
<xhtml:li><xhtml:code>Zend_Dom_Query</xhtml:code>, a class for using CSS selectors
(and XPath) to query (X)HTML and XML documents.</xhtml:li>
<xhtml:li>PHPUnit constraints that consume <xhtml:code>Zend_Dom_Query</xhtml:code>
and the Response object to make their comparisons.</xhtml:li>
<xhtml:li>A specialized PHPUnit test case that contains functionality for
bootstrapping an MVC application, dispatching requests, and a
variety of assertions that utilize the above constraints and
objects.</xhtml:li>
</xhtml:ul>
<xhtml:p>What might you want to test?</xhtml:p>
<xhtml:ul>
<xhtml:li>HTTP response codes</xhtml:li>
<xhtml:li>Whether or not the action resulted in a redirect, and where it
redirected to</xhtml:li>
<xhtml:li>Whether or not certain DOM artifacts are present (particularly
helpful for ensuring that the DOM structure is correct for JS
actions)</xhtml:li>
<xhtml:li>Presence of specific HTTP response headers and/or their
content</xhtml:li>
<xhtml:li>What module, controller, and/or action was used in the last
iteration of the dispatch loop</xhtml:li>
<xhtml:li>What route was selected</xhtml:li>
</xhtml:ul>
<xhtml:p>The aim is to make testing your controllers trivial and fun.
Let's look at an example:</xhtml:p>
<xhtml:pre><xhtml:code class="language-php hljs php" data-lang="php"><xhtml:span class="hljs-class"><xhtml:span class="hljs-keyword">class</xhtml:span> <xhtml:span class="hljs-title">UserControllerTest</xhtml:span> <xhtml:span class="hljs-keyword">extends</xhtml:span> <xhtml:span class="hljs-title">Zend_Test_PHPUnit_ControllerTestCase</xhtml:span>
</xhtml:span>{
    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">setUp</xhtml:span><xhtml:span class="hljs-params">()</xhtml:span>
    </xhtml:span>{
        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;bootstrap = <xhtml:span class="hljs-keyword">array</xhtml:span>(<xhtml:span class="hljs-keyword">$this</xhtml:span>, <xhtml:span class="hljs-string">'appBootstrap'</xhtml:span>);
        <xhtml:span class="hljs-keyword">parent</xhtml:span>::setUp();
    }

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">appBootstrap</xhtml:span><xhtml:span class="hljs-params">()</xhtml:span>
    </xhtml:span>{
        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;frontController-&gt;registerPlugin(
            <xhtml:span class="hljs-keyword">new</xhtml:span> Bugapp_Plugin_Initialize(<xhtml:span class="hljs-string">'test'</xhtml:span>)
        );
    }

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">testCallingControllerWithoutActionShouldPullFromIndexAction</xhtml:span><xhtml:span class="hljs-params">()</xhtml:span>
    </xhtml:span>{
        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;dispatch(<xhtml:span class="hljs-string">'/user'</xhtml:span>);
        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;assertResponseCode(<xhtml:span class="hljs-number">200</xhtml:span>);
        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;assertController(<xhtml:span class="hljs-string">'user'</xhtml:span>);
        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;assertAction(<xhtml:span class="hljs-string">'index'</xhtml:span>);
    }

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">testIndexActionShouldContainLoginForm</xhtml:span><xhtml:span class="hljs-params">()</xhtml:span>
    </xhtml:span>{
        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;dispatch(<xhtml:span class="hljs-string">'/user'</xhtml:span>);
        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;assertResponseCode(<xhtml:span class="hljs-number">200</xhtml:span>);
        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;assertSelect(<xhtml:span class="hljs-string">'form#login'</xhtml:span>);
    }

    <xhtml:span class="hljs-keyword">public</xhtml:span> <xhtml:span class="hljs-function"><xhtml:span class="hljs-keyword">function</xhtml:span> <xhtml:span class="hljs-title">testValidLoginShouldInitializeAuthSessionAndRedirectToProfilePage</xhtml:span><xhtml:span class="hljs-params">()</xhtml:span>
    </xhtml:span>{
        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;request
             -&gt;setMethod(<xhtml:span class="hljs-string">'POST'</xhtml:span>)
             -&gt;setPost(<xhtml:span class="hljs-keyword">array</xhtml:span>(
                 <xhtml:span class="hljs-string">'username'</xhtml:span> =&gt; <xhtml:span class="hljs-string">'foobar'</xhtml:span>,
                 <xhtml:span class="hljs-string">'password'</xhtml:span> =&gt; <xhtml:span class="hljs-string">'foobar'</xhtml:span>
             ));
        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;dispatch(<xhtml:span class="hljs-string">'/user/login'</xhtml:span>);
        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;assertTrue(Zend_Auth::getInstance()-&gt;hasIdentity());
        <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;assertRedirectTo(<xhtml:span class="hljs-string">'/user/view'</xhtml:span>);
    }
}
</xhtml:code></xhtml:pre>
<xhtml:p>You'll note that the <xhtml:code>setUp()</xhtml:code> method assigns a
callback to the <xhtml:code>$bootstrap</xhtml:code> property. This allows the
test case to call that callback to bootstrap the application;
alternately, you can specify the path to a file to include that
would do your bootstrapping. In the example above, I actually
simply add a single "initialization" plugin to the front controller
that takes care of bootstrapping my application (via the
<xhtml:code>routeStartup()</xhtml:code> hook).</xhtml:p>
<xhtml:p>I then have a few test cases. The first checks to ensure that
the default action is called when no action is provided. The second
checks to ensure that the login form is present on that page (by
using a CSS selector to find a form with the id of 'login'). The
third checks to see if I get a valid authentication session when
logging in with good credentials, and that I get redirected to the
appropriate location.</xhtml:p>
<xhtml:p>This is, of course, just the tip of the iceberg; I've created a
couple dozen other assertions as well.</xhtml:p>
<xhtml:p>You can preview the functionality in the <xhtml:a href="http://framework.zend.com/svn/framework/standard/incubator/">Zend
Framework standard incubator</xhtml:a>; look for
<xhtml:code>Zend_Test_PHPUnit_ControllerTestCase</xhtml:code> in there, as well
as the <xhtml:code>Zend_Test</xhtml:code> documentation in the documentation
tree (in human-readable DocBook XML).</xhtml:p>
<xhtml:p>For those of you who decide to start playing with this, I'd love
any feedback I can get. The best place to do so, however, is on the
fw-mvc mailing list; <xhtml:a href="http://framework.zend.com/wiki/display/ZFDEV/Contributing+to+Zend+Framework#ContributingtoZendFramework-Subscribetotheappropriatemailinglists">
instructions are on the ZF wiki</xhtml:a>.</xhtml:p>
<xhtml:div class="h-entry"><xhtml:img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&amp;u=79dd2ea1d4d8855944715d09ee4c86215027fa80&amp;s=140" alt="matthew"/> <xhtml:a class="u-url u-uid p-name" href="https://mwop.net/blog/182-Testing-Zend-Framework-MVC-Applications.html">
Testing Zend Framework MVC Applications</xhtml:a> was originally
published <xhtml:time class="dt-published" datetime="2008-06-30T12:00:00-05:00">30 June 2008</xhtml:time> on <xhtml:a href="https://mwop.net">https://mwop.net</xhtml:a> by <xhtml:a rel="author" class="p-author" href="https://mwop.net">Matthew Weier
O'Phinney</xhtml:a>.</xhtml:div>
</xhtml:div>
    </content>
  </entry>
  <entry xmlns:xhtml="http://www.w3.org/1999/xhtml">
    <title type="html"><![CDATA[Zend Framework Dojo Integration]]></title>
    <published>2008-05-21T10:57:00-05:00</published>
    <updated>2008-05-25T12:40:37-05:00</updated>
    <link rel="alternate" type="text/html" href="https://mwop.net/blog/176-Zend-Framework-Dojo-Integration.html"/>
    <id>https://mwop.net/blog/176-Zend-Framework-Dojo-Integration.html</id>
    <author>
      <name>Matthew Weier O'Phinney</name>
      <email>contact@mwop.net</email>
      <uri>https://mwop.net</uri>
    </author>
    <content xmlns:xhtml="http://www.w3.org/1999/xhtml" type="xhtml">
      <xhtml:div xmlns:xhtml="http://www.w3.org/1999/xhtml"><xhtml:p>I'm pleased to announce that <xhtml:a href="http://framework.zend.com/">Zend Framework</xhtml:a> will be partnering
with <xhtml:a href="http://dojotoolkit.org/">Dojo Toolkit</xhtml:a> to deliver
out-of-the-box Ajax and rich user interfaces for sites developed in
Zend Framework.</xhtml:p>
<xhtml:p>First off, for those ZF users who are using other Javascript
toolkits: Zend Framework will continue to be basically JS toolkit
agnostic. You will still be able to use whatever toolkit you want
with ZF applications. ZF will simply be shipping Dojo so that users
have a toolkit by default. Several points of integration have been
defined, and my hope is that these can be used as a blueprint for
community contributions relating to other javascript frameworks. In
the meantime, developers choosing to use Dojo will have a rich set
of components and integration points to work with.</xhtml:p>
<xhtml:p>The integration points we have defined for our initial release
are as follows:</xhtml:p>
<xhtml:ul>
<xhtml:li>
<xhtml:p><xhtml:strong>JSON-RPC Server:</xhtml:strong> We are re-working the
<xhtml:code>Zend_Json_Server</xhtml:code> that has been in our incubator since,
oh, what? 0.2.0? and never released to actually follow a
specification: <xhtml:a href="http://groups.google.com/group/json-rpc">JSON-RPC</xhtml:a>. This will
allow it to work seamlessly with Dojo, as well as other toolkits
that have JSON-RPC client implementations. I have actually
completed work on this, though the proposal is waiting to be
approved; if you want to check it out, you can find it in the
<xhtml:a href="http://framework.zend.com/svn/framework/branch/user/matthew/zed_json_server">
ZF svn</xhtml:a>.</xhtml:p>
<xhtml:p>The original <xhtml:code>Zend_Json_Server</xhtml:code> implementation will
be abandoned. It was never fully tested nor fully documented, which
has prevented its release. Additionally, since it implemented its
own ad-hoc standard, it did not provide the type of
interoperability that a true JSON-RPC server implementation will
provide. I am excited that we will finally be able to provide a
standards-compliant solution for general availability.</xhtml:p>
<xhtml:p>One final note: there are currently two different JSON-RPC
specifications, 1.0 and 2.0. Currently, the implementation I've
been working on will switch payload formats based on the request,
and can deliver different SMD formats appropriately as well.</xhtml:p>
</xhtml:li>
<xhtml:li>
<xhtml:p><xhtml:strong>dojo() View Helper:</xhtml:strong> Enabling Dojo for a page is
not typically as trivial as just loading the <xhtml:code>dojo.js</xhtml:code>
script — you have a choice of loading it from the AOL CDN or a
local path, and also may want or need to load additional dojo,
dijit, or dojox modules, specify custom modules and paths, specify
code to run at <xhtml:code>onLoad()</xhtml:code>, and specify stylesheets for
decorating dijits. On top of this, this information may change from
page to page, and may only be needed for a subset of pages. The
<xhtml:code>dojo()</xhtml:code> view helper will act as a <xhtml:a href="http://framework.zend.com/manual/en/zend.view.helpers.html#zend.view.helpers.initial.placeholder">
placeholder</xhtml:a> implementation, and facilitate all of the above
tasks, as well as take care of rendering the necessary
<xhtml:code>style</xhtml:code> and <xhtml:code>script</xhtml:code> elements in your
page.</xhtml:p>
</xhtml:li>
<xhtml:li>
<xhtml:p><xhtml:strong>Form Element implementations:</xhtml:strong> One area that
developers really leverage javascript and ajax toolkits is forms.
In particular, many types of form input can benefit from advanced
and rich user interfaces that only javascript can provide: calendar
choosers, time selectors, etc. Additionally, many like to use
client-side validation in order to provide instantaneous validation
feedback to users (instead of requiring a round-trip to the
server). We will be identifying a small group of form elements that
we feel solve the most relevant use cases, and write Dojo-specific
versions that can be utilized with <xhtml:code>Zend_Form</xhtml:code>. (One
thing to note: <xhtml:code>Zend_Form</xhtml:code>'s design already works very
well with Dojo, allowing many widgets and client-side validations
to be created by simply setting the appropriate element
attributes.)</xhtml:p>
</xhtml:li>
<xhtml:li>
<xhtml:p><xhtml:strong>dojo.data Compatibility:</xhtml:strong> <xhtml:code>dojo.data</xhtml:code>
defines a standard storage interface; services providing data in
this format can then be consumed by a variety of Dojo facilities to
provide highly flexible and dynamic content for your user
interfaces. We will be building a component that will create
<xhtml:code>dojo.data</xhtml:code> compatible payloads with which to respond to
<xhtml:code>XmlHttpRequest</xhtml:code>s; you will simply need to pass in the
data, and provide metadata regarding it.</xhtml:p>
</xhtml:li>
</xhtml:ul>
<xhtml:p>So, some examples are in order. First off,
<xhtml:code>Zend_Json_Server</xhtml:code> operates like all of ZF's server
components: if follows the <xhtml:a href="http://php.net/soap_soapserver_construct">SoapServer API</xhtml:a>. This
allows you to attach arbitrary classes and functions to the server
component. Additionally, it can build a Service Mapping Description
(SMD) that Dojo can consume in order to discover valid methods and
signatures. As an example, on the server side you could have the
following:</xhtml:p>
<xhtml:pre><xhtml:code class="language-php hljs php" data-lang="php"><xhtml:span class="hljs-comment">// /json-rpc.php</xhtml:span>
<xhtml:span class="hljs-comment">// Assumes you have a class 'Foo' with methods 'bar' and 'baz':</xhtml:span>
$server = <xhtml:span class="hljs-keyword">new</xhtml:span> Zend_Json_Server();
$server-&gt;setClass(<xhtml:span class="hljs-string">'Foo'</xhtml:span>)
       -&gt;setTarget(<xhtml:span class="hljs-string">'/json-rpc.php'</xhtml:span>)
       -&gt;setEnvelope(<xhtml:span class="hljs-string">'JSON-RPC-1.0'</xhtml:span>)
       -&gt;setDojoCompatible(<xhtml:span class="hljs-keyword">true</xhtml:span>);

<xhtml:span class="hljs-comment">// For GET requests, simply return the service map</xhtml:span>
<xhtml:span class="hljs-keyword">if</xhtml:span> (<xhtml:span class="hljs-string">'GET'</xhtml:span> == $_SERVER[<xhtml:span class="hljs-string">'REQUEST_METHOD'</xhtml:span>]) {
    $smd = $server-&gt;getServiceMap();
    header(<xhtml:span class="hljs-string">'Content-Type: application/json'</xhtml:span>);
    <xhtml:span class="hljs-keyword">echo</xhtml:span> $smd;
    <xhtml:span class="hljs-keyword">exit</xhtml:span>;
}

$server-&gt;handle();
</xhtml:code></xhtml:pre>
<xhtml:p>On your view script side, you might then do the following:</xhtml:p>
<xhtml:pre><xhtml:code class="language-php hljs php" data-lang="php">&lt;h2&gt;Dojo JSON-RPC Demo&lt;/h2&gt;
&lt;input name=\<xhtml:span class="hljs-string">"foo\" type=\"button\" value=\"Demo\" onClick=\"demoRpc()\"/&gt;
&lt;? 
$this-&gt;dojo()-&gt;setLocalPath('/js/dojo/dojo.js')
             -&gt;addStyleSheetModule('dijit.themes.tundra')
             -&gt;requireModule('dojo.rpc.JsonService');
$this-&gt;headScript()-&gt;captureStart(); ?&gt;
function demoRpc()
{
    var myObject = new dojo.rpc.JsonService('/json-rpc.php');
    console.log(myObject.bar());
}
&lt;? $this-&gt;headScript()-&gt;captureEnd() ?&gt;
</xhtml:span></xhtml:code></xhtml:pre>
<xhtml:p>And, finally, in your layout script, you might have the
following:</xhtml:p>
<xhtml:pre><xhtml:code class="language-php hljs php" data-lang="php"><xhtml:span class="hljs-meta">&lt;?</xhtml:span>= <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;doctype() <xhtml:span class="hljs-meta">?&gt;</xhtml:span>
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;...&lt;/title&gt;
        <xhtml:span class="hljs-meta">&lt;?</xhtml:span>= <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;dojo() <xhtml:span class="hljs-meta">?&gt;</xhtml:span>
        <xhtml:span class="hljs-meta">&lt;?</xhtml:span>= <xhtml:span class="hljs-keyword">$this</xhtml:span>-&gt;headScript() <xhtml:span class="hljs-meta">?&gt;</xhtml:span>
    &lt;/head&gt;
    &lt;body <xhtml:span class="hljs-class"><xhtml:span class="hljs-keyword">class</xhtml:span>=\"<xhtml:span class="hljs-title">tundra</xhtml:span>\"&gt;
        &lt;?= $<xhtml:span class="hljs-title">this</xhtml:span>-&gt;<xhtml:span class="hljs-title">layout</xhtml:span>()-&gt;<xhtml:span class="hljs-title">content</xhtml:span> ?&gt;
    &lt;/<xhtml:span class="hljs-title">body</xhtml:span>&gt;
&lt;/<xhtml:span class="hljs-title">html</xhtml:span>&gt;
</xhtml:span></xhtml:code></xhtml:pre>
<xhtml:p>The example doesn't do much — it simply logs the results of the
JSON-RPC call to the console — but it demonstrates a number of
things:</xhtml:p>
<xhtml:ul>
<xhtml:li><xhtml:strong>dojo() View Helper:</xhtml:strong> The example shows using
dojo from a local path relative to the server's document root;
using the 'Tundra' stylesheet shipped with Dojo and attaching it to
the layout; capturing a required module
(<xhtml:code>dojo.rpc.JsonService</xhtml:code>); and rendering the necessary
Dojo stylsheet and script includes in the layout.</xhtml:li>
<xhtml:li><xhtml:strong>JSON-RPC client:</xhtml:strong> Dojo requires that you point
the JsonService to an endpoint that delivers a Service Mapping
Description; in this example, I use any GET request to return the
SMD. Once the SMD is retrieved, any methods exposed are available
to the Javascript object as if they were internal methods — hence
<xhtml:code>myObject.bar()</xhtml:code>. Dojo's current implementation performs
all other requests as POST requests, passing the data via the raw
POST body.</xhtml:li>
</xhtml:ul>
<xhtml:p>There will be more to come in the future, and I will be blogging
about developments as I get more proposals up and code into the
repository. All in all, this is a very exciting collaboration, and
should help provide ZF developers the ability to rapidly create web
applications with rich, dynamic user interfaces.</xhtml:p>
<xhtml:p><xhtml:strong>Update:</xhtml:strong> <xhtml:a href="http://andigutmans.blogspot.com/2008/05/dojo-and-zend-framework-partnership.html">
Andi has posted an FAQ on our integration</xhtml:a>.</xhtml:p>
<xhtml:div class="h-entry"><xhtml:img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&amp;u=79dd2ea1d4d8855944715d09ee4c86215027fa80&amp;s=140" alt="matthew"/> <xhtml:a class="u-url u-uid p-name" href="https://mwop.net/blog/176-Zend-Framework-Dojo-Integration.html">Zend
Framework Dojo Integration</xhtml:a> was originally published
<xhtml:time class="dt-published" datetime="2008-05-21T10:57:00-05:00">21
May 2008</xhtml:time> on <xhtml:a href="https://mwop.net">https://mwop.net</xhtml:a>
by <xhtml:a rel="author" class="p-author" href="https://mwop.net">Matthew
Weier O'Phinney</xhtml:a>.</xhtml:div>
</xhtml:div>
    </content>
  </entry>
  <entry xmlns:xhtml="http://www.w3.org/1999/xhtml">
    <title type="html"><![CDATA[2007 Retrospective]]></title>
    <published>2008-01-01T16:49:26-06:00</published>
    <updated>2008-01-03T06:22:35-06:00</updated>
    <link rel="alternate" type="text/html" href="https://mwop.net/blog/154-2007-Retrospective.html"/>
    <id>https://mwop.net/blog/154-2007-Retrospective.html</id>
    <author>
      <name>Matthew Weier O'Phinney</name>
      <email>contact@mwop.net</email>
      <uri>https://mwop.net</uri>
    </author>
    <content xmlns:xhtml="http://www.w3.org/1999/xhtml" type="xhtml">
      <xhtml:div xmlns:xhtml="http://www.w3.org/1999/xhtml"><xhtml:p>2007 was a busy year, both personally and professionally. I
won't go into the personal too much, because, well, it's personal,
and some of the details are simply inappropriate for blogging
material.</xhtml:p>
<xhtml:p>Here's the short version:</xhtml:p>
<xhtml:ul>
<xhtml:li>One trip to Belgium and The Netherlands.</xhtml:li>
<xhtml:li>Two trips to Israel.</xhtml:li>
<xhtml:li>Two trips to Atlanta, GA (not counting the return trip from
Europe, when I was stranded for a day due to storms in the
Northeast).</xhtml:li>
<xhtml:li>Three different user groups attended, with three
presentations.</xhtml:li>
<xhtml:li>One major Zend Framework release</xhtml:li>
<xhtml:li>One PEAR release.</xhtml:li>
<xhtml:li>One podcast.</xhtml:li>
<xhtml:li>One webinar.</xhtml:li>
<xhtml:li>One book published.</xhtml:li>
<xhtml:li>One conference attended.</xhtml:li>
</xhtml:ul>
<xhtml:p>What follows is my month-by-month breakdown:</xhtml:p>
<xhtml:h3>January</xhtml:h3>
<xhtml:p>I finished up the last of my three chapters for <xhtml:a href="http://sitepoint.com/books/phpant2/">The PHP Anthology, 2nd
Edition</xhtml:a>, and promptly used my advance to buy the family a
Wii.</xhtml:p>
<xhtml:p>I was also introduced to <xhtml:a href="http://jansch.nl/">Ivo
Jansch</xhtml:a> through work, and had him wow me with one of the
<xhtml:a href="http://ibuildings.nl">ibuildings</xhtml:a> products.</xhtml:p>
<xhtml:h3>February</xhtml:h3>
<xhtml:p>Mid-month, my boss at <xhtml:a href="http://www.zend.com/">Zend</xhtml:a>,
Boaz, got the go-ahead to use the ibuildings WDE platform to build
our new website CMS; part of the plan would include training at the
ibuildings home office in Vlissingen, NL… which meant I had to get
my passport pronto.</xhtml:p>
<xhtml:p>Towards the end of the month, I was invited to <xhtml:a href="http://bostonphp.org">BostonPHP</xhtml:a> to present building a simple
<xhtml:a href="http://framework.zend.com/">Zend Framework</xhtml:a> CRUD
application, along with <xhtml:a href="http://hagunbu.ch/">Chuck
Hagenbuch</xhtml:a> of the <xhtml:a href="http://horde.org/">HORDE project</xhtml:a>.
While there, <xhtml:a href="http://cake.insertdesignhere.com/">Nate
Abele</xhtml:a> heckled me, and then joined Chuck and myself for an
impromptu framework panel; a good time was had by all.</xhtml:p>
<xhtml:h3>March</xhtml:h3>
<xhtml:p>I flew to Brussels, Belgium, where I met my supervisor, Boaz, so
we could go to Vlissingen. We spent the day in Brussels, walking
around and visiting such sites as the Cathedral of St. Michael, La
Grand Place, and the Mannekin Pis.</xhtml:p>
<xhtml:p>Our visit to ibuildings was very productive, and I was very
impressed by the team there; everybody was very knowledgeable and
skilled. I presented a Zend Framework overview, as well as an
abbreviated version of the Best Practices talk I'd given with Mike
Naberezny at the 2006 ZendCon; the latter ignited a ton of
questions and enthusiasm.</xhtml:p>
<xhtml:p>On returning home, I had a ton of work to do on the zend.com
CMS, and this continued in spurts through November. The job was
made much easier by the ibuildings WDE product.</xhtml:p>
<xhtml:p>I closed out a ton of MVC issues in the Zend Framework, and we
released the first beta version late in the month.</xhtml:p>
<xhtml:h3>April</xhtml:h3>
<xhtml:p>At the beginning of the month, our landlord threw us for a loop
and announced he was going to sell our apartment… meaning that we
either had to step up our plans to purchase a home, or start
looking for a new rental. Ultimately, we ended up looking for a
rental, due to time constraints. The next two months would be
highlighted with the look for a new place as well as countless
showings of our apartment to potential buyers.</xhtml:p>
<xhtml:p>Mid-month, we packed up the family and flew down to Atlanta, GA,
to visit my wife's family. While there, we were able to go to the
Atlanta Zoo and see Mei Lan, their baby panda — way cute!</xhtml:p>
<xhtml:h3>May</xhtml:h3>
<xhtml:p>Mid-month, we found a new place in Richmond, VT — a small
village about 10 minutes from Burlington, near where we originally
lived when we first moved to Vermont.</xhtml:p>
<xhtml:p>During the first RC for Zend Framework, released at the end of
the month, I introduced the ViewRenderer, a feature for
auto-rendering views based on the current controller and action
name — a feature common to many frameworks. However, it ostensibly
broke a ton of existing applications by being enabled by default —
not one of my more popular decisions. Since the 1.0.0 release, I've
heard little grumbling about it, and it's now often cited as an
ease-of-use feature — go figure.</xhtml:p>
<xhtml:h3>June</xhtml:h3>
<xhtml:p>The first week of June, I flew to Tel Aviv, Israel, to start
training people on the new CMS, as well as to work with our entire
ebiz team to finalize the work plan for completing the CMS. It was,
needless to say, my first time to Israel or the Middle East, and I
was constantly confronted with culture shock. Europe was an easy
transition to make, but Israel was completely foreign to me —
everything from the way people drove, to the architecture, to the
food was different. Unfortunately, I arrived a day late due to a
flight cancellation, and missed the tour of Jerusalem my supervisor
had planned for all of us. However, he took me to the city of
Jaffa, an Arabic city where the Israeli's originally tried to
settle before building Tel Aviv to the north. The architecture was
amazing, as were the winding, narrow streets of the old city.</xhtml:p>
<xhtml:p>I was also told during this trip that Andi had requested
transferring me full-time to the Zend Framework team. I would spend
the next week or two weighing my options, and ultimately decided to
do so.</xhtml:p>
<xhtml:p>A week after I returned, we moved into our new rental in
Richmond. The kids love the new place, which has a bedroom for each
of them, a yard, and porches on each entrance.</xhtml:p>
<xhtml:p>Somehow, I also found time to record my first (and so far only)
<xhtml:a href="http://devzone.zend.com/article/2140-PHP-Abstract-Podcast-Episode-2---Backup-or-Die">
PHP Abstract podcast</xhtml:a>.</xhtml:p>
<xhtml:h3>July</xhtml:h3>
<xhtml:p>We released <xhtml:a href="http://framework.zend.com/">Zend
Framework</xhtml:a> 1.0.0 at the beginning of the month, marking our
first stable release. While many still view it as incomplete, the
overwhelming feedback has been positive, and we've had over 2
million downloads to date.</xhtml:p>
<xhtml:p>I accepted the transfer to the Zend Framework team, but the
condition was made that I would stay part-time on the ebiz team
until the new site was launched. This meant that the next 5 months
were spent splitting my time between the two projects, often
working late and on weekends to get work done.</xhtml:p>
<xhtml:p>Towards the end of the month, we took a long weekend camping in
Vermont's Northeast Kingdom. The weather was unseasonably wet, but
we persevered and had a great time. 5 days of offline time was
definitely needed!</xhtml:p>
<xhtml:p>I also finally released the first stable version of <xhtml:a href="http://pear.php.net/packages/File_Fortune">File_Fortune</xhtml:a> on
<xhtml:a href="http://pear.php.net/">PEAR</xhtml:a>, over a year since I'd
first proposed it. The package interfaces with
<xhtml:code>mod_fortune</xhtml:code> files, allowing both the ability to read
and write such files, with full binary compatability.</xhtml:p>
<xhtml:h3>August</xhtml:h3>
<xhtml:p>Not much to report in August, except work, work, and more
work.</xhtml:p>
<xhtml:h3>September</xhtml:h3>
<xhtml:p>My ebiz supervisor, Boaz, flew me to Tel Aviv for a second time,
this time to perform a "brain dump" for the rest of the team before
I transitioned fully out of the team, and also to help setup our
new data center and release procedures. This time, Boaz took me to
Jerusalem himself during my last full day in the country. If you've
never been to the city, you should definitely put it on your list
of things to do before you die. With my degree in religion, the
place was full of meaning for me, but it would be putting it
lightly to say that religion is palpable in the air there. We
visited the Wailing Wall, the Via Dolorosa, the Church of the Holy
Sepulchre, and listened to the muezzins sing the call to prayer for
the muslims. The tour was simply amazing.</xhtml:p>
<xhtml:p>A few days after I returned, I flew down to New York City for a
special meeting of <xhtml:a href="http://nyphp.org/">NYPHP</xhtml:a>, where
<xhtml:a href="http://blogs.zend.com/author/mark/">Mark de Visser</xhtml:a>
presented on various Zend products and initiatives, and I gave a
Zend Framework overview.</xhtml:p>
<xhtml:p>A week after the NYPHP presentation, I did a <xhtml:a href="http://www.zend.com/webinars">zend.com webinar</xhtml:a> on the Zend
Framework MVC layer.</xhtml:p>
<xhtml:h3>October</xhtml:h3>
<xhtml:p>October was the month of <xhtml:a href="http://www.zendcon.con/">ZendCon</xhtml:a>. I presented a full-day
tutorial on best practices and unit testing with <xhtml:a href="http://sebastian-bergmann.de/">Sebastian Bergmann</xhtml:a> and <xhtml:a href="http://naberezny.com/">Mike Naberezny</xhtml:a>; despite the length and
subject matter, we were SRO for most of the day.</xhtml:p>
<xhtml:p>I also did a main-stage presentation on Zend Framework's MVC
components, directly following <xhtml:a href="http://terrychay.com/blog/">Terry Chay</xhtml:a> — an intimidating
situation at best. From the feedback I've seen, the presentation
was well-received, and I had somewhere between 120 and 150
attendees — phenomenal! (Even more amazing was how many people were
familiar with MVC in general!)</xhtml:p>
<xhtml:p>One great thing about the conference was the fact that I got to
network with a number of framework developers, both Zend Framework
and otherwise, including Nate Abele of CakePHP as well as <xhtml:a href="http://paul-m-jones.com">Paul M. Jones</xhtml:a> of <xhtml:a href="http://solarphp.com">the Solar framework</xhtml:a>. Many good
conversations were had.</xhtml:p>
<xhtml:p>Late in the month, <xhtml:a href="http://sitepoint.com/books/phpant2/">The PHP Anthology, 2nd
Edition</xhtml:a>, my first published book as an author, was finally
released!</xhtml:p>
<xhtml:h3>November</xhtml:h3>
<xhtml:p>I spent much of the month working on <xhtml:a href="http://framework.zend.com/wiki/display/ZFPROP/Zend_Layout">Zend_Layout</xhtml:a>,
a much requested component that simplifies and automates Two Step
Views in Zend Framework. I also started work implementing <xhtml:a href="http://framework.zend.com/wiki/pages/viewpage.action?pageId=33071">
Zend_View Enhanced</xhtml:a>, a set of view helpers for making complex
views with <xhtml:code>Zend_View</xhtml:code> possible.</xhtml:p>
<xhtml:p>I also started playing with <xhtml:a href="http://twitter.com/">Twitter</xhtml:a> a bit, and came up with a
<xhtml:a href="http://framework.zend.com/wiki/display/ZFPROP/Zend_Service_Twitter">
Zend_Service_Twitter</xhtml:a> proposal for interacting with the Twitter
API via PHP.</xhtml:p>
<xhtml:p>And finally, the Sunday before Thanksgiving, we finally launched
the new <xhtml:a href="http://www.zend.com/">Zend.com</xhtml:a> site, which was
well-received in the blogosphere.</xhtml:p>
<xhtml:h3>December</xhtml:h3>
<xhtml:p>A goal I've had for some time has been to form a PHP user group
in the Burlington area. A friend of mine pointed out to me sometime
this fall that there's actually already <xhtml:a href="http://groups.google.com/group/Burlington-VT-PHP">a Google
Group</xhtml:a> formed; he and the original founder started planning a
meeting for early December. I spoke at this inaugural meeting,
presenting Zend Framework's MVC layer yet again; a good time was
had by all, and a lot of enthusiasm for future meetings was
generated.</xhtml:p>
<xhtml:p>I finished up <xhtml:code>Zend_Layout</xhtml:code> and
<xhtml:code>Zend_View</xhtml:code> Enhanced with the help of Ralph Schindler,
and got a new proposal up for <xhtml:a href="http://framework.zend.com/wiki/display/ZFPROP/Zend_Form">Zend_Form</xhtml:a>,
just in time for my holidays to begin — 11 days with family and
with little to no internet connectivity during a trip to Atlanta,
GA for one of only a handful of Christmases I've spent without
snow.</xhtml:p>
<xhtml:h2>Summary</xhtml:h2>
<xhtml:p>This year was <xhtml:em>incredibly</xhtml:em> busy — three cross-seas trips,
one cross-continent trip, a move, and several trips along the
Eastern Seaboard; three user group presentations, and eight
presentations over the course of the year; one conference; one
move; one PEAR release; one podcast; one webinar; one book; and
countless hours of programming.</xhtml:p>
<xhtml:p>My goals for the coming year? I'm too tired to even think about
it ;-).</xhtml:p>
<xhtml:div class="h-entry"><xhtml:img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&amp;u=79dd2ea1d4d8855944715d09ee4c86215027fa80&amp;s=140" alt="matthew"/> <xhtml:a class="u-url u-uid p-name" href="https://mwop.net/blog/154-2007-Retrospective.html">2007
Retrospective</xhtml:a> was originally published <xhtml:time class="dt-published" datetime="2008-01-01T16:49:26-06:00">1 January
2008</xhtml:time> on <xhtml:a href="https://mwop.net">https://mwop.net</xhtml:a> by
<xhtml:a rel="author" class="p-author" href="https://mwop.net">Matthew
Weier O'Phinney</xhtml:a>.</xhtml:div>
</xhtml:div>
    </content>
  </entry>
</feed>
