Точные имена свойств модели в Doctrine

Эта статья посвящена точному(с учётом регистра) отображению названий колонок таблицы БД в свойства модели. Данная проблема актуальна для 1ой ветки ORM, т.к. во второй ветке она похоже уже решена. Но т.к. стабильным релизом на данный момент является версия 1.2.1 а версия 2 в стадии ALPHA4, то думаю, что эта проблема актуальная для многих пользователей ORM.

Если camelCase названия/алиасы колонок не используются в вашем проекте, то данное решение будет неактуально для вас.

Проблема

Во время работы с Doctrine, у меня возникла проблема. Многие столбцы БД имели названия вида camelCase. А при получении записей превращались в lowercase. Сначала я грешил на Mysql.

Column, index, and stored routine names are not case sensitive on any platform, nor are column aliases. Trigger names are case sensitive, which differs from standard SQL.

см. Identifier-case-sensitivity

Из этого стало понятно, что алиасы колонок всё-таки зависят от регистра, и суть проблемы в том, что сама Doctrine неправильно(без учета регистра) подставляет алиасы для колонок, делая им strtolower(). Погуглив, набрел на эту страничку. Баг закрыт для второй ветки. А для первой автоматически производится strtolower() для имен колонок. Осталось найти где…

Решение

Участки кода нашлись довольно быстро. Это Doctrine_Table -> setColumn(). Там мы видим:

$name = strtolower($parts[0]);

и

$name = strtolower($name);

Именно они отвечают за приведение имен колонок к нижнему регистру. Теперь наша задача, не изменяя классы ORM сделать так чтобы регистр имен колонок не изменялся.
Во-первых создаем свой класс MyDoctrine_Table, в котором перекрываем функцию setColumn() и заменяем эти участки кода.

class MyDoctrine_Table extends Doctrine_Table
{

public function setColumn($name, $type, $length = null, $options = array(), $prepend = false)
{
if (is_string($options)) {
$options = explode('|', $options);
}

foreach ($options as $k => $option) {
if (is_numeric($k)) {
if ( ! empty($option)) {
$options[$option] = true;
}
unset($options[$k]);
}
}

// extract column name & field name
if (stripos($name, ' as '))
{
if (strpos($name, ' as ')) {
$parts = explode(' as ', $name);
else {
$parts = explode(' AS ', $name);
}

if (count($parts) > 1) {
$fieldName = $parts[1];
else {
$fieldName = $parts[0];
}

//$name = strtolower($parts[0]);
$name = $parts[0];
else {
$fieldName = $name;
//$name = strtolower($name);
}

$name = trim($name);
$fieldName = trim($fieldName);

if ($prepend) {
$this->_columnNames = array_merge(array($fieldName => $name), $this->_columnNames);
$this->_fieldNames = array_merge(array($name => $fieldName), $this->_fieldNames);
else {
$this->_columnNames[$fieldName] = $name;
$this->_fieldNames[$name] = $fieldName;
}

if ($length == null) {
switch ($type) {
case 'integer':
$length = 8;
break;
case 'decimal':
$length = 18;
break;
case 'string':
case 'clob':
case 'float':
case 'integer':
case 'array':
case 'object':
case 'blob':
case 'gzip':
//$length = 2147483647;

//All the DataDict driver classes have work-arounds to deal
//with unset lengths.
$length = null;
break;
case 'boolean':
$length = 1;
case 'date':
// YYYY-MM-DD ISO 8601
$length = 10;
case 'time':
// HH:NN:SS+00:00 ISO 8601
$length = 14;
case 'timestamp':
// YYYY-MM-DDTHH:MM:SS+00:00 ISO 8601
$length = 25;
break;
}
}

$options['type'] = $type;
$options['length'] = $length;

if ($prepend) {
$this->_columns = array_merge(array($name => $options), $this->_columns);
else {
$this->_columns[$name] = $options;
}

if (isset($options['primary']) && $options['primary']) {
if (isset($this->_identifier)) {
$this->_identifier = (array) $this->_identifier;
}
if ( ! in_array($fieldName, $this->_identifier)) {
$this->_identifier[] = $fieldName;
}
}
if (isset($options['default'])) {
$this->hasDefaultValues = true;
}
}

}

* This source code was highlighted with Source Code Highlighter.

Затем в bootstrap.php инклудим его, и регистрируем как класс таблицы Doctrine.

// Убирает автоматический lowercase для столбцов таблицы
require_once(dirname(__FILE__) . '/lib/doctrine_extra/MyDoctrine/Table.php');
$conn->setAttribute(Doctrine::ATTR_TABLE_CLASS, 'MyDoctrine_Table');

* This source code was highlighted with Source Code Highlighter.

Всё, теперь название свойств моделей полностью совпадает с названиями столбцов таблицы.

Прошу строго не судить, если у вас есть другие варианты решения этой задачи, буду рад услышать их в комментариях.

Leave a Comment