Zend Framework and Doctrine. Part 2

Posted November 18th, 2009 by Juozas

doctrine-orm-php5Today we start actual development with Doctrine and Zend Framework. Base of this post is my code which I have been using for quite a few projects and it worked really well.

These are the steps required to setup Doctrine:

  1. Create MySQL (or any other adapter supported by Doctrine) database
  2. Download Doctrine 1.2 (as of today – 1.2.0beta3). Believe, it’s stable enough (no problems at all for me) and supports functions all functions we are going to use later
  3. Setup application.ini and application resource
  4. Generate models from database
  5. Profit!

To start with, download this archive (or get updated version from my public repository). It has everything you will need. Some of the files are quite long so I’m not going to post them here, it’s better that you download them and have them ready to be used as the article progresses.

Create MySQL database

For MySQL databases I use a product called “MySQL Workbench“. If you haven’t tried it I definitely recommend to give it a go – it basically allows you create a database as a diagram and then export it to sql file or update actual database (can be risky). Since I expect you to have enough knowledge how to create a database I won’t write anything more – you can find tutorials all over the web.

Download Doctrine

After downloading Doctrine extract it to library folder. You should have ./library/Doctrine.php in your library folder. Although, you can use svn:externals (if you are using SVN at all) to remove Doctrine code from repository, but in this case you probably need to setup paths in application resource file explain below to use Doctrine_Core if you just get Doctrine folder contents (from here).

Most important point here – don’t forget to download 1.2, not version 1.1. Even though it doesn’t have a stable release, 1.1 doesn’t support models generation as we want them to be (auto-loadable). Also this week all bugs have been fixed which I reported and now models generation works perfectly. From my personal experience, I haven’t even used 1.1 at all (started with 1.2 when it was in alpha) and didn’t had any problems, so my recommendation – use 1.2.

Setup application.ini and application resource

From the file listed above open a file called application.ini. Append you current configuration (more on getting Zend Framework project running can be found here) with settings in that file (leave compiled and cache options to false for now). Doctrine uses DNS strings to connect to a database, there are quite a few examples in the documentation. If you have used PDO before you be very familiar.

In archive there is a file called library/resource.php. Depending on what namespace you use for outside-Zend code, paste it to chosen folder. If you choose to have “App” as a namespace, just copy that file to library/App/Application/Resource/Doctrine.php. And don’t forget to have:

autoloaderNamespaces[] = "App_"
pluginpaths.App_Application_Resource = "App/Application/Resource"

in application.ini also, otherwise library code won’t be auto-loaded. I don’t recommend putting any code in Zend folder, because it will create tons of problems in the future. Having App or ProjectName library folders allows to have your own code in separate packages which you can use in later projects (I usually have App, ProjectName, CompanyName).

Provided application.ini file isn’t that much configurable, mainly because it wasn’t supposed to be released at all. If you are looking for something more dynamic, look no further than this proposal in Zend incubator. Nevertheless, my code should work fine – it has all options required to get you started and have been tweaked with settings which I found to be really useful.

Generate models from database

This part is the most awesome. To start, just copy and paste everything from scripts folder in the zip archive to your scripts folder in application (I have scripts folder in the same level as application and library, in the root of project). There actually only two files – doctrine-cli.php and custom task in Doctrine/Task/GenerateModels.php. You can run doctrine-cli.php like this:

php doctrine-cli.php

and you should get a list of all the possible tasks (if you setup everything properly). To run my custom task, append “generate-models” to the end of previous command to get:

php doctrine-cli.php generate-models

This task will load your database schema and create all classes (table, base model, model) required for models. Doctrine also supports generating models from yaml configuration files, though I’ve never used it before, but configuration should be almost the same.

It works

To test the models you can look at the generate code or start coding you application (or even run dql task from doctrine-cli.php). For example you can test simply like this (adjust by models you have):

$product = new Model_Product();
$product->title = 'Product name';
$product->save();
 
print_r(Doctrine_Core::getTable('Model_Product')->findAll()->toArray());

If everything has been configured properly you shouldn’t get any errors and this code (put it in controller) should output array of your saved data. Because of the settings in application resource, save() method also runs validation so if you missed a field and it breaks a not null constrain, this code will throw a validator exception.

What’s next?

This article has enough information to get you started and almost all the code provided it left as simple as possible and open for further modifications. I hope that in coming months code for Doctrine and Zend Framework integration will be completed and you can use Doctrine without any outside code.

Since we have application running now we can dive into actual usage, testing and more advanced stuff. I have code for these also and will be posting more articles very soon, even though the best resource for Doctrine is documentation. If you have any problems with this code just leave a comment here or find my on twitter (here) and I will try to explain more.

All parts:

  1. Zend Framework and Doctrine. Part 1
  2. Zend Framework and Doctrine. Part 2
  3. Zend Framework and Doctrine. Part 3

 

Trackbacks/Pingbacks

  1. Zend Framework and Doctrine. Part 1 | Juozas devBlog
  2. Juozas Kaziukenas’ Blog: Zend Framework and Doctrine. Part 2 | Webs Developer
  3. Juozas Kaziukenas’ Blog: Zend Framework and Doctrine. Part 2 | Development Blog With Code Updates : Developercast.com
  4. Zend Framework and Doctrine Part 1 - More Cowbell
  5. Zend Framework and Doctrine | Hot Dorkage
  6. Robo47.net
  7. Zend Framework and Doctrine. Part 3 | Juozas devBlog
  8. খবরাখবর: ডিসেম্বর ৪ « পিএইচপি, বাংলায়
  9. LimeSpace – IT » Der Wochenrückblick: Doctrine, HTTP Header und Wordpress Tricks

Comments (10)

  1. Phil Brown

    Don’t forget to setup the resource plugin path in the plugin loader, eg

    ; application.ini
    pluginPaths.App_Application_Resource = "App/Application/Resource"
  2. Juozas (author)

    Thanks, forgot to add that one.

  3. snapshot

    Please, write something about subqueries in Doctrine.

  4. Giorgio Sironi

    Particularly useful the part on models generation, which suppose you are dealing with a legacy schema. Once upon a time, me and a colleague generated classes with Doctrine 1 from a Sql server database and regenerated tables on Mysql, effectively porting the tables from one vendor to another.

  5. guybrush

    Hi, thank you for the articles.

    I am writing a new application with ZF, almost complete, and I was wondering how and if I could use Doctrine with my current db scheme. Are there any particular rules for table and fields names ? And what about tables relationships ?

    Clients table, for example, is called “client” and have an “id” field.
    Contacts are stored in a “client_contact” table where a “client_id” field indicates the relationship with “client”.

    Is that the way Doctrine expects the tables to be or does it search for 3 tables: a “client” table, a “contact” table and a relationship table (such as “client_contact”) with only two fields “client_id” and “contact_id” ?

    case 1: 2 tables: client (clients data), client_contact (contacts data)

    case 2: 3 tables: client (clients data), contact (contacts data), client_contact (contacts to clients relationship)

    Thanks!

  6. matthew

    I must be missing something the generated models don’t autoload.

  7. Juozas (author)

    guybrush, Doctrine works with many-to-one, one-to-many, many-to-many relationships just fine. You only need a separate relationships table if you are doing many-to-many relation.

    But you need to remember that only all tables must have a primary key and probably best if that key is one column (usually id).

    matthew, when you get an error about not found class, what include paths it shows? Also check that model classes have a correct class name (maybe I misspelled preparing this code).

  8. matthew

    I figured it out, I had to add

    protected function _initAutoload()
    {
    $moduleLoader = new Zend_Application_Module_Autoloader(array(
    'namespace' => '',
    'basePath' => APPLICATION_PATH));
    return $moduleLoader;
    }

  9. Matt Cockayne

    Juozas.. great post.. realy helped me in getting my model generation to work.

    There is an easier way though that you can generate Zend compliant models without having to build your own tasks in a lot of cases.

    I created my own resource to suite my needs better but essentailly it works in the same way yours does.

    In my application.ini file I add a new key and data which then gets passed to a lot of the tasks automatically and builds them nicely.


    ; doctrine resource
    resources.doctrine.connection_string = "mysql://root:password3@127.0.0.1/database"
    resources.doctrine.charset = "utf8"
    resources.doctrine.cache = "false"
    resources.doctrine.compiled = "false"
    resources.doctrine.models_path = APPLICATION_PATH "/models"
    resources.doctrine.data_fixtures_path = APPLICATION_PATH "/doctrine/data/fixtures"
    resources.doctrine.sql_path = APPLICATION_PATH "/doctrine/data/sql"
    resources.doctrine.migrations_path = APPLICATION_PATH "/doctrine/migrations"
    resources.doctrine.yaml_schema_path = APPLICATION_PATH "/doctrine/schema"
    resources.doctrine.phpDocPackage = Titan
    resources.doctrine.phpDocSubpackage = Application
    resources.doctrine.phpDocName = Zucchi.co.uk
    resources.doctrine.phpDocEmail = titan@zucchi.co.uk
    resources.doctrine.generate_models_options.pearStyle = true
    resources.doctrine.generate_models_options.generateTableClasses = false
    resources.doctrine.generate_models_options.generateBaseClasses = false
    resources.doctrine.generate_models_options.classPrefix = "Model_"
    resources.doctrine.generate_models_options.baseClassPrefix = "Base_"
    resources.doctrine.generate_models_options.baseClassesDirectory = "Base"
    resources.doctrine.generate_models_options.classPrefixFiles = false
    resources.doctrine.generate_models_options.generateAccessors = false

    Also I tend to build my modes from a yaml schema. in v 1.2 of doctrine there is a required dependancy on one of Symfonys yaml parsers.. I had to make an amend to the DOctrine files to get it work but I have logged this as a bug/task on Doctrines Issue tracker

    Something Else to note. The way I configured my application also meant that it wasnt autoloading the models correctly. So I cheated and created a new auto loader to handle it properly


    /**
    * Resource loader for application module classes
    *
    * @uses Zucchi_Doctrine_Application_Autoloader
    * @package Zucchi_Doctrine
    * @subpackage Application
    * @copyright Copyright (c) 2009 Zucchi.co.uk
    * @license http://framework.zend.com/license/new-bsd New BSD License
    */
    class Zucchi_Doctrine_Application_Autoloader extends Zend_Loader_Autoloader_Resource
    {
    /**
    * Constructor
    *
    * @param array|Zend_Config $options
    * @return void
    */
    public function __construct($options)
    {
    parent::__construct($options);
    $this->initDefaultResourceTypes();
    }

    /**
    * Initialize default resource types for module resource classes
    *
    * @return void
    */
    public function initDefaultResourceTypes()
    {
    $basePath = $this->getBasePath();
    $this->addResourceTypes(array(
    'dbtable' => array(
    'namespace' => 'Model_DbTable',
    'path' => 'models/DbTable',
    ),
    'model' => array(
    'namespace' => 'Model',
    'path' => 'models',
    ),
    'modelbase' => array(
    'namespace' => 'Model_Base',
    'path' => 'models/Base',
    )
    ));
    $this->setDefaultResourceType('model');
    }
    }

    I then load this by adding it in my resource and it all works 100%


    $this->getBootstrap()
    ->getApplication()
    ->getAutoloader()
    ->pushAutoloader(array('Doctrine', 'autoload'), 'Doctrine')
    ->pushAutoLoader(new Zucchi_Doctrine_Application_Autoloader(array(
    'namespace' => '',
    'basePath' => APPLICATION_PATH,
    )), 'ZucchiDoctrine');

    for a more complete look its all in my subversion repo @ subversion.zucchi.co.uk

  10. Fred

    I had a problem yesterday with the cli script to generate models. I had two related tables, Terms, and Terms_Categories. Terms.category_id has foreign key Terms_Categories.id. When I created the relationship in phpMyAdmin, it broke the cli script. I found that everything worked fine, models were generated and put in proper folder, but the relationship part of Base_Terms was failing. It was creating

    $this->hasOne(‘Model_TermsCategories as TermsCategories’..

    Should have been Model_Terms_Categories and manually changing that worked.

    Like I said it did create the right class in the right spot for Model_Terms_Categories

Leave a Reply

XHTML: You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">