doctrineorm

Doctrine 2 ve Zend Framework Entegrasyonu

Bu yazımda sizlere Doctrine 2 ve Zend Framework 1.x entegrasyonunu anlatacağım. Doctine Nedir ? Doctrine PHP ile yazılmış bir ORM (Object Relational Mapper) dir. Peki bu ne anlama geliyor. Nesne tabanlı programlama dilleri ile  uyumsuz sistemler arasında veri dönüştürmek için geliştirilmiş bir programlama tekniğidir. Veritabanındaki her bir tabloyu sanallaştırarak bir obje olarak erişimimizi sağlar. Böylece geliştirmekte olduğumuz projede garip […]

Bu yazımda sizlere Doctrine 2 ve Zend Framework 1.x entegrasyonunu anlatacağım.

Doctine Nedir ?
Doctrine PHP ile yazılmış bir ORM (Object Relational Mapper) dir. Peki bu ne anlama geliyor.
Nesne tabanlı programlama dilleri ile  uyumsuz sistemler arasında veri dönüştürmek için geliştirilmiş bir programlama tekniğidir.

Veritabanındaki her bir tabloyu sanallaştırarak bir obje olarak erişimimizi sağlar. Böylece geliştirmekte olduğumuz projede garip sql sorguları yerine güzel güzel objeler görürüz.Sql yazmayız bunun yerine işimizi objelerle hallederiz.

ORM ler anlaşılabilirliği kolaylaştırır.Kodunuz OOP ye çok daha uygun olur. Sql sorgularından sizi kurtarır.Tüm bunlar göz önünde bulundurulduğunda evet ORM kullanmak güzel bişey.Fakat bu objelerle çalışmanın güzel tarafı olduğu kadar bir bedeli de olacak. ORM ler performans kaybına sebebiyet verir.Eğer Facebook Google gibi sistemler ile uğraşıyorsanız ORM kullanmanız neredeyse imkansızdır.

Doctrine 2 nin Doctrine 1.2 ye göre çok daha iyi olduğu söyleniyor. Bunu ben değil geliştiricileri söylüyor.
Doctrine 2.0, 1.2 ye göre çok daha hızlı ve daha az kaynak tüketiyor.

ZF ve Doctrine 2 entegrasyonu yaparken zorlanmadım değil. Aslında bir çok kişi bugüne kadar bunu yaptı.Açıkçası bende bu adamların bloglarını okuyarak kodlarından yardım alarak bu işi yaptım. Onların kodlarını kullanmama sebebim bazı ihtiyaçlarımı kısmen karşılamaması ve beğenmediğim noktalar olması.

Geliştirici Günlüğü olarak geliştirmeyi düşündüğümüz bir core var ve büyük ihtimalle bu core da bunu kullanacağız. O yüzden düzgün bir yapı olmasına dikkat ettim.

Hazırsanız başlayalım:

İlk iş olarak Doctrine i indirelim. http://www.doctrine-project.org/projects/orm adresinden 2.1 sürümünü indirin.

Sonrasında bu indirdiğiniz kaynağı açın ve Doctrine dizinini ZF altında application/library dizini altına konumlandırın.bin dizinini ise direk okarak application dizini altına konumlandırın.

Açıkçası models dizini altından çalıştıramadığım için farklı bir yapılanmaya gittim.Bunun için application dizini altında Entity adında bir dizin açtım.Yani dizin yapım şu şekilde oldu.

Zend Doctrine Entegrasyonu Dizin Yapısı Dizin yapısında library dizini altına bir Base namespace i  oluşturdum.Bu sayede ek bir katman oluşturdum.Dizin yapımızı bu şekilde oluşturduktan sonra gerekli değişikleri yapmaya başlayalım.

Yazının ilerleyen kısımlarında Git Hub üzerinde paylaşacağım.İlk olarak application.ini dosyasını aşağıdaki gibi düzenledim. Doctrine için gerekli bağlantı ayarlarını ekledim.

[production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
includePaths.library = APPLICATION_PATH
bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
bootstrap.class = "Bootstrap"

appnamespace = "Application"
resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
resources.frontController.params.displayExceptions = 0

autoloadernamespaces.base =  "Base_"
autoloadernamespaces.entity = "Entity_"

doctrine.conn.host = '127.0.0.1'
doctrine.conn.user = 'root'
doctrine.conn.pass = 'root'
doctrine.conn.driver = 'pdo_mysql'
doctrine.conn.dbname = 'test'
doctrine.path.models = APPLICATION_PATH "/models"
[staging : production]

[testing : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1

[development : production]
phpSettings.display_startup_errors = 1
phpSettings.display_errors = 1
resources.frontController.params.displayExceptions = 1

public dizini altında index.php dosyasında düzenliyoruz. Kullanılan Application require ı Base dizininden alıyorum.

<?php
// Define path to application directory
defined('APPLICATION_PATH')
    || define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application'));

// Define application environment
defined('APPLICATION_ENV')
    || define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV') : 'production'));

// Ensure library/ is on include_path
set_include_path(implode(PATH_SEPARATOR, array(
    realpath(APPLICATION_PATH . '/../library'),
    get_include_path(),
)));

/** Zend_Application */
require_once 'Zend/Application.php';

// Create application, bootstrap, and run
$application = new Zend_Application(
    APPLICATION_ENV,
    APPLICATION_PATH . '/configs/application.ini'
);
$application->bootstrap()->run();

Şimdi Base namespace i altına Bootstrap.php dosyasını oluşturuyorum.(Burada implementasyon için kaynak : http://www.oelerich.org/integrate-doctrine-2-with-zend-framework-1-11-3/

<?php
class Base_Bootstrap extends Zend_Application_Bootstrap_Bootstrap {

    /**
     * generate registry
     * @return Zend_Registry
     */
    protected function _initRegistry() {
        $registry = Zend_Registry::getInstance();
        return $registry;
    }

    /**
     * Initialize Doctrine
     * @return Doctrine_Manager
     */
    public function _initDoctrine() {
        // Doctrine class loader
        require_once('Doctrine/Common/ClassLoader.php');
        $classLoader = new \Doctrine\Common\ClassLoader(
                        'Doctrine',
                        APPLICATION_PATH . '/../library/'
        );
        $classLoader->register();

        // Doctrine configuration
        $config = new \Doctrine\ORM\Configuration();

        // Doctrine cache implementasyonu
        $cache = new \Doctrine\Common\Cache\ArrayCache;
        $config->setMetadataCacheImpl($cache);
        $config->setQueryCacheImpl($cache);

        // Annotations
        $driver = $config->newDefaultAnnotationDriver(
                APPLICATION_PATH . '/Entity'
        );
        $config->setMetadataDriverImpl($driver);

        // set the proxy dir and set some options
        $config->setProxyDir(APPLICATION_PATH . '/models/Proxies');
        $config->setAutoGenerateProxyClasses(true);
        $config->setProxyNamespace('App\Proxies');

        // doctrine connection
        $connectionSettings = $this->getOption('doctrine');
        $conn = array(
            'driver' => $connectionSettings['conn']['driver'],
            'user' => $connectionSettings['conn']['user'],
            'password' => $connectionSettings['conn']['pass'],
            'dbname' => $connectionSettings['conn']['dbname'],
            'host' => $connectionSettings['conn']['host']
        );
        $entityManager = \Doctrine\ORM\EntityManager::create($conn, $config);

        // Entity manager register
        $registry = Zend_Registry::getInstance();
        $registry->entitymanager = $entityManager;

        return $entityManager;
    }

}

Gerçek Bootstrap.php ise oluşturduğum Base_Bootstrap ten miras alıyor.

<?php
class Bootstrap extends Base_Bootstrap {

}

Sonrasında bir entity oluşturdum.Entity dizini altına User.php isminde bir dosya açtım.

<?php
use Doctrine\ORM\Mapping as ORM;

/**
 * User
 *
 * @Table(name="user")
 * @Entity
 */
class Entity_User
{
    /**
     * @var integer $userid
     *
     * @Column(name="userId", type="integer", nullable=false)
     * @Id
     * @GeneratedValue(strategy="IDENTITY")
     */
    private $userid;

    /**
     * @var string $username
     *
     * @Column(name="userName", type="string", length=255, nullable=false)
     */
    private $username;

    /**
     * @var string $usermail
     *
     * @Column(name="userMail", type="string", length=255, nullable=false)
     */
    private $usermail;

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

    /**
     * @var bigint $registerdate
     *
     * @Column(name="registerDate", type="bigint", nullable=false)
     */
    private $registerdate;

    /**
     * @var string $status
     *
     * @Column(name="status", type="string", length=255, nullable=false)
     */
    private $status;

    /**
     * @var string $useravatar
     *
     * @Column(name="userAvatar", type="string", length=255, nullable=false)
     */
    private $useravatar;

    /**
     * @var string $usertype
     *
     * @Column(name="userType", type="string", length=255, nullable=false)
     */
    private $usertype;

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

    /**
     * Set username
     *
     * @param string $username
     */
    public function setUsername($username)
    {
        $this->username = $username;
    }

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

    /**
     * Set usermail
     *
     * @param string $usermail
     */
    public function setUsermail($usermail)
    {
        $this->usermail = $usermail;
    }

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

    /**
     * Set password
     *
     * @param string $password
     */
    public function setPassword($password)
    {
        $this->password = $password;
    }

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

    /**
     * Set registerdate
     *
     * @param bigint $registerdate
     */
    public function setRegisterdate($registerdate)
    {
        $this->registerdate = $registerdate;
    }

    /**
     * Get registerdate
     *
     * @return bigint
     */
    public function getRegisterdate()
    {
        return $this->registerdate;
    }

    /**
     * Set status
     *
     * @param string $status
     */
    public function setStatus($status)
    {
        $this->status = $status;
    }

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

    /**
     * Set useravatar
     *
     * @param string $useravatar
     */
    public function setUseravatar($useravatar)
    {
        $this->useravatar = $useravatar;
    }

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

    /**
     * Set usertype
     *
     * @param string $usertype
     */
    public function setUsertype($usertype)
    {
        $this->usertype = $usertype;
    }

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

Şimdi bin dizini altında doctrine.php yi şu şekilde düzenliyoruz.

<?php
define('APPLICATION_ENV', 'development');
define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application'));

set_include_path(implode(PATH_SEPARATOR, array(
    realpath(APPLICATION_PATH . '/../library'),
    get_include_path(),
)));

// Doctrine and Symfony Classes
require_once 'Doctrine/Common/ClassLoader.php';
$classLoader = new \Doctrine\Common\ClassLoader('Doctrine', APPLICATION_PATH . '/../library');
$classLoader->register();
$classLoader = new \Doctrine\Common\ClassLoader('Symfony', APPLICATION_PATH . '/../library/Doctrine');
$classLoader->register();
$classLoader = new \Doctrine\Common\ClassLoader('Entities', APPLICATION_PATH . '/Entity');
$classLoader->setNamespaceSeparator('_');
$classLoader->register();

// Zend Components
require_once 'Zend/Application.php';

// Create application
$application = new Zend_Application(
    APPLICATION_ENV,
    APPLICATION_PATH . '/configs/application.ini'
);

// bootstrap doctrine
$application->getBootstrap()->bootstrap('doctrine');
$em = $application->getBootstrap()->getResource('doctrine');

// generate the Doctrine HelperSet
$helperSet = new \Symfony\Component\Console\Helper\HelperSet(array(
    'db' => new \Doctrine\DBAL\Tools\Console\Helper\ConnectionHelper($em->getConnection()),
    'em' => new \Doctrine\ORM\Tools\Console\Helper\EntityManagerHelper($em)
));

\Doctrine\ORM\Tools\Console\ConsoleRunner::run($helperSet);
./doctrine orm:schema-tool:create

komutu ile Entity den şema oluşturabilirsiniz.

Peki benim gibi zaten DB niz hazır ise o zaman ne yapacaksınız. Örnek kodlarda bir generator var bin dizini altında generator.php bu dosyayı kendi ayarlarınıza göre düzenleyebilirsiniz.

Bu generator u aldığım ve düzenlediğim kaynağı tekrar bulamadım. O yüzden bu generator u ilk yazan ve yayınlayan arkadaştan gerçekten çok özür dilerim.

Son olarak çalıştığını test edelim.

<?php
class IndexController extends Zend_Controller_Action {

    public function init() {
        $registry = Zend_Registry::getInstance();
        $this->_em = $registry->entitymanager;
    }

    public function indexAction() {
        try {

            $userEntity = new Entity_User;
            $userEntity->setUsername('Mustafa');
            $userEntity->setUsermail('[email protected]');
            $userEntity->setPassword(md5('pass'));
            $userEntity->setRegisterdate(time());
            $userEntity->setStatus('active');
            $userEntity->setUsertype('admin');
            $userEntity->setUseravatar('a.png');
            $this->_em->persist($userEntity);
            $this->_em->flush();

        } catch (Exception $exc) {
            echo $exc->getMessage();
        }
    }

}

Eğer herşeyi eksiksiz yazmayı başarabilmiş isem herşeyin düzgün çalışması gerekir.Eğer eksik birşey yazdıysam çalışan kaynak kodu buraya ekledim.İndirebilirsiniz.

Ek olarak önemli 2 noktayı paylaşmak istiyorum:

1. Doctrine 1 de pk sız olan tablolar için modeller oluşturabiliyorduk.Entity i generate ederken bununla ilgili hatalar aldım.Yani tablolarınızda PK olmalı.

2. Doctrine 2 de enum alanı string olarak kullanabiliyoruz.Generator da böyle yapılmasının sebebi.ENUM alana göre Entity generate edilmeye çalışıldığında hata veriyordu.

ZF 2 de sanırım Doctrine entegre olarak gelecek.