Zend Framework from Scratch - Models and Integrating Doctrine ORM

Ready to take your PHP skills to the next level? In this new “From Scratch” series, we'll focus exclusively on Zend Framework, a full-stack PHP framework created by Zend Technologies. This second tutorial on our series is entitled “Models and Integrating Doctrine ORM”.


Review

Welcome back to our Zend Framework from Scratch series! In our last tutorial, we learned some basic things about Zend Framework, such as:

  • Where to download the latest Zend Framework files
  • Where and how to set it up locally
  • Creating your first Zend Framework project and setting up a VirtualHost on your web server
  • How exactly Zend Framework implements the MVC pattern and its default application routing
  • Passing data from a controller to its view
  • Creating a site-wide layout for your Zend Framework application
  • Creating new controllers and actions

If you haven't yet, you should give the previous tutorial a read. It will really make it easier to for you to understand some Zend Framework basics and help you understand some things we discuss in this tutorial.

In this second part of the series, we'll be talking about a crucial part of any web application — the MODELS. We'll also be taking a look at how to integrate the very popular Doctrine ORM with our Zend Framework project, and find out why it's much better to use than Zend Framework's native Zend_Db. So, without further ado, let's begin!


What exactly are “Models”?

When I started trying to grasp the concept of MVC, I read quite a number of analogies, which attempted to explain exactly what each of these components represent. One of the best analogies I've read so far was from this article, Another way to think about MVC. It goes something like this:

So, let's imagine a bank.

The safe is the Database – this is where all the most important goodies are stored, and are nicely protected from the outside world.

Then we have the bankers or in programmatic terms the Models. The bankers are the only ones who have access to the safe (the DB). They are generally fat, old and lazy, which follows quite nicely with one of the rules of MVC: *fat Models, skinny controllers*. We'll see why and how this analogy applies a little later.
Now we've got our average bank workers, the gophers, the runners, the Controllers. Controllers or gophers do all the running around, that's why they have to be fit and skinny. They take the loot or information from the bankers (the Models) and bring it to the bank customers the Views.

The bankers (Models) have been at the job for a while, therefore they make all the important decisions. Which brings us to another rule: *keep as much business logic in the Model as possible*. The controllers, our average workers, should not be making such decisions, they ask the banker for details, get the info, and pass it on to the customer (the View). Hence, we continue to follow the rule of *fat Models, skinny controllers*. The gophers do not make important decisions, but they cannot be plain dumb (thus a little business logic in the controller is OK). However, as soon as the gopher begins to think too much the banker gets upset and your bank (or you app) goes out of business. So again, always remember to offload as much business logic (or decision making) to the model.

Now, the bankers sure as hell aren't going to talk to the customers (the View) directly, they are way too important in their cushy chairs for that. Thus another rule is followed: *Models should not talk to Views*. This communication between the banker and the customer (the Model and the View) is always handled by the gopher (the Controller). (Yes, there are some exception to this rule for super VIP customers, but let's stick to basics for the time being).

It also happens that a single worker (Controller) has to get information from more than one banker, and that's perfectly acceptable. However, if the bankers are related (otherwise how else would they land such nice jobs?)… the bankers (Models) will communicate with each other first, and then pass cumulative information to their gopher, who will happily deliver it to the customer (View). So here's another rule: *Related Models provide information to the controller via their association (relation)*.

So what about our customer (the View)? Well, banks do make mistakes and the customer should be smart enough to balance their own account and make some decisions. In MVC terms we get another simple rule: *it's quite alright for the views to contain some logic, which deals with the view or presentation*. Following our analogy, the customer will make sure not forget to wear pants while they go to the bank, but they are not going to tell the bankers how to process the transactions.

In a nutshell:

  • Models are representatives of the Database, and should be where all the business logic of an application resides
  • Controllers communicate with Models and ask them to retrieve information they need
  • This information is then passed by a Controller to the View and is rendered
  • It's very rare that a Model directly interacts with a View, but sometimes it may happen when necessary
  • Models can talk with other Models and aren't self-contained. They have relationships that intertwine with each other
  • These relationships make it easier and quicker for a Controller to get information, since it doesn't have to interact with different Models - the Models can do that themselves

We can see how important Models are in any application, since it's repsonsible for any dynamic actions that happens in an application. Now that we have a pretty clear understanding of the responsibilities of the Model, as well as the View and Controller, let's dive into implementing the Models in our application.


Step 1 - Setting up your Zend Application to Connect to a Database

The first thing we'll need to do is to make our Zend application connect to a database. Luckily, the zf command can take care of that. Open your Command Prompt (or Terminal), cd into your thenextsocial folder, and type in the following:

If correct, you should get an output similar to:

Additionally, you should see two new lines inside your application.ini file:

We can clearly see that the SetEnv APPLICATION_ENV directive sets our application's environment. If and when we move the application to another environment, this should be the only thing we need to change. This ensures that everything our application relies on to work is defined in the application.ini, which makes sure that our application isn't relying on any external setting. This helps eliminate the “it works on my development machine, how come it doesn't work on the production server?” problem.


Step 2 - Create the Database and Some Tables

Before we create your our first Model for the application, we'll need a Database that the Model will represent first. Let's start with something simple — a User table, where we'll save all the registered users for TheNextSocial.

Login to your MySQL database and create a database called thenextsocial. Once created, execute the following query to create a User table, and an accompanying User Settings table:

These SQL queries should create two tables. A user table with the following columns:

  • id - a unique ID for each user
  • email - the email address of the user, also unique
  • password - the user's password, which we'll hash
  • salt - a random salt, which we'll use to hash the user's password
  • date_created - the date and time the user record was created

And a user_settings table, where we'll store any user-related settings, with the columns:

  • id - a unique ID for each setting
  • user_id - the user_id which is a foreign key to user.id
  • name - a string of text that represents the setting
  • value - the value of the setting

It's worth taking note that the User and User Settings table share a One-to-Many relationship, which means a single User record can be related to multiple User Settings records. This will make it easier to store any kind of information related to a user, for example, their name or profile photo.

Now that we have a few tables to play around with, let's learn how to create our first Model: the User Model.


Step 3 - Creating your First Model

The DAO Design Pattern

As with a lot of applications, the usual way to use make models in Zend Framework is to make use of a popular design pattern called the “DAO” pattern. In this pattern we have the following components:

  • Table Data Gateway (DataSource) which connects our application to the data source, the MySQL table
  • Data Mapper (DataAccessObject) which maps the data retrieved from the database to the
  • Data Object (Data) which represents a row from our database, after the DataMapper maps the data to it

Let's begin by creating a Table Data Gateway for the User table using the zf CLI tool:

The zf create db-table takes in two parameters:

  • ClassName - the name of the class
  • database_table - the name of the table

Open the User.php file found in the application/models/DbTable folder and it should look like this:

Now let's create a Data Mapper class. Again, using the zf CLI tool:

The UserMapper.php file will be empty right now but we'll put in some code later. For now, we need to create the Data Object, which is the User model:

Here we have three methods:

  • __construct() - constructor for the class. Once instantiated, it creates an instance of the Table Data Gateway and stores it
  • save(Application_Model_User $user_object) - takes in a Application_Model_User and saves the data from the object to the database
  • getUserById($id) - takes in an integer $id which represents a single row from the database table, retrieves it, then returns a Application_Model_User with the data mapped

Open up User.php and put the following code in:

  • __construct($user_row) - takes in an optional Zend_Db_Table_Row object, which represents one row from the database, and maps the data to itself
  • __set($name, $value) - a magic function that takes care of setting all of the attributes for the model.
  • __get($name) - a magic function that takes care of getting an attribute of the model.

Let's try it out! If you followed the previous tutorial, you should have an IndexController.php file. Open it and put in this code that creates a new user:

Now go to http://thenextsocial.local/. Once it loads, check the thenextsocial.user table on MySQL and if everything worked, you should have a new User record!

A new User record!

A new User record!

Now, let's try updating this record. Go back to IndexController.php and update the code to match the following:

Check the MySQL table again, and you should see that the email for the record has been updated!

Updated User record

Updated User record

Congratulations! You've successfully created your first ever Zend Framework Model!


The Doctrine ORM

Introduction

From the Doctrine ORM website, http://doctrine-project.org/projects/orm:

Object relational mapper (ORM) for PHP that sits on top of a powerful database abstraction layer (DBAL). One of its key features is the option to write database queries in a proprietary object oriented SQL dialect called Doctrine Query Language (DQL), inspired by Hibernates HQL. This provides developers with a powerful alternative to SQL that maintains flexibility without requiring unnecessary code duplication.

Basically, the Doctrine ORM library abstracts most, if not all of the Model implementation for an application. Some of the incredible advantages I discovered while using Doctrine with Zend Framework are:

  • Very easy to use, guaranteed to cut your development time in half
  • Works just as well with different kinds of DB's with very little tweaking needed. For example, Doctrine made it very easy for me to port an application I worked on before from using MySQL to MSSQL
  • A scaffolding tool, called Doctrine_Cli that creates models from the database very quickly

To get started, you should download the Doctrine ORM library first from their site. I'll be using the 1.2.4 version. Go to http://www.doctrine-project.org/projects/orm/1.2/download/1.2.4 and click on the Download 1.2.4 Package link.

Downloading the Doctrine ORM

Downloading the Doctrine ORM

Once downloaded, open it and extract the contents. Inside, you should see Doctrine-1.2.4 folder and a package.xml file. Go inside the folder and you should see a Doctrine folder, a Doctrine.php file, and a LICENSE file.

Doctrine download contents

Doctrine download contents

Copy the Doctrine folder and the Doctrine.php file and put it inside the include path of your PHP installation. If you remember how we set up Zend Framework from the last tutorial, it's most likely the same folder you placed the Zend library files in.

Doctrine library with Zend library in PHP's include_path

Doctrine library with Zend library in PHP's include_path

Now we're ready to integrate it with our Zend application! Begin by opening application.ini again and adding the following configuration inside the [development : production] block:

Now that we have our configuration set up, open the application's Bootstrap.php file. You'll find this inside the thenextsocial/application folder.

The Bootstrap.php

The Bootstrap.php lets us initialize any resources we might use in our application. Basically, all resources we need to instantiate should be placed here. We'll dive into this in more detail later in the series, but for now, all you need to know is that the format for methods here are like this:

Inside the Bootstra.php file, add the following code to initialize Doctrine with the application:

The setup I've done here is based on a script I found in the past on http://dev.juokaz.com, which was maintained by Juozas Kaziukenas, one of the team members at the Doctrine project. Sadly, the blog has already been shut down, so I won't be able to link to it anymore. Also, take note that we have another method called _initAutoload(). This basically initializes the Zend Autoloader, which will autoload all the generated models inside the models folder. This saves us the hassle of having to include these files one by one.

Next, we need to setup the Doctrine CLI script that we'll use to auto-generate Models from the database. Go back to the thenextsocial folder and create a folder called scripts. Inside, create a file named doctrine-cli.php and put the following inside:

Go back inside to your models folder and delete the User model files we have there (if you want, you can move it to another location first, but it shouldn't be inside the folder). Next, open up your command prompt (or terminal), cd to the scripts folder and type in the following command:

You should see something like this:

Expected Doctrine CLI output

Expected Doctrine CLI output

If everything worked out, let's start creating some models! Type in the following:

You should now see the following output:

Generating Models using Doctrine CLI

Generating Models using Doctrine CLI

If you did, check out your models folder again and you should see some brand new User and UserSettings models that have been generated by Doctrine!

Generated Models!

Generated Models!

If you open the files, you won't see much inside. Most of the code for the models are abstracted by the Doctrine library. By extending the Doctrine_Record class, we have available to us a lot of prebuilt methods from the library. Open IndexController.php again and replace the old test code with the following:

Once done, go back to http://thenextsocial.local. If the page loads, check your MySQL table and you should see that a brand new User record has been inserted!

User record via Doctrine ORM

User record via Doctrine ORM

Now, let's try some more complicated stuff — retrieving an existing User via prebuilt Doctrine methods and updating it. Update the code so it looks like this:

The findOneByEmailAndPassword() method is a convenience method prebuilt by Doctrine to make it easier to select one row from the database. The great thing about it is you can mix-and-match table columns in the method. For example, you can call something like findOneByIdAndNameAndPasswordAndSalt() and it will still work!

Updating a User record via Doctrine ORM

Updating a User record via Doctrine ORM

There's a whole lot more we can do now that we use Doctrine ORM for the application's Model implementation. Stuff like the Doctrine Query Language (DQL), taking advantage of Model relationships and generating Models from YAML. For the remainder of the series, we'll be using Doctrine ORM for the Model implementation of the application, so you'll actually be learning two things in the series instead of just one! Score!


Conclusion

By now, you should be able to know the following:

  • What are “Models”
  • Connecting your Zend application to a database
  • How the application.ini works
  • The DAO Design Pattern
  • Creating Models using the ZF CLI tool
  • Where to download the Doctrine ORM and how to install it
  • Integrating the Doctrine ORM with your Zend application
  • The Bootstrap.php
  • Generating Models using the Doctrine CLI tool
  • Basic usage of Models generated with Doctrine

Now that you know how to implement the Models in an Zend Framework powered application, you have the knowledge to create dynamic websites. Try to play around with the application, create some new Controllers and Models that read, update, save and delete from the database.

In our next tutorial, we'll learn about some often used components of the Zend Framework library, the Zend_Auth and Zend_Acl components and build TheNextSocial's authentication system!

Until then, stay tuned, and remember that all the code used here is available on TheNextSocial's GitHub repository!

Tags:

Comments

Related Articles