Welcome to Abdul Malik Ikhsan's Blog

Zend Framework 2 : Generate Doctrine Entities from Existing Database using DoctrineModule and DoctrineORMModule

Posted in orm, Teknologi, Tutorial PHP, Zend Framework 2 by samsonasik on April 10, 2013

zf2-zendframework2If we are working with Doctrine , we usually create entities first, and generate the database tables. How if the situation is the database tables and data is already ready, and we have to create an application based on doctrine and Zend Framework 2? We need generate entities! Don’t create them manually!
For example, i have two tables : album and track (i’m using PostgreSQL ) like the following :

-- Table: album
CREATE TABLE album
(
  id bigserial NOT NULL,
  artist character varying(255),
  title character varying(255),
  CONSTRAINT pk_album PRIMARY KEY (id )
)
WITH (
  OIDS=FALSE
);
ALTER TABLE album
  OWNER TO developer;

-- Table: track
CREATE TABLE track
(
  track_id bigserial NOT NULL,
  track_title character varying(255),
  album_id bigint,
  CONSTRAINT track_pkey PRIMARY KEY (track_id ),
  CONSTRAINT fk_track_album FOREIGN KEY (album_id)
      REFERENCES album (id) MATCH SIMPLE
      ON UPDATE CASCADE ON DELETE CASCADE
)
WITH (
  OIDS=FALSE
);
ALTER TABLE track
  OWNER TO developer;

Ok, let’s create an application based on ZF2 step by step :
1. install ZF2 SkeletonApplication

git clone git://github.com/zendframework/ZendSkeletonApplication.git zftutordoctrine

2. add “doctrine/doctrine-orm-module” to your zftutordoctrine/composer.json

{
    "name": "zendframework/skeleton-application",
    "description": "Skeleton Application for ZF2",
    "license": "BSD-3-Clause",
    "keywords": [
        "framework",
        "zf2"
    ],
    "homepage": "http://framework.zend.com/",
    "require": {
        "php": ">=5.3.3",
        "zendframework/zendframework": ">2.1.3",
        "doctrine/doctrine-orm-module": "0.*"
    }
}

3. Install it

php composer.phar self-update && php composer.phar install

4. Create an Album module with structure entity like the following :
5-albumomodule-structure
5. Configure doctrine connection
You can define it at one file, but separate it with two file(local and global) can make security happy 😀
a. config/autoload/doctrine.global.php

//config/autoload/doctrine.global.php
return array(
    'doctrine' => array(
        'connection' => array(
            'orm_default' => array(
                'driverClass' => 'Doctrine\DBAL\Driver\PDOMySql\Driver',
                    'params' => array(
                        'host' => 'localhost',
                        'port' => '5432',
                        'dbname' => 'zftutordoctrine',
                ),
            ),
        )
));

b. config/autoload/doctrine.local.php

//config/autoload/doctrine.local.php
return array(
    'doctrine' => array(
        'connection' => array(
            'orm_default' => array(
                'driverClass' => 'Doctrine\DBAL\Driver\PDOPgSql\Driver',
                    'params' => array(
                        'user' => 'developer',
                        'password' => '123456',
                ),
            ),
        )
));

6. register Album\Entity into doctrine driver in module/Album/config/module.config.php

//module/Album/config/module.config.php
return array(
    'doctrine' => array(
        'driver' => array(
            'Album_driver' => array(
                'class' => 'Doctrine\ORM\Mapping\Driver\AnnotationDriver',
                'cache' => 'array',
                'paths' => array(__DIR__ . '/../src/Album/Entity')
            ),
            'orm_default' => array(
                'drivers' => array(
                     'Album\Entity' =>  'Album_driver'
                ),
            ),
        ),
    ),                 
);

7. Register modules into config/application.config.php

//config/application.config.php
return array(
    'modules' => array(
        'Application',
        'DoctrineModule',
        'DoctrineORMModule',
        'Album'
    ),
    
    // These are various options for the listeners attached to the ModuleManager
    'module_listener_options' => array(
        'module_paths' => array(
            './module',
            './vendor',
        ),
        'config_glob_paths' => array(
            'config/autoload/{,*.}{global,local}.php',
        ),
    ),
);

8. Generate Time !
a. convert-mapping

./vendor/doctrine/doctrine-module/bin/doctrine-module orm:convert-mapping --namespace="Album\\Entity\\" --force  --from-database annotation ./module/Album/src/

it will export “annotation” mapping information into ./module/Album/src/
b. generate-entities

 ./vendor/doctrine/doctrine-module/bin/doctrine-module orm:generate-entities ./module/Album/src/ --generate-annotations=true

it will add setter/getter into entities.
and you will get the following entities AUTOMATICALLY :
Album\Entity\Album

<?php

namespace Album\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Album
 *
 * @ORM\Table(name="album")
 * @ORM\Entity
 */
class Album
{
    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="bigint", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="SEQUENCE")
     * @ORM\SequenceGenerator(sequenceName="album_id_seq", allocationSize=1, initialValue=1)
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="artist", type="string", length=255, nullable=true)
     */
    private $artist;

    /**
     * @var string
     *
     * @ORM\Column(name="title", type="string", length=255, nullable=true)
     */
    private $title;



    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set artist
     *
     * @param string $artist
     * @return Album
     */
    public function setArtist($artist)
    {
        $this->artist = $artist;
    
        return $this;
    }

    /**
     * Get artist
     *
     * @return string 
     */
    public function getArtist()
    {
        return $this->artist;
    }

    /**
     * Set title
     *
     * @param string $title
     * @return Album
     */
    public function setTitle($title)
    {
        $this->title = $title;
    
        return $this;
    }

    /**
     * Get title
     *
     * @return string 
     */
    public function getTitle()
    {
        return $this->title;
    }
}

Album\Entity\Track

<?php

namespace Album\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * Track
 *
 * @ORM\Table(name="track")
 * @ORM\Entity
 */
class Track
{
    /**
     * @var integer
     *
     * @ORM\Column(name="track_id", type="bigint", nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="SEQUENCE")
     * @ORM\SequenceGenerator(sequenceName="track_track_id_seq", allocationSize=1, initialValue=1)
     */
    private $trackId;

    /**
     * @var string
     *
     * @ORM\Column(name="track_title", type="string", length=255, nullable=true)
     */
    private $trackTitle;

    /**
     * @var \Album\Entity\Album
     *
     * @ORM\ManyToOne(targetEntity="Album\Entity\Album")
     * @ORM\JoinColumns({
     *   @ORM\JoinColumn(name="album_id", referencedColumnName="id")
     * })
     */
    private $album;



    /**
     * Get trackId
     *
     * @return integer 
     */
    public function getTrackId()
    {
        return $this->trackId;
    }

    /**
     * Set trackTitle
     *
     * @param string $trackTitle
     * @return Track
     */
    public function setTrackTitle($trackTitle)
    {
        $this->trackTitle = $trackTitle;
    
        return $this;
    }

    /**
     * Get trackTitle
     *
     * @return string 
     */
    public function getTrackTitle()
    {
        return $this->trackTitle;
    }

    /**
     * Set album
     *
     * @param \Album\Entity\Album $album
     * @return Track
     */
    public function setAlbum(\Album\Entity\Album $album = null)
    {
        $this->album = $album;
    
        return $this;
    }

    /**
     * Get album
     *
     * @return \Album\Entity\Album 
     */
    public function getAlbum()
    {
        return $this->album;
    }
}

JUST IMAGINE that YOU SHOULD WRITE THEM MANUALLY :p
9. Fill database tables rows, and Let’s call from whatever controller for testing :

    public function indexAction()
    {
        $em = $this->getServiceLocator()
                ->get('doctrine.entitymanager.orm_default');
        $data = $em->getRepository('Album\Entity\Track')->findAll();
        foreach($data as $key=>$row)
        {
            echo $row->getAlbum()->getArtist().' :: '.$row->getTrackTitle();
            echo '<br />';
        }
    }

References :
1. Conversation with Anass Ans
1. http://docs.doctrine-project.org/en/2.0.x/reference/tools.html
2. http://wildlyinaccurate.com/useful-doctrine-2-console-commands

Advertisements