<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/">
  <channel>
    <title>Blog entries tagged mvc :: mwop.net</title>
    <description>Blog entries tagged mvc :: mwop.net</description>
    <pubDate>Mon, 09 Jun 2014 12:00:00 -0500</pubDate>
    <generator>Laminas_Feed_Writer 2 (https://getlaminas.org)</generator>
    <link>https://mwop.net/blog/tag/mvc</link>
    <atom:link rel="self" type="application/rss+xml" href="https://mwop.net/blog/tag/mvc/rss.xml"/>
    <item>
      <title>Better Understanding Controllers Through Basic Patterns</title>
      <pubDate>Mon, 09 Jun 2014 12:00:00 -0500</pubDate>
      <link>https://mwop.net/blog/2014-06-09-controllers-as-facades.html</link>
      <guid>https://mwop.net/blog/2014-06-09-controllers-as-facades.html</guid>
      <author>contact@mwop.net (Matthew Weier O'Phinney)</author>
      <dc:creator>Matthew Weier O'Phinney</dc:creator>
      <content:encoded><![CDATA[<p><a href="http://paul-m-jones.com/">Paul M. Jones</a> has started an interesting discussion
rethinking the
<a href="http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller">MVC</a>
pattern as applied to the web, which he has dubbed <a href="https://github.com/pmjones/mvc-refinement">Action-Domain-Responder (ADR)</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.</p>
<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.</p>
<p>Controllers — Actions in ADR — can be explained as <em>facades</em>.</p>


<h2>Definitions</h2>
<p>The design pattern <em>Facade</em> originates in the book &quot;Design Patterns: Elements
of Reusable Object Oriented Software,&quot; written by Erich Gamma, Ralph Johnson,
Richard Helm, and John Vlissides. Over the years, that book has come to be
referred to as the &quot;Gang of Four&quot;, referring to the four authors, and often
abbreviated as &quot;GoF&quot;.</p>
<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 <em>relationships</em> between classes or objects in a
system. Specifically:</p>
<blockquote>
<p>Facade defines a simplifed interface to a complex system.</p>
</blockquote>
<p><a href="http://en.wikipedia.org/wiki/Facade_pattern">Wikipedia has a general entry on the pattern</a>
as well, and provides some other general characteristics of a Facade:</p>
<ul>
<li>A Facade creates a convenience method around a set of operations, thus
reducing the complexity of operations.</li>
<li>A Facade reduces the immediate dependencies of the calling code (they call
the Facade, not the underlying code).</li>
</ul>
<h2>Facade Example</h2>
<p>As an example, let's consider the following workflow:</p>
<ul>
<li>Marshal some objects</li>
<li>Munge some incoming data</li>
<li>Call a validator</li>
<li>If the data does not validate, raise an error</li>
<li>Start a transaction</li>
<li>Pass data to several different tables</li>
<li>Commit the transaction</li>
<li>Log the changes</li>
<li>Email notifications</li>
</ul>
<p>Now, we could just write the code:</p>
<pre><code class="language-php hljs php" data-lang="php">$db     = <span class="hljs-keyword">new</span> Db($connectionConfig);
$log    = <span class="hljs-keyword">new</span> Logger($loggerConfig);
$mailer = <span class="hljs-keyword">new</span> Mailer($mailerConfig);
$data   = array_merge_recursive($_POST, $_FILES);

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

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

$log-&gt;info(<span class="hljs-string">'Finished a transaction'</span>);
$mailer-&gt;send(<span class="hljs-string">'New transaction'</span>)
</code></pre>
<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.</p>
<p>A facade would wrap this logic:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DataTransaction</span>
</span>{
    <span class="hljs-keyword">protected</span> $db;
    <span class="hljs-keyword">protected</span> $logger;
    <span class="hljs-keyword">protected</span> $mailer;

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

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

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

        <span class="hljs-keyword">$this</span>-&gt;logger-&gt;info(<span class="hljs-string">'Finished a transaction'</span>);
        <span class="hljs-keyword">$this</span>-&gt;mailer-&gt;send(<span class="hljs-string">'New transaction'</span>)
    }
}
</code></pre>
<p>You would then instantiate the facade — likely using an
<a href="http://en.wikipedia.org/wiki/Inversion_of_control">Inversion of Control</a>
container to inject the dependencies — and then invoke it:</p>
<pre><code class="language-php hljs php" data-lang="php">$dataTransaction-&gt;execute(array_merge_recursive($_POST, $_FILES));
</code></pre>
<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.</p>
<h2>Controllers and Actions</h2>
<p>Hopefully you can see where I'm going with this.</p>
<blockquote>
<p>Controllers in MVC, and Actions in ADR, are best characterized as Facades.</p>
</blockquote>
<p>You can define Controllers or Actions as Facades for the following operations:</p>
<ul>
<li>Marshaling arguments from the request.</li>
<li>Invoking any domain/model logic, using arguments marshaled from the request.</li>
<li>Marshaling and returning a response/responder.</li>
</ul>
<p>I think characterizing Controllers and Actions as Facades has some huge
benefits. In both
<a href="http://blog.astrumfutura.com/archives/373-The-M-in-MVC-Why-Models-are-Misunderstood-and-Unappreciated.html">PHP</a>
and <a href="https://www.google.com/search?q=fat+controllers+rails">Rails</a>, we've
witnessed the problems that arise from so-called &quot;Fat Controllers&quot; —
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:</p>
<ul>
<li>Adherence to the <a href="http://en.wikipedia.org/wiki/Single_responsibility_principle">Single Responsibility Principle</a></li>
<li>Adherence to the <a href="http://en.wikipedia.org/wiki/Dependency_inversion_principle">Dependency Inversion Priniciple</a></li>
<li>Adherence to the <a href="http://en.wikipedia.org/wiki/Law_Of_Demeter">Law of Demeter</a></li>
<li>Ability to unit test our Controllers and Actions (instead of requiring integration tests with complex configuration and setup)</li>
<li>The possibility of <a href="http://en.wikipedia.org/wiki/Hierarchical_model%E2%80%93view%E2%80%93controller">hierarchical MVC</a> (usually tacked on, or poorly implemented)</li>
<li>Clarity of purpose when creating Controllers and Actions (do only those three things)</li>
</ul>
<p>Defining them as Facades for these three specific operations means we push
logic into specific domains, achieving a proper
<a href="http://en.wikipedia.org/wiki/Separation_of_concerns">separation of concerns</a>.
Anything that falls outside those three operations gets pushed elsewhere:</p>
<ul>
<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.</li>
<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).</li>
</ul>
<p>For me, thinking of Controllers and Actions as Facades has an additional
benefit: it describes rather complex <em>architectural</em> patterns in terms of
<em>basic design patterns</em>. I find the more I can reduce the complexity of a
definition, the more likely I will understand and use it correctly.</p>
<h3>Epilogue</h3>
<p>Consider this post a <em>refinement</em> of the MVC and ADR architectural patterns — a
way of describing them in terms of more fundamental design patterns.</p>
<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.</p>


<div class="h-entry">
    <img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&u=79dd2ea1d4d8855944715d09ee4c86215027fa80&s=140" alt="matthew">
    <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</a> was originally
    published <time class="dt-published" datetime="2014-06-09T12:00:00-05:00">9 June 2014</time>
    on <a href="https://mwop.net">https://mwop.net</a> by
    <a rel="author" class="p-author" href="https://mwop.net">Matthew Weier O&#039;Phinney</a>.
</div>
]]></content:encoded>
      <slash:comments>0</slash:comments>
    </item>
    <item>
      <title>Model Infrastructure</title>
      <pubDate>Mon, 05 Jan 2009 11:51:07 -0600</pubDate>
      <link>https://mwop.net/blog/202-Model-Infrastructure.html</link>
      <guid>https://mwop.net/blog/202-Model-Infrastructure.html</guid>
      <author>contact@mwop.net (Matthew Weier O'Phinney)</author>
      <dc:creator>Matthew Weier O'Phinney</dc:creator>
      <content:encoded><![CDATA[<p>In the last two entries in this series on models, I covered <a href="/blog/200-Using-Zend_Form-in-Your-Models.html">using forms as input filters</a> and <a href="/blog/201-Applying-ACLs-to-Models.html">integrating ACLs into models</a>. In this entry, I tackle some potential infrastructure for your models.</p>
<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.</p>


<p>I <em>am</em>, however, a fan of the <a href="http://en.wikipedia.org/wiki/Domain_model">Domain Model</a>. To quote wikipedia,</p>
<blockquote>
<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.</p>
</blockquote>
<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 <em>do</em> things with either incoming or stored data — or even completely autonomously.</p>
<p>As an example, when starting with Zend Framework, it's tempting to use <code>Zend_Db_Table</code> and <code>Zend_Db_Table_Row</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 <em>all</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.</p>
<p>Additionally, if in the future you wish to refactor your application to utilize <a href="http://www.danga.com/memcached/">memcached</a> or a web service, you now not only need to completely rewrite your models, but also all <em>consumer</em> code, because the return values from your model have changed.</p>
<p>So, if you're not going to use an ORM or a Table Data Gateway directly, how should you architect your model infrastructure?</p>
<h2>What are you modelling?</h2>
<p>The principal question to ask is, &quot;What am I modelling?&quot;</p>
<p>Let's look at a rather standard website issue: user management. Typically, you'll get a requirement such as, &quot;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.&quot; That's assuming you actually get good requirement documents, of course.</p>
<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 <em>what are you modelling?</em></p>
<p>The answer is: <em>users</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 <em>roles</em>. There's also the matter of what a <em>group</em> of users might look like (since the administrator needs to be able to <em>list</em> users), and how we might want to work with groups.</p>
<p>Let's start with narrowing down the definition of a user:</p>
<ul>
<li>A user consists of the following metadata:
<ul>
<li>Unique username</li>
<li>Full name</li>
<li>Email address</li>
<li>Hashed password</li>
<li>A role within the site</li>
</ul>
</li>
<li>A <em>new user</em> must provide a unique username, their full name, a valid email address, and a password and password verification.</li>
<li>An <em>authenticated</em> user is one who has provided a matching combination of <em>username</em> and <em>password</em>.</li>
<li>A user may <em>logout</em> of the site.</li>
<li>A user may be granted a new role.</li>
<li>A user may be marked as banned.</li>
</ul>
<p>Notice the fifth piece of metadata? It mentions a &quot;role&quot;? That's something to do with our ACLs — which means that ACLs are part of our user domain. I'll touch on this later.</p>
<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 <code>Zend_Form</code> to fulfill that role. Authentication on the web usually consists of both <em>validating</em> submitted credentials against <em>stored</em> credentials, as well as <em>persisting</em> a verified identity in the <em>session</em>. This means that other parts of our model include <em>data persistence</em> and <em>session management</em>. We'll use <code>Zend_Db_Table</code> for data persistence, and <code>Zend_Auth</code>/<code>Zend_Session</code> for identity persistence.</p>
<p>Now, let's turn to defining <em>lists</em> of users:</p>
<ul>
<li>Administrators should be able to pull lists of users. These lists should allow for:
<ul>
<li>Sorting by username, full name, email address, or role</li>
<li>Pagination (i.e., pulling a set number of users from a given offset)</li>
<li>Iteration</li>
</ul>
</li>
<li>Administrators should be able to specify criteria for selecting users to list.</li>
</ul>
<p>These criteria indicate that a <em>list</em> of users should be an object. This list will likely implement the SPL class <code>Traversable</code> in some fashion. Looking at this criteria, another aspect of our model becomes clear: we are modelling <em>user selection</em> — which includes the ability to specify sorting and selection criteria. The <em>user selection</em> object would return a <em>user list</em> object, which would consist of <em>user</em> objects. User objects define ACL roles and can authenticate users.</p>
<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).</p>
<p>Now that we know what we're modelling, let's look at some of the objects in our model.</p>
<h2>Gateway to the Domain</h2>
<p>We've identified &quot;user management&quot; as the purpose of our model. This will include retrieving and saving individual users, as well as selecting groups of users.</p>
<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.</p>
<p>This object will be what I'll term our domain <em>gateway</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.</p>
<p>The API of this gateway might look something like the following.</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-comment">// Instantiate the gateway</span>
$userGateway = <span class="hljs-keyword">new</span> Spindle_Model_UserGateway();

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

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

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

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

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

<span class="hljs-comment">// Create a new user:</span>
$user = $userGateway-&gt;createUser(<span class="hljs-keyword">array</span>(
    <span class="hljs-string">'username'</span> =&gt; <span class="hljs-string">'matthew'</span>,
    <span class="hljs-string">'fullname'</span> =&gt; <span class="hljs-string">"Matthew Weier O'Phinney"</span>,
    <span class="hljs-string">'password'</span> =&gt; <span class="hljs-string">'secret'</span>,
    <span class="hljs-string">'email'</span>    =&gt; <span class="hljs-string">'matthew@local'</span>,
));
</code></pre>
<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.</p>
<h2>Of Value Objects and Record Sets</h2>
<p>To other objects we've identified in our model are <em>users</em> and <em>user lists</em>. How should we define these?</p>
<p>The traditional answer is as <em>value</em> or <em>data transfer</em> objects and <em>record sets</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.</p>
<h3>Value Objects</h3>
<p>Martin Fowler makes a differentiation between value objects and data transfer objects in his book &quot;Patterns of Enterprise Application Architecture&quot; (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.</p>
<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 &quot;value object,&quot; as it will be familiar to those with a Java background, and to indicate that we are aggregating a unique <em>value</em> that is the sum of a number of <em>attributes</em>.</p>
<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.</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Spindle_Model_User</span>
</span>{
    <span class="hljs-keyword">protected</span> $_data = <span class="hljs-keyword">array</span>(
        <span class="hljs-string">'username'</span> =&gt; <span class="hljs-keyword">null</span>,
        <span class="hljs-string">'email'</span>    =&gt; <span class="hljs-keyword">null</span>,
        <span class="hljs-string">'fullname'</span> =&gt; <span class="hljs-string">''</span>,
        <span class="hljs-string">'role'</span>     =&gt; <span class="hljs-string">'guest'</span>,
    );

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

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

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

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

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

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

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

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">__unset</span><span class="hljs-params">($name)</span>
    </span>{
        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">isset</span>(<span class="hljs-keyword">$this</span>-&gt;$name)) {
            <span class="hljs-keyword">$this</span>-&gt;_data[$name] = <span class="hljs-keyword">null</span>;
        }
    }
}
</code></pre>
<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.)</p>
<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 <code>Zend_Db_Table</code> within your model, you might want to add the ability for your value object to accept a <code>Zend_Db_Table_Row</code> object, and pull its values from there:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Spindle_Model_User</span>
</span>{
    <span class="hljs-comment">/* ... */</span>

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

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

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

    <span class="hljs-comment">/* ... */</span>
}
</code></pre>
<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.</p>
<p>Now, what about data integrity? This is where <code>Zend_Form</code> comes into play. Don't think of <code>Zend_Form</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:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Spindle_Model_User</span>
</span>{
    <span class="hljs-comment">/* ... */</span>

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

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

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

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

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

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

    <span class="hljs-comment">/* ... */</span>
}
</code></pre>
<p>One note: if your model contains metadata that will never be represented as part of a form, you shoould look into using <code>Zend_Filter_Input</code> or custom validation chains instead of <code>Zend_Form</code>. That's outside the scope of this article, however.</p>
<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 <em>gateway</em> into objects, and then pull what I need from it. Let's look at how that might work for saving the user.</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Spindle_Model_User</span>
</span>{
    <span class="hljs-comment">/* ... */</span>

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

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

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

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

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

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

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

    <span class="hljs-comment">/* ... */</span>
}
</code></pre>
<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 <code>Zend_Db_Table</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.</p>
<p>Another requirement we identified was that a user be able to authenticate itself. This can be done trivially by implementing <code>Zend_Auth_Adapter_Interface</code>:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Spindle_Model_User</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Zend_Auth_Adapter_Interface</span>
</span>{
    <span class="hljs-comment">/* ... */</span>

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

    <span class="hljs-comment">/* ... */</span>
}
</code></pre>
<p>To authenticate a user, you would create a new user object with the username and password, and then attempt to authenticate it:</p>
<pre><code class="language-php hljs php" data-lang="php">$auth = Zend_Auth::getInstance();
$user = $gateway-&gt;createUser(<span class="hljs-keyword">array</span>(
    <span class="hljs-string">'username'</span> =&gt; $username,
    <span class="hljs-string">'password'</span> =&gt; $password,
));
<span class="hljs-keyword">if</span> ($auth-&gt;authenticate($user)) {
    <span class="hljs-comment">// AUTHENTICATED!</span>
}
</code></pre>
<p>This also has the effect of populating the user from the persistence store, as well as storing the identity in the session.</p>
<p>I covered ACL roles <a href="/blog/201-Applying-ACLs-to-Models.html">previously</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.</p>
<h3>Record Sets</h3>
<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.</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Spindle_Model_Users</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Iterator</span>,<span class="hljs-title">Countable</span>
</span>{
    <span class="hljs-keyword">protected</span> $_count;
    <span class="hljs-keyword">protected</span> $_gateway;
    <span class="hljs-keyword">protected</span> $_resultSet;

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

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

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

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

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

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

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

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

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">valid</span><span class="hljs-params">()</span>
    </span>{
        <span class="hljs-keyword">return</span> (bool) <span class="hljs-keyword">$this</span>-&gt;current();
    }
}
</code></pre>
<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.</p>
<h2>Using Value Objects and Record Sets in Your Gateway</h2>
<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 <code>fetch()</code> and <code>fetchAll()</code> methods:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Spindle_Model_UserGateway</span>
</span>{
    <span class="hljs-comment">/* ... */</span>

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

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

    <span class="hljs-comment">/* ... */</span>
}
</code></pre>
<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 <code>Spindle_Model_User</code> and <code>Spindle_Model_Users</code>.</p>
<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 <em>banned</em> users, and that this will be a common task. Define a method for it:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Spindle_Model_UserGateway</span>
</span>{
    <span class="hljs-comment">/* ... */</span>

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

    <span class="hljs-comment">/* ... */</span>
}
</code></pre>
<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.</p>
<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:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Spindle_Model_UserGateway</span>
</span>{
    <span class="hljs-comment">/* ... */</span>

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

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

    <span class="hljs-comment">/* ... */</span>
}
</code></pre>
<p>From a consumer standpoint, <em>nothing has changed</em>; they are still calling the same methods, and receiving the same responses. This is absolutely key in creating maintainable, future proof code.</p>
<h2>Summary</h2>
<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 <em>does stuff</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.</p>
<p><strong>Updates:</strong></p>
<ul>
<li><em>2009-01-04:</em> Updated <code>__unset()</code> per Gabriel's feedback (comment <a href="/search?q=tag:14">#14</a>)</li>
<li><em>2009-01-05:</em> Updated <code>current()</code> implementation per Falk's feedback (comment <a href="/search?q=tag:15">#15</a>)</li>
<li><em>2009-01-05:</em> Updated <code>current()</code> implementation per Martin's feedback (comment <a href="/search?q=tag:15">#15</a>.1.1)</li>
</ul>


<div class="h-entry">
    <img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&u=79dd2ea1d4d8855944715d09ee4c86215027fa80&s=140" alt="matthew">
    <a class="u-url u-uid p-name" href="https://mwop.net/blog/202-Model-Infrastructure.html">Model Infrastructure</a> was originally
    published <time class="dt-published" datetime="2008-12-30T07:35:01-06:00">30 December 2008</time>
    on <a href="https://mwop.net">https://mwop.net</a> by
    <a rel="author" class="p-author" href="https://mwop.net">Matthew Weier O&#039;Phinney</a>.
</div>
]]></content:encoded>
      <slash:comments>0</slash:comments>
    </item>
    <item>
      <title>Setting up your Zend_Test test suites</title>
      <pubDate>Sat, 13 Sep 2008 09:37:40 -0500</pubDate>
      <link>https://mwop.net/blog/190-Setting-up-your-Zend_Test-test-suites.html</link>
      <guid>https://mwop.net/blog/190-Setting-up-your-Zend_Test-test-suites.html</guid>
      <author>contact@mwop.net (Matthew Weier O'Phinney)</author>
      <dc:creator>Matthew Weier O'Phinney</dc:creator>
      <content:encoded><![CDATA[<p>Now that <a href="http://framework.zend.com/manual/en/zend.test.html">Zend_Test</a> has
shipped, developers are of course asking, &quot;How do I setup my test suite?&quot;
Fortunately, after some discussion with my colleagues and a little
experimenting on my one, I can answer that now.</p>


<p><a href="http://phpunit.de">PHPUnit</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.</p>
<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
<a href="http://www.phpunit.de/pocket_guide/3.2/en/appendixes.configuration.html">XML configuration file</a>.</p>
<p>As an example, consider the following:</p>
<pre><code class="language-xml hljs xml" data-lang="xml"><span class="hljs-tag">&lt;<span class="hljs-name">phpunit</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">testsuite</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"My Test Suite"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">directory</span>&gt;</span>./<span class="hljs-tag">&lt;/<span class="hljs-name">directory</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">testsuite</span>&gt;</span>

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

    <span class="hljs-tag">&lt;<span class="hljs-name">logging</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">log</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"coverage-html"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"./log/report"</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>
            <span class="hljs-attr">yui</span>=<span class="hljs-string">"true"</span> <span class="hljs-attr">highlight</span>=<span class="hljs-string">"true"</span>
            <span class="hljs-attr">lowUpperBound</span>=<span class="hljs-string">"50"</span> <span class="hljs-attr">highLowerBound</span>=<span class="hljs-string">"80"</span>/&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">log</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"testdox-html"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"./log/testdox.html"</span> /&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">logging</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">phpunit</span>&gt;</span>
</code></pre>
<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 <code>directory</code> directive to the <code>testsuite</code> directive scans for all
files ending in <code>Test.php</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 <code>logging</code> directive lets us
specify what kinds of logs to create and where.</p>
<p>Drop the above into <code>tests/phpunit.xml</code> in your application, and you can start
writing test cases and running the suite immediately, using the following
command:</p>
<pre><code class="language-bash hljs bash" data-lang="bash">$ phpunit --configuration phpunit.xml
</code></pre>
<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.</p>
<p>First, I create directories. For instance, I have the following hierarchy in my
test suite:</p>
<pre><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
</code></pre>
<p><code>controllers/</code> contains my controllers, <code>models/</code> contains my models. If I were
developing a modular application, I'd have something like <code>blog/controllers/</code>
instead. Library code is given the same hierarchy as is found in my <code>library/</code>
directory.</p>
<p>Second, I use docblock annotations to group my tests. I add the following to my
class-level docblock in my controller test cases:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-comment">/**
 * <span class="hljs-doctag">@group</span> Controllers
 */</span>
</code></pre>
<p>Models get the annotation <code>@group Models</code>, etc. This allows me to run
individual sets of tests on demand:</p>
<pre><code class="language-bash hljs bash" data-lang="bash">$ phpunit --configuration phpunit.xml --group=Controllers
</code></pre>
<p>You can specify multiple <code>@group</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.</p>
<p>Astute readers will have noticed the <code>TestHelper.php</code> file in that directory
listing earlier, and will be wondering what that's all about.</p>
<p>A test suite needs some environmental information, just like your application
does. It may need a default database adapter, altered <code>include_path</code>s,
autoloading set up, and more. Here's what my <code>TestHelper.php</code> looks like:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-meta">&lt;?php</span>
<span class="hljs-comment">/*
 * Start output buffering
 */</span>
ob_start();

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

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

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

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

<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.
 */</span>
$path = <span class="hljs-keyword">array</span>(
    $models,
    $library,
    $tests,
    get_include_path()
);
set_include_path(implode(PATH_SEPARATOR, $path));

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

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

<span class="hljs-comment">/*
 * Unset global variables that are no longer needed.
 */</span>
<span class="hljs-keyword">unset</span>($root, $library, $models, $controllers, $tests, $path);
</code></pre>
<p>The above ensures that my <code>APPLICATION_ENV</code> constant is set appropriately, that error reporting is appropriate for tests (i.e., I want to see <em>all</em> errors), and that autoloading is enabled. Additionally, I place a couple items in my registry — the bootstrap and test root directory.</p>
<p>In each test case file, I then do a <code>require_once</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.</p>
<p>Hopefully this will get you started with your application testing; what are you
waiting for?</p>


<div class="h-entry">
    <img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&u=79dd2ea1d4d8855944715d09ee4c86215027fa80&s=140" alt="matthew">
    <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</a> was originally
    published <time class="dt-published" datetime="2008-09-11T15:00:00-05:00">11 September 2008</time>
    on <a href="https://mwop.net">https://mwop.net</a> by
    <a rel="author" class="p-author" href="https://mwop.net">Matthew Weier O&#039;Phinney</a>.
</div>
]]></content:encoded>
      <slash:comments>0</slash:comments>
    </item>
    <item>
      <title>Testing Zend Framework MVC Applications</title>
      <pubDate>Thu, 03 Jul 2008 04:47:17 -0500</pubDate>
      <link>https://mwop.net/blog/182-Testing-Zend-Framework-MVC-Applications.html</link>
      <guid>https://mwop.net/blog/182-Testing-Zend-Framework-MVC-Applications.html</guid>
      <author>contact@mwop.net (Matthew Weier O'Phinney)</author>
      <dc:creator>Matthew Weier O'Phinney</dc:creator>
      <content:encoded><![CDATA[<p>Since I originally started hacking on the <a href="http://framework.zend.com/">Zend Framework</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.</p>
<p>So, one of my ongoing projects the past few months has been to create an
infrastructure for functional testing of ZF projects using
<a href="http://phpunit.de/">PHPUnit</a>. This past weekend, I made the final commits that
make this functionality feature complete.</p>
<p>The new functionality provides several facets:</p>
<ul>
<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).</li>
<li><code>Zend_Dom_Query</code>, a class for using CSS selectors (and XPath) to query (X)HTML and XML documents.</li>
<li>PHPUnit constraints that consume <code>Zend_Dom_Query</code> and the Response object to make their comparisons.</li>
<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.</li>
</ul>


<p>What might you want to test?</p>
<ul>
<li>HTTP response codes</li>
<li>Whether or not the action resulted in a redirect, and where it redirected to</li>
<li>Whether or not certain DOM artifacts are present (particularly helpful for ensuring that the DOM structure is correct for JS actions)</li>
<li>Presence of specific HTTP response headers and/or their content</li>
<li>What module, controller, and/or action was used in the last iteration of the dispatch loop</li>
<li>What route was selected</li>
</ul>
<p>The aim is to make testing your controllers trivial and fun. Let's look at an example:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UserControllerTest</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Zend_Test_PHPUnit_ControllerTestCase</span>
</span>{
    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">setUp</span><span class="hljs-params">()</span>
    </span>{
        <span class="hljs-keyword">$this</span>-&gt;bootstrap = <span class="hljs-keyword">array</span>(<span class="hljs-keyword">$this</span>, <span class="hljs-string">'appBootstrap'</span>);
        <span class="hljs-keyword">parent</span>::setUp();
    }

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

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

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

    <span class="hljs-keyword">public</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">testValidLoginShouldInitializeAuthSessionAndRedirectToProfilePage</span><span class="hljs-params">()</span>
    </span>{
        <span class="hljs-keyword">$this</span>-&gt;request
             -&gt;setMethod(<span class="hljs-string">'POST'</span>)
             -&gt;setPost(<span class="hljs-keyword">array</span>(
                 <span class="hljs-string">'username'</span> =&gt; <span class="hljs-string">'foobar'</span>,
                 <span class="hljs-string">'password'</span> =&gt; <span class="hljs-string">'foobar'</span>
             ));
        <span class="hljs-keyword">$this</span>-&gt;dispatch(<span class="hljs-string">'/user/login'</span>);
        <span class="hljs-keyword">$this</span>-&gt;assertTrue(Zend_Auth::getInstance()-&gt;hasIdentity());
        <span class="hljs-keyword">$this</span>-&gt;assertRedirectTo(<span class="hljs-string">'/user/view'</span>);
    }
}
</code></pre>
<p>You'll note that the <code>setUp()</code> method assigns a callback to the <code>$bootstrap</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 &quot;initialization&quot; plugin to the front controller that takes care of
bootstrapping my application (via the <code>routeStartup()</code> hook).</p>
<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.</p>
<p>This is, of course, just the tip of the iceberg; I've created a couple dozen
other assertions as well.</p>
<p>You can preview the functionality in the <a href="http://framework.zend.com/svn/framework/standard/incubator/">Zend Framework standard incubator</a>;
look for <code>Zend_Test_PHPUnit_ControllerTestCase</code> in there, as well as the <code>Zend_Test</code>
documentation in the documentation tree (in human-readable DocBook XML).</p>
<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;
<a href="http://framework.zend.com/wiki/display/ZFDEV/Contributing+to+Zend+Framework#ContributingtoZendFramework-Subscribetotheappropriatemailinglists">instructions are on the ZF wiki</a>.</p>


<div class="h-entry">
    <img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&u=79dd2ea1d4d8855944715d09ee4c86215027fa80&s=140" alt="matthew">
    <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</a> was originally
    published <time class="dt-published" datetime="2008-06-30T12:00:00-05:00">30 June 2008</time>
    on <a href="https://mwop.net">https://mwop.net</a> by
    <a rel="author" class="p-author" href="https://mwop.net">Matthew Weier O&#039;Phinney</a>.
</div>
]]></content:encoded>
      <slash:comments>0</slash:comments>
    </item>
    <item>
      <title>Zend Framework Dojo Integration</title>
      <pubDate>Sun, 25 May 2008 12:40:37 -0500</pubDate>
      <link>https://mwop.net/blog/176-Zend-Framework-Dojo-Integration.html</link>
      <guid>https://mwop.net/blog/176-Zend-Framework-Dojo-Integration.html</guid>
      <author>contact@mwop.net (Matthew Weier O'Phinney)</author>
      <dc:creator>Matthew Weier O'Phinney</dc:creator>
      <content:encoded><![CDATA[<p>I'm pleased to announce that <a href="http://framework.zend.com/">Zend Framework</a> will
be partnering with <a href="http://dojotoolkit.org/">Dojo Toolkit</a> to deliver
out-of-the-box Ajax and rich user interfaces for sites developed in Zend
Framework.</p>
<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.</p>
<p>The integration points we have defined for our initial release are as follows:</p>


<ul>
<li>
<p><strong>JSON-RPC Server:</strong> We are re-working the <code>Zend_Json_Server</code> that has been
in our incubator since, oh, what? 0.2.0? and never released to actually
follow a specification: <a href="http://groups.google.com/group/json-rpc">JSON-RPC</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 <a href="http://framework.zend.com/svn/framework/branch/user/matthew/zed_json_server">ZF svn</a>.</p>
<p>The original <code>Zend_Json_Server</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.</p>
<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.</p>
</li>
<li>
<p><strong>dojo() View Helper:</strong> Enabling Dojo for a page is not typically as trivial
as just loading the <code>dojo.js</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 <code>onLoad()</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 <code>dojo()</code> view helper will act as a
<a href="http://framework.zend.com/manual/en/zend.view.helpers.html#zend.view.helpers.initial.placeholder">placeholder</a>
implementation, and facilitate all of the above tasks, as well as take care
of rendering the necessary <code>style</code> and <code>script</code> elements in your page.</p>
</li>
<li>
<p><strong>Form Element implementations:</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 <code>Zend_Form</code>. (One thing to note: <code>Zend_Form</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.)</p>
</li>
<li>
<p><strong>dojo.data Compatibility:</strong> <code>dojo.data</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
<code>dojo.data</code> compatible payloads with which to respond to <code>XmlHttpRequest</code>s; you
will simply need to pass in the data, and provide metadata regarding it.</p>
</li>
</ul>
<p>So, some examples are in order. First off, <code>Zend_Json_Server</code> operates like all
of ZF's server components: if follows the <a href="http://php.net/soap_soapserver_construct">SoapServer API</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:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-comment">// /json-rpc.php</span>
<span class="hljs-comment">// Assumes you have a class 'Foo' with methods 'bar' and 'baz':</span>
$server = <span class="hljs-keyword">new</span> Zend_Json_Server();
$server-&gt;setClass(<span class="hljs-string">'Foo'</span>)
       -&gt;setTarget(<span class="hljs-string">'/json-rpc.php'</span>)
       -&gt;setEnvelope(<span class="hljs-string">'JSON-RPC-1.0'</span>)
       -&gt;setDojoCompatible(<span class="hljs-keyword">true</span>);

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

$server-&gt;handle();
</code></pre>
<p>On your view script side, you might then do the following:</p>
<pre><code class="language-php hljs php" data-lang="php">&lt;h2&gt;Dojo JSON-RPC Demo&lt;/h2&gt;
&lt;input name=\<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;
</span></code></pre>
<p>And, finally, in your layout script, you might have the following:</p>
<pre><code class="language-php hljs php" data-lang="php"><span class="hljs-meta">&lt;?</span>= <span class="hljs-keyword">$this</span>-&gt;doctype() <span class="hljs-meta">?&gt;</span>
&lt;html&gt;
    &lt;head&gt;
        &lt;title&gt;...&lt;/title&gt;
        <span class="hljs-meta">&lt;?</span>= <span class="hljs-keyword">$this</span>-&gt;dojo() <span class="hljs-meta">?&gt;</span>
        <span class="hljs-meta">&lt;?</span>= <span class="hljs-keyword">$this</span>-&gt;headScript() <span class="hljs-meta">?&gt;</span>
    &lt;/head&gt;
    &lt;body <span class="hljs-class"><span class="hljs-keyword">class</span>=\"<span class="hljs-title">tundra</span>\"&gt;
        &lt;?= $<span class="hljs-title">this</span>-&gt;<span class="hljs-title">layout</span>()-&gt;<span class="hljs-title">content</span> ?&gt;
    &lt;/<span class="hljs-title">body</span>&gt;
&lt;/<span class="hljs-title">html</span>&gt;
</span></code></pre>
<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:</p>
<ul>
<li><strong>dojo() View Helper:</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
(<code>dojo.rpc.JsonService</code>); and rendering the necessary Dojo stylsheet and
script includes in the layout.</li>
<li><strong>JSON-RPC client:</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 <code>myObject.bar()</code>. Dojo's current implementation performs all
other requests as POST requests, passing the data via the raw POST body.</li>
</ul>
<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.</p>
<p><strong>Update:</strong> <a href="http://andigutmans.blogspot.com/2008/05/dojo-and-zend-framework-partnership.html">Andi has posted an FAQ on our integration</a>.</p>


<div class="h-entry">
    <img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&u=79dd2ea1d4d8855944715d09ee4c86215027fa80&s=140" alt="matthew">
    <a class="u-url u-uid p-name" href="https://mwop.net/blog/176-Zend-Framework-Dojo-Integration.html">Zend Framework Dojo Integration</a> was originally
    published <time class="dt-published" datetime="2008-05-21T10:57:00-05:00">21 May 2008</time>
    on <a href="https://mwop.net">https://mwop.net</a> by
    <a rel="author" class="p-author" href="https://mwop.net">Matthew Weier O&#039;Phinney</a>.
</div>
]]></content:encoded>
      <slash:comments>0</slash:comments>
    </item>
    <item>
      <title>2007 Retrospective</title>
      <pubDate>Thu, 03 Jan 2008 06:22:35 -0600</pubDate>
      <link>https://mwop.net/blog/154-2007-Retrospective.html</link>
      <guid>https://mwop.net/blog/154-2007-Retrospective.html</guid>
      <author>contact@mwop.net (Matthew Weier O'Phinney)</author>
      <dc:creator>Matthew Weier O'Phinney</dc:creator>
      <content:encoded><![CDATA[<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.</p>
<p>Here's the short version:</p>
<ul>
<li>One trip to Belgium and The Netherlands.</li>
<li>Two trips to Israel.</li>
<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).</li>
<li>Three different user groups attended, with three presentations.</li>
<li>One major Zend Framework release</li>
<li>One PEAR release.</li>
<li>One podcast.</li>
<li>One webinar.</li>
<li>One book published.</li>
<li>One conference attended.</li>
</ul>
<p>What follows is my month-by-month breakdown:</p>


<h3>January</h3>
<p>I finished up the last of my three chapters for
<a href="http://sitepoint.com/books/phpant2/">The PHP Anthology, 2nd Edition</a>,
and promptly used my advance to buy the family a Wii.</p>
<p>I was also introduced to <a href="http://jansch.nl/">Ivo Jansch</a> through work, and had
him wow me with one of the <a href="http://ibuildings.nl">ibuildings</a> products.</p>
<h3>February</h3>
<p>Mid-month, my boss at <a href="http://www.zend.com/">Zend</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.</p>
<p>Towards the end of the month, I was invited to <a href="http://bostonphp.org">BostonPHP</a>
to present building a simple <a href="http://framework.zend.com/">Zend Framework</a> CRUD
application, along with <a href="http://hagunbu.ch/">Chuck Hagenbuch</a> of the
<a href="http://horde.org/">HORDE project</a>. While there,
<a href="http://cake.insertdesignhere.com/">Nate Abele</a> heckled me, and then joined
Chuck and myself for an impromptu framework panel; a good time was had by all.</p>
<h3>March</h3>
<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.</p>
<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.</p>
<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.</p>
<p>I closed out a ton of MVC issues in the Zend Framework, and we released the
first beta version late in the month.</p>
<h3>April</h3>
<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.</p>
<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!</p>
<h3>May</h3>
<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.</p>
<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.</p>
<h3>June</h3>
<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.</p>
<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.</p>
<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.</p>
<p>Somehow, I also found time to record my first (and so far only)
<a href="http://devzone.zend.com/article/2140-PHP-Abstract-Podcast-Episode-2---Backup-or-Die">PHP Abstract podcast</a>.</p>
<h3>July</h3>
<p>We released <a href="http://framework.zend.com/">Zend Framework</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.</p>
<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.</p>
<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!</p>
<p>I also finally released the first stable version of
<a href="http://pear.php.net/packages/File_Fortune">File_Fortune</a> on
<a href="http://pear.php.net/">PEAR</a>, over a year since I'd first proposed it. The
package interfaces with <code>mod_fortune</code> files, allowing both the ability to read
and write such files, with full binary compatability.</p>
<h3>August</h3>
<p>Not much to report in August, except work, work, and more work.</p>
<h3>September</h3>
<p>My ebiz supervisor, Boaz, flew me to Tel Aviv for a second time, this time to
perform a &quot;brain dump&quot; 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.</p>
<p>A few days after I returned, I flew down to New York City for a special meeting
of <a href="http://nyphp.org/">NYPHP</a>, where <a href="http://blogs.zend.com/author/mark/">Mark de Visser</a>
presented on various Zend products and initiatives, and I gave a Zend Framework overview.</p>
<p>A week after the NYPHP presentation, I did a <a href="http://www.zend.com/webinars">zend.com webinar</a>
on the Zend Framework MVC layer.</p>
<h3>October</h3>
<p>October was the month of <a href="http://www.zendcon.con/">ZendCon</a>. I presented a
full-day tutorial on best practices and unit testing with
<a href="http://sebastian-bergmann.de/">Sebastian Bergmann</a> and
<a href="http://naberezny.com/">Mike Naberezny</a>; despite the length and subject matter,
we were SRO for most of the day.</p>
<p>I also did a main-stage presentation on Zend Framework's MVC components,
directly following <a href="http://terrychay.com/blog/">Terry Chay</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!)</p>
<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 <a href="http://paul-m-jones.com">Paul M. Jones</a> of
<a href="http://solarphp.com">the Solar framework</a>. Many good conversations were had.</p>
<p>Late in the month, <a href="http://sitepoint.com/books/phpant2/">The PHP Anthology, 2nd Edition</a>,
my first published book as an author, was finally released!</p>
<h3>November</h3>
<p>I spent much of the month working on
<a href="http://framework.zend.com/wiki/display/ZFPROP/Zend_Layout">Zend_Layout</a>, a
much requested component that simplifies and automates Two Step Views in Zend
Framework. I also started work implementing <a href="http://framework.zend.com/wiki/pages/viewpage.action?pageId=33071">Zend_View Enhanced</a>,
a set of view helpers for making complex views with <code>Zend_View</code> possible.</p>
<p>I also started playing with <a href="http://twitter.com/">Twitter</a> a bit, and came up
with a <a href="http://framework.zend.com/wiki/display/ZFPROP/Zend_Service_Twitter">Zend_Service_Twitter</a>
proposal for interacting with the Twitter API via PHP.</p>
<p>And finally, the Sunday before Thanksgiving, we finally launched the new
<a href="http://www.zend.com/">Zend.com</a> site, which was well-received in the
blogosphere.</p>
<h3>December</h3>
<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 <a href="http://groups.google.com/group/Burlington-VT-PHP">a Google Group</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.</p>
<p>I finished up <code>Zend_Layout</code> and <code>Zend_View</code> Enhanced with the help of Ralph
Schindler, and got a new proposal up for
<a href="http://framework.zend.com/wiki/display/ZFPROP/Zend_Form">Zend_Form</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.</p>
<h2>Summary</h2>
<p>This year was <em>incredibly</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.</p>
<p>My goals for the coming year? I'm too tired to even think about it ;-).</p>


<div class="h-entry">
    <img class="u-photo photo" width="50" src="https://avatars0.githubusercontent.com/u/25943?v=3&u=79dd2ea1d4d8855944715d09ee4c86215027fa80&s=140" alt="matthew">
    <a class="u-url u-uid p-name" href="https://mwop.net/blog/154-2007-Retrospective.html">2007 Retrospective</a> was originally
    published <time class="dt-published" datetime="2008-01-01T16:49:26-06:00">1 January 2008</time>
    on <a href="https://mwop.net">https://mwop.net</a> by
    <a rel="author" class="p-author" href="https://mwop.net">Matthew Weier O&#039;Phinney</a>.
</div>
]]></content:encoded>
      <slash:comments>0</slash:comments>
    </item>
  </channel>
</rss>
