Documente Academic
Documente Profesional
Documente Cultură
Integrate the Doctrine ORM tool with a Zend Framework application to simplify data access and manipulation
Vikram Vaswani Founder Melonfire 25 June 2013 (First published 28 May 2013)
Doctrine is an object-relational mapping (ORM) tool for PHP application development. With a modicum of configuration, you can combine it with the Zend Framework so that you can use Doctrine entities to simplify interaction with application data. This article shows you the process for integrating Doctrine 2.3 with a Zend Framework 1.x or 2.x application. The Zend Framework is one of the most popular frameworks for PHP application development. It's easy to get started with, comes with an extensive library of components, and is well-supported by documentation, code samples, and an active community. Zend Framework is also a complete implementation of the Model-View-Controller (MVC) pattern, allowing for more reusable code and a better separation of concerns. Another Zend Framework benefit is that you can easily integrate third-party libraries into a Zend Framework application. Many developers like to pair the Zend Framework with a third-party ORM tool to make it easier to query and manipulate database content by using objects. And one of the most commonly used ORMs is Doctrine, a well-known open source PHP library for database abstraction and manipulation. In this article, I: Explain how to integrate the Doctrine ORM with a Zend Framework application Cover the basics of creating Doctrine entities Explain how to configure the Zend Framework to load Doctrine classes Illustrate the process of using Doctrine entities within Zend Framework action controllers
developerWorks
ibm.com/developerWorks/
Are conversant with the basic principles of application development with the Zend Framework Understand the interaction between actions and controllers Are familiar with the namespace implementation in PHP 5.3 Have a working Apache/PHP/MySQL development environment Have installed the Zend Framework to your PHP include_path Are familiar with the basics of SQL Have configured your Apache HTTP Server to support virtual hosting and URL rewriting through .htaccess files
If you are unfamiliar with these topics, see Resources for more information. The techniques that are outlined in this article are based on information and ideas from online documentation and from various blog posts by clever PHP developers (see Resources for credits). The techniques were tested with the software versions mentioned later in this section, but they might not work in future versions because of ongoing development and changes. Currently two versions of the Zend Framework are available. The method to integrate Doctrine differs significantly depending on which Zend version that you use. The article reviews the process of integrating Doctrine with each version separately. The versions that are used in this article are: Zend Framework 1.11.11 Zend Framework 2.0.3 Doctrine 2.3.0 The application database is common to both scenarios, so set up the database right at the start.
Page 2 of 21
ibm.com/developerWorks/
developerWorks
INSERT INTO publisher (id, name) VALUES(3, 'Developer.com'); INSERT INTO publisher (id, name) VALUES(4, 'PHP Architect Magazine'); INSERT INTO article (id, title, url, date, publisher) VALUES(1, 'Search and integrate Google+ activity streams with PHP applications', 'http://www.ibm.com/developerworks/xml/library/x-googleplusphp/index.html', '2012-07-10', 2); INSERT INTO article (id, title, url, date, publisher) VALUES(2, 'Getting Started with Zend Server CE', 'http://devzone.zend.com/1389/getting-started-with-zend-server-ce/', '2009-03-02', 1); INSERT INTO article (id, title, url, date, publisher) VALUES(3, 'Integrating Advanced Spring Framework Features with Magnolia CMS', 'http://www.developer.com/java/web/integrating-advanced-spring-frameworkfeatures-with-magnolia-cms.html', '2010-12-13', 3);
Later on, you'll create Doctrine objects that represent these database tables and then use them within Zend Framework controllers to manipulate records in the database.
You can now define a new virtual host for this application (such as http://example.localhost/) in your Apache configuration and point the virtual host's document root to the application's public/ directory. If you then browse to this host, you should see the default Zend Framework 1.x welcome page, which is shown in Figure 1:
Page 3 of 21
developerWorks
ibm.com/developerWorks/
By default, the application namespace is automatically set to Application, and application-specific classes (such as the Doctrine entities that you'll create soon) are stored in $PROJECT/library/ Application/. Create this directory manually. The next step is to add the Doctrine ORM libraries. Download and manually install the libraries from the project website (for more information, see Resources). Copy the contents of the Doctrine/ directory in the project archive to the $PROJECT/library/Doctrine/ directory. Now your Doctrine and Zend Framework libraries are in the same place. The next step is to bind them together. The glue for that is provided by Guilherme Blanco, whose Bisna project provides a ready-made integration for Zend Framework 1.x with Doctrine 2.x. Download the Bisna project archive (for more information, see Resources). Copy the contents of the library/ directory in the project archive to your $PROJECT/library/ directory, and copy the contents of the bin/ directory to your $PROJECT/bin/ directory. At the end of this process, your $PROJECT/library/ folder should look similar to the one shown in Figure 2:
Page 4 of 21
ibm.com/developerWorks/
developerWorks
Page 5 of 21
developerWorks
resources.doctrine.dbal.defaultConnection = default ; "default" connection resources.doctrine.dbal.connections.default.id = default resources.doctrine.dbal.connections.default.eventManagerClass = "Doctrine\Common\EventManager" resources.doctrine.dbal.connections.default.parameters.driver = "pdo_mysql" resources.doctrine.dbal.connections.default.parameters.dbname = "appdata" resources.doctrine.dbal.connections.default.parameters.host = "localhost" resources.doctrine.dbal.connections.default.parameters.port = 3306 resources.doctrine.dbal.connections.default.parameters.user = "root" resources.doctrine.dbal.connections.default.parameters.password = "guessme"
ibm.com/developerWorks/
; Doctrine ORM resources.doctrine.orm.defaultEntityManager = default ; "default" manager resources.doctrine.orm.entityManagers.default.id = default resources.doctrine.orm.entityManagers.default.entityManagerClass = "Doctrine\ORM\EntityManager" resources.doctrine.orm.entityManagers.default.configurationClass = "Doctrine\ORM\Configuration" resources.doctrine.orm.entityManagers.default.defaultRepositoryClass = "Doctrine\ORM\EntityRepository" resources.doctrine.orm.entityManagers.default.entityNamespaces.app = "Application\Entity" resources.doctrine.orm.entityManagers.default.connection = default resources.doctrine.orm.entityManagers.default.proxy.autoGenerateClasses = true resources.doctrine.orm.entityManagers.default.proxy.namespace = "Application\Entity\Proxy" resources.doctrine.orm.entityManagers.default.proxy.dir = APPLICATION_PATH "/../library/Application/Entity/Proxy" resources.doctrine.orm.entityManagers.default.metadataDrivers. annotationRegistry.annotationFiles[] = APPLICATION_PATH "/../library/Doctrine/ORM/Mapping/Driver/DoctrineAnnotations.php" resources.doctrine.orm.entityManagers.default.metadataDrivers.drivers.0.adapterClass = "Doctrine\ORM\Mapping\Driver\AnnotationDriver" resources.doctrine.orm.entityManagers.default.metadataDrivers.drivers.0. mappingNamespace = "Application\Entity" resources.doctrine.orm.entityManagers.default.metadataDrivers.drivers.0. mappingDirs[] = APPLICATION_PATH "/../library/Application/Entity" resources.doctrine.orm.entityManagers.default.metadataDrivers.drivers.0. annotationReaderClass = "Doctrine\Common\Annotations\AnnotationReader" resources.doctrine.orm.entityManagers.default.metadataDrivers.drivers.0. annotationReaderCache = default
Update the database credentials that are listed for the default connection to reflect those of your database server host. To enable the use of a command-line script to generate Doctrine entities, you also must update the application bootstrapper, which is located at $PROJECT/application/Bootstrap.php. Listing 5 shows the code for the Symfony namespace:
Page 6 of 21
ibm.com/developerWorks/
developerWorks
The first command in Listing 6 produces annotated Article and Publisher entities that are based on the existing database schema. The second updates these entities to add setter and getter methods. The generated entities are stored in the $PROJECT/library/Application/Entity/ directory, as shown in Figure 3:
developerWorks
ibm.com/developerWorks/
and so Article has a many-to-one relationship with Publisher. Add this information manually to the $PROJECT/library/Application/Entity/Article.php class by updating it with the annotation shown in Listing 7:
You now are ready to use these entities in your application code.
Page 8 of 21
ibm.com/developerWorks/
developerWorks
$this->view->articles = $articles; } }
The ArticleController::indexAction begins by calling the getDoctrineContainer() method to retrieve the DoctrineContainer object. This object's getEntityManager() method retrieves the entity manager, which then retrieves the Article entity through the repository and calls its findAll() method to retrieve all available article records. The resulting collection is then transferred to the view, which iterates over the collection and returns the individual fields of the record by using the entity's getters. Save the view script in Listing 9 to $PROJECT/application/ views/scripts/article/index.phtml:
Figure 4 shows an example of the output that you see when you browse to the controller URL, http://example.localhost/article/index. (The output is a numbered list of three articles and shows the title, publish date, and website of each article.)
Page 9 of 21
developerWorks
ibm.com/developerWorks/
In a similar vein, you can add a record to the database by using the input that is supplied through a form like the one shown in Figure 5:
Page 10 of 21
ibm.com/developerWorks/
developerWorks
Page 11 of 21
developerWorks
$date = new \DateTime($form->getValue('date')); $article->setDate($date); $em->persist($article); $em->flush(); $this->_helper->getHelper('FlashMessenger') ->addMessage('Your submission has been accepted as item #' . $article->getId() . '.'); } } } }
ibm.com/developerWorks/
The code in Listing 10 reads and validates the input that is provided through the web form. Then, the code initializes a new Article entity and uses its setter methods to set its various properties from the form input. The resulting object is then written to the database using the entity manager's persist() and flush() methods. The entity manager also retrieves a Publisher entity from the database (by using the publisher identifier that is provided in the form) and links it to the Article entity through the setPublisher() method. As these listings illustrate, with a little help from the Bisna integration, it's not difficult to use Doctrine 2.3 entities inside a Zend Framework 1.x application. But what if you like to be on the leading edge, and want to integrate Doctrine with a Zend Framework 2.x application? Keep reading.
Page 12 of 21
ibm.com/developerWorks/
developerWorks
The recommended way to include dependencies (such as Doctrine) in a Zend Framework 2.x application is with Composer. Edit the $PROJECT/composer.json file and add the Doctrine ORM module for Zend Framework 2.x to the project through the entry that is shown in Listing 11:
Composer now starts to download the Zend Framework and Doctrine ORM module libraries and to set up its auto-loader correctly. When that process is complete, your $PROJECT/vendor/ directory should look similar to the one in Figure 7:
Page 13 of 21
developerWorks
ibm.com/developerWorks/
By default, the skeleton application includes an Application module and namespace. To add the Doctrine driver to this module's configuration file, edit the $PROJECT/module/Application/config/ module.config.php file and add a doctrine key to the configuration array, as shown in Listing 13:
Page 14 of 21
ibm.com/developerWorks/
developerWorks
Finally, add database credentials to the $PROJECT/config/autoload/local.php configuration file, as shown in Listing 14:
Be sure to update the database credentials in Listing 14 to reflect those of your database server host.
developerWorks
ibm.com/developerWorks/
The first command in Listing 15 produces annotated Article and Publisher entities that are based on the existing database schema. The second command updates these entities to add setter and getter methods. The generated entities are stored in the $PROJECT/module/Application/src/ Application/Entity/ directory, as shown in Figure 8:
Page 16 of 21
ibm.com/developerWorks/
developerWorks
* @ORM\ManyToOne(targetEntity="Publisher", fetch="EAGER") * @ORM\JoinColumn(name="publisher", referencedColumnName="id") */ private $publisher; /* other annotations and methods */ }
Now you can begin to use these entities in the application code.
Listing 17 contains an ArticleController whose indexAction() is responsible for generating a list of articles and publishers. First, the getServiceLocator() method obtains an instance of the Doctrine entity manager, and then the entity manager's findAll() method retrieves a collection of Articles. This collection is then passed on to the view script at $PROJECT/module/Application/ src/view/application/article/index.phtml, which extracts the necessary information from it using getter methods. Listing 18 shows the view script:
You also must add the new controller to the invokables array within $PROJECT/module/ Application/config/module.config.php, as shown in Listing 19:
Use Doctrine with the Zend Framework Page 17 of 21
developerWorks
ibm.com/developerWorks/
Now, browse to the URL http://example.localhost/application/article/index, and you should see output like that shown in Figure 9. (The output is a numbered list of three articles and shows the title, publish date, and website of each article.)
In a similar vein, you can write a createAction() that reads article data from a web form and persists it to the database as a new record. Listing 20 offers an example implementation:
Page 18 of 21
ibm.com/developerWorks/
developerWorks
{ public function createAction() { $form = new \Application\Form\ArticleCreate(); $request = $this->getRequest(); if ($request->isPost()) { $a = new \Application\InputFilter\Article(); $form->setInputFilter($a->getInputFilter()); $form->setData($request->getPost()); if ($form->isValid()) { $data = $form->getData(); $sl = $this->getServiceLocator(); $em = $sl->get('doctrine.entitymanager.orm_default'); $article = new \Application\Entity\Article; $article->setTitle($data['title']); $article->setUrl($data['url']); $publisher = $em->getRepository('\Application\Entity\Publisher') ->find($data['publisher']); $article->setPublisher($publisher); $date = new \DateTime($data['date']); $article->setDate($date); $em->persist($article); $em->flush(); $this->flashMessenger()->addMessage('Your submission has been accepted as item #' . $article->getId() . '.'); } } return new ViewModel(array('form' => $form)); } }
Listing 20 reads and validates the input that is provided through the web form. The code then uses the service locator to retrieve the Doctrine entity manager and initialize a new Article entity. The entity's setter methods set various properties from the form input. The resulting object is then written to the database by using the entity manager's persist() and flush() methods. Also, you can easily write methods to update, delete, and search articles, using techniques similar to the methods shown in the previous listings.
Conclusion
As this article demonstrated, you can add the power of Doctrine to a Zend Framework 1.x or 2.x application. Getting the two pieces to talk to each other is a somewhat complex task. But the effort is well worth it, because you can use all of Doctrine's ORM capabilities to perform advanced data manipulation from within a Zend Framework application. What are you waiting for? Get Doctrine and your favourite Zend Framework flavour, and put them to work.
Page 19 of 21
developerWorks
ibm.com/developerWorks/
Resources
Learn Zend Framework: Learn more about Zend at the official website: Read the community blogs. Take advantage of the Zend Framework 1.x Quickstart and Getting Started with Zend Framework 2. Access the full documentation. Learn how to contribute to the Zend Framework codebase. Doctrine: Visit the Doctrine project website. Bisna: Visit the Bisna site. Unit Testing Doctrine 2 Entities: View this video to see some examples of Bisna integration with the Zend Framework in action. Visit the developerWorks Open source technical topic for extensive how-to information, tools, and project updates to help you develop with open source technologies and use them with IBM's products. More articles by this author (Vikram Vaswani, developerWorks, August 2007-current): Read articles about XML, Google APIs, and other technologies. Get products and technologies Zend Framework: Download the Zend Framework. Doctrine: Download the Doctrine ORM tool. Bisna: Download the Bisna integration for Zend Framework 1.x. Doctrine ORM module for Zend Framework 2.x: Download the module. Composer: Download the Composer dependency manager. Innovate your next open source development project with IBM trial software, available for download or on DVD.
Discuss Get involved in the developerWorks community. Connect with other developerWorks users while you explore the developer-driven blogs, forums, groups, and wikis.
Page 20 of 21
ibm.com/developerWorks/
developerWorks
Page 21 of 21