Разработка

Использование Доктрины объектно-реляционной проекции (ORM) в Joomla!

Joomla
Добавление в избранное
Сохранить

Реализация Доктрины ORM в Joomla

ORM (англ. Object-relational mapping, рус. Объектно-реляционное отображение) — технология программирования, которая связывает базы данных с концепциями объектно-ориентированных языков программирования, создавая «виртуальную объектную базу данных»

ru.wikipedia.org

 

Доктрина ORM довольно хорошо работает применительно к Joomla, она хороша для конечного пользователя. На просторах интернета под ORM существует множество компонентов, готовых к использованию. Как разработчика, меня не очень устраивает реализация Доктрины ORM в Joomla!. В частности, реализация классов «model» и «table» довольно неудобна. Очень сложно также внедрить другие модели в контроллере или добавить другой класс моделей. В данной статье я постараюсь объяснить, как использовать технологию ORM для вашего компонента в Joomla!. Доктрина является ORM фреймворком, в котором есть постоянная библиотека. Фреймворк не панацея, и вам нужно будет самостоятельно решить, нужны ли будут дополнительные средства или достаточно только ORM.

Шаг первый

Начну с чистой инсталляции Joomla 2.5 с официального сайта. Я также установил Доктрину ORM с ее официального сайта. Распокавываем архив с ORM в новую папку в каталоге библиотек Joomla!. Структура останется такой же как и в архиве, таким образом, у нас будет папка Доктрины и папка bin в каталоге libraries/doctrine.

Примечание: для успешной работы ORM, необходимо, чтобы на сервере был установлен PHP версии 5.3. Инструкции, приведеннные в данной статье подразумевают, что у вас установлена именно эта версия (PHP 5.3). Если на сервере используется устаревшая версия PHP 5.2, следует подумать об обновлении.

Пример

Моя задача: показать, как внедрить Доктрину в проект на Joomla! и как ее использовать в дальнейшем. Информация о Доктрине на официальном сайте довольно подробная и может существенно помочь в изучении вопроса. Приведу небольшой пример. Создаем новый компонент под названием «Bugs» и весь объект будет называться «Bug».У него 5 атрибутов: ID, название, описание, параметры notificationDate и solvedDate. Я покажу, как использовать все это в контроллере.

Конфигурация по умолчанию

Возможно, вы захотите использовать Доктрину в нескольких своих проектах. Вот почему я предпочитаю добавлять Доктрину в папку библиотек. В эту библиотеку мы добавим два файла. Один отвечает за многофункциональный интерфейс, другой поможет Доктрине запуститься и работать. Сначала простая задача – интерфейс. Он выглядит следующим образом:

interface JoomlaDoctrineController {
    public function setEntityManager(Doctrine\ORM\EntityManager $entityManager);

Здесь ничего нового. Продолжим. Для работы Доктрины нужна особая конфигурация. Ей нужно указать путь к объектам и где разместить сгенерированные прокси. Вот решение для рабочего файла (JoomlaDoctrineBootstrapper):


/**
 * Конфигурационный класс для интеграции Доктрины в Joomla.
 *
 * @author pderaaij
 */
use Doctrine\Common\ClassLoader,
    Doctrine\ORM\EntityManager,
    Doctrine\ORM\Configuration,
   Doctrine\Common\Cache\ArrayCache;
if( !class_exists('\Doctrine\Common\Classloader')) {
    require_once dirname(__FILE__) . '/../doctrine/Doctrine/Common/ClassLoader.php';
}
class JoomlaDoctrineBootstrapper {
    const APP_MODE_DEVELOPMENT = 1;
    const APP_MODE_PRODUCTION  = 2;
    private $applicationMode;
    private $cache;
    private $entityLibrary;
    private $proxyLibrary;
    private $proxyNamespace;
    private $entityManager;
    private $connectionOptions;
    public function __construct($applicationMode) {
        $this->applicationMode = $applicationMode;
    }
    public function getConnectionOptions() {
        return $this->connectionOptions;
    }
    public function setConnectionOptions($connectionOptions) {
        $this->connectionOptions = $connectionOptions;
    }
    public function getProxyLibrary() {
        return $this->proxyLibrary;
    }
    public function setProxyLibrary($proxyLibrary) {
        $this->proxyLibrary = $proxyLibrary;
    }
    public function getProxyNamespace() {
        return $this->proxyNamespace;
    }
    public function setProxyNamespace($proxyNamespace) {
        $this->proxyNamespace = $proxyNamespace;
    }
    public function getCache() {
        return $this->cache;
    }
    public function setCache($cache) {
        $this->cache = $cache;
    }
    public function getEntityLibrary() {
        return $this->entityLibrary;
    }
    public function setEntityLibrary($entityLibrary) {
        $this->entityLibrary = $entityLibrary;
    }
    public function getApplicationMode() {
        return $this->applicationMode;
    }
    public function setApplicationMode($applicationMode) {
        $this->applicationMode = $applicationMode;
    }
    public function getEntityManager() {
        return $this->entityManager;
    }
    public function setEntityManager($entityManager) {
        $this->entityManager = $entityManager;
    }
    /**
     * Bootstrap Доктрина, выставляющая библиотеки и пространства имен и создающая
     * менеджер сущностей
     */
    public function bootstrap() {
        $this->registerClassLoader();
        // Load cache
        if ($this->getApplicationMode() == self::APP_MODE_DEVELOPMENT) {
            $this->cache = new ArrayCache;
        } else {
            $this->cache = new ApcCache;
        }
        /** @var $config Doctrine\ORM\Configuration */
        $config = new Configuration;
        $config->setMetadataCacheImpl($this->cache);
        $driverImpl = $config->newDefaultAnnotationDriver($this->getEntityLibrary());
        $config->setMetadataDriverImpl($driverImpl);
        $config->setQueryCacheImpl($this->cache);
        $config->setProxyDir($this->getProxyLibrary());
        $config->setProxyNamespace($this->getProxyNamespace());
        if ($this->applicationMode == self::APP_MODE_DEVELOPMENT) {
            $config->setAutoGenerateProxyClasses(true);
        } else {
            $config->setAutoGenerateProxyClasses(false);
        }
        $this->entityManager = EntityManager::create($this->getConnectionOptions(), $config);
    }
    /**
     * Регистрация различных загрузчиков классов для каждого типа
     */
    private function registerClassLoader() {
        // Autoloader for all the Doctrine library files
        $classLoader = new ClassLoader('Doctrine', dirname(__FILE__) . '/');
        $classLoader->register();
        // Autoloader for all Entities
        $modelLoader = new ClassLoader('Entities', $this->getEntityLibrary());
        $modelLoader->register();
        // Autoloader for all Proxies
        $proxiesClassLoader = new ClassLoader('Proxies', $this->getProxyLibrary());
        $proxiesClassLoader->register();
    }
}

Как уже упоминалось выше, для успешной работы на сервере должна быть установлена версия PHP не ниже 5.3, поскольку мы часто пользуемся пространствами имён. Загружаем некоторые основные классы Доктрины, чтобы запустить ее. В этом классе две постоянные: флажки, которые можно использовать для того, чтобы настроить среду приложения. Если приложение используется на рабочем проекте, то можно использовать кэш APC. При разработке будет использоваться простой кэш ArrayCache, который достаточно непостоянен.

Итак, у нас есть две весьма интересные функции. Первая – registerClassLoader, в ней мы регистрируем три загрузчика классов (classloaders). Первая функция применяется для автозагрузки всех классов Доктрины. Вторая функция для создаваемых нами объектов. Прокси автоматически подгружаются при помощи третьего загрузчика классов. Параметр, который получают последние два загрузчика – это полные пути к папкам, содержащим эти файлы. В функции загрузки мы описываем конфигурацию Доктрины. Определяем используемый кэш, драйвер аннотации (AnnotationDriver) и папки, где находятся прокси и объекты. Чтобы лучше разобраться в конфигурации Доктрины, прочтите мануал. Мы делаем стандартную конфигурацию – устанавливаем верное местонахождение.

Создание компонента

Теперь можно настроить наш компонент. Создадим папку com_bugs в компонентах и в ней папку models. В папке models, создайте папку Entities (не забудьте про заглавную букву, это важно). Создайте объект под именем «Bug». Вставьте в файл Bug.php (models/Entities) текст, приведенный ниже. Важно не забыть установите пространство имен, иначе загрузчик классов не сможет найти объект. Также важно учитывать заглавные буквы.


namespace Entities;
/**
* @Entity @Table(name="bugs")
*/
 
class Bug {
/** @Id @Column(type="integer") @GeneratedValue */
private $id;
/** @Column(type="string") */
 
private $title;
/** @Column(type="string") */
 
private $description;
/** @Column(type="datetime") */
 
private $notificationDate;
/** @Column(type="datetime") */
 
private $solvedDate;
public function getId() {
return $this->id;
}
 
public function setId($id) {
$this->id = $id;
}
 
public function getTitle() {
return $this->title;
}
 
public function setTitle($title) {
$this->title = $title;
}
 
public function getDescription() {
return $this->description;
}
 
public function setDescription($description) {
$this->description = $description;
}
 
public function getNotificationDate() {
return $this->notificationDate;
}
 
public function setNotificationDate($notificationDate) {
$this->notificationDate = $notificationDate;
}
 
public function getSolvedDate() {
return $this->solvedDate;
}
 
public function setSolvedDate($solvedDate) {
$this->solvedDate = $solvedDate;
}
 
}

Объект создан. Теперь нам нужно получить его описание в нашей базе данных. В Доктрине есть инструменты командной строки, но у них должна быть такая же конфигрурация, как и у контроллера в клиентской части. Давайте настроим конфигурацию, и используем ее в командной строке.

Конфигурация ORM

Для того, чтобы можно было использовать Доктрину в наших контроллерах, нужно создать конфигурацию и использовать загрузчик. Я выбираю файл точки входа компонента для работы над инициализацией Доктрины. Я пытаюсь вытащить код инициализации в более подходящее место, но у меня ничего хорошего не выходит. Нужно указать местонахождение наших сущностей и где необходимо хранить файлы прокси. Также перенесем параметры из базы данных в Доктрину для того, чтобы у нее был доступ к базам. Вот файл инициализации компонента (bugs.php):


// no direct access
defined('_JEXEC') or die;
// Включение зависимостей (include dependancies)
jimport('joomla.application.component.controller');
require_once(JPATH_LIBRARIES . '/doctrine/bootstrap.php');
$controller = JController::getInstance('Bugs');
// configure Doctrine thru the bootstrapper
$controller->setEntityManager(bootstrapDoctrine());
$controller->execute(JRequest::getCmd('task', 'index'));
$controller->redirect();
/**
 * Инициализация Доктрины путем выставления сущностей и прокси локаций. Также установка
 * пространства имен по умолчанию для прокси.
 */
function bootstrapDoctrine() {
    $doctrineProxy = new JoomlaDoctrineBootstrapper( JoomlaDoctrineBootstrapper::APP_MODE_DEVELOPMENT );
    $doctrineProxy->setEntityLibrary(dirname(__FILE__) . '/models');
    $doctrineProxy->setProxyLibrary(dirname(__FILE__) . '/proxies');
    $doctrineProxy->setProxyNamespace('Joomla\Proxies');
    $doctrineProxy->setConnectionOptions(getConfigurationOptions());
    $doctrineProxy->bootstrap();
    return $doctrineProxy->getEntityManager();
}
function getConfigurationOptions() {
     // Определение опций конфигурации базы данных
    $joomlaConfig = JFactory::getConfig();
    return array(
        'driver' => 'pdo_mysql',
        'path' => 'database.mysql',
        'dbname' => $joomlaConfig->get('db'),
        'user' =>  $joomlaConfig->get('user'),
        'password' =>  $joomlaConfig->get('password')
    );
}

Теперь можно использовать ORM в вашем компоненте. Надеюсь, это был не самый трудный способ обращения с Доктриной объектно-реляционной проекции в Joomla!

Оригинальная статья: Paul de Raaij
Katerina Vorobyova
Переводчик, IT любитель, фотомодель.

Подпишитесь на рассылку новостей CMScafe