Троичная логика (ternary logic) в Doctrine ORM
// 7 июля, 2011 // Doctrine
В этой статье речь пойдет о третичной логике. Я не буду говорить об операциях в троичной логике, а коснусь лишь вопросов хранения данных в Doctrine.
Представим, что есть некоторый столбец в БД, данные которого могут принимать три возможных значения. Для хранения типа boolean (двоичная логика) Doctrine используется тип данных boolean в PHP, а при отражении в таблицу он проектируется в tinyint(1). Но как тогда кодировать третье значение? Мы будем использовать NULL. Т.е. область определения = {0,1,NULL}. Значит notnull => false. Но тогда возникает одна проблема.
$model = new SomeModel(); $model->ternary_field = NULL; $model->save();
При этом срабатывает приведение типов, и в БД сохраняется 0, а не NULL. Приведение типов столбцов закодировано в файле Doctrine_Record::getPrepared():
public function getPrepared(array $array = array()) { $a = array(); if (empty($array)) { $modifiedFields = $this->_modified; } foreach ($modifiedFields as $field) { $type = $this->_table->getTypeOf($field); if ($this->_data[$field] === self::$_null) { $a[$field] = null; continue; } switch ($type) { case 'array': case 'object': $a[$field] = serialize($this->_data[$field]); break; case 'gzip': $a[$field] = gzcompress($this->_data[$field],5); break; case 'boolean': $a[$field] = $this->getTable()->getConnection()->convertBooleans($this->_data[$field]); break; case 'set': if (is_array($this->_data[$field])) { $a[$field] = implode(',', $this->_data[$field]); } else { $a[$field] = $this->_data[$field]; } break; default: if ($this->_data[$field] instanceof Doctrine_Record) { $a[$field] = $this->_data[$field]->getIncremented(); if ($a[$field] !== null) { $this->_data[$field] = $a[$field]; } } else { $a[$field] = $this->_data[$field]; } /** TODO: if ($this->_data[$v] === null) { throw new Doctrine_Record_Exception('Unexpected null value.'); } */ } } return $a; }
А для типа boolean срабатывает вот этот case:
case 'boolean': $a[$field] = $this->getTable()->getConnection()->convertBooleans($this->_data[$field]); break;
Определение столбца выглядело следующим образом:
$this->hasColumn('ternary_field', 'boolean', null, array( 'default' => false ));
Не буду томить с решением проблемы, во общем всё портил параметр default. После его изменения на default=>NULL в базу стали сохраняться правильные значения.
Литература
http://stackoverflow.com/questions/1391777/how-do-i-insert-null-values-using-pdo
http://php.net/manual/en/pdostatement.bindparam.php
Спасибо!
Если вам помогла статья, или вы хотите поддержать мои исследования и блог - вот лучший способ сделать это:
по идее нужно использовать Expression, в первой ветке делалось так.
$model->ternary_field = new Doctrine_Expression(‘NULL’);
Пробовали все методы (см. http://stackoverflow.com/questions/1391777/how-do-i-insert-null-values-using-pdo). Они все работают только в случае default => NULL, при default => false ни один не работает. А т.к. потом всё заработало, то решили остановиться на простом присваивании.