+++ /dev/null
-<?php
-
-use \PFF\HtmlBuilder\Text as Text;
-use \PFF\HtmlBuilder\Tag as Tag;
-
-class IPF_Admin_Model extends IPF_Admin_Component
-{
- public $modelName;
-
- public function __construct($modelName)
- {
- $this->modelName = $modelName;
- }
-
- public function verbose_name()
- {
- return IPF_Utils::humanTitle($this->modelName);
- }
-
- public function slug()
- {
- return strtolower($this->modelName);
- }
-
- private function table()
- {
- return IPF_ORM::getTable($this->modelName);
- }
-
- protected function query($searchValue, $filters)
- {
- if (method_exists($this->modelName, 'ordering')) {
- $m = new $this->modelName;
- $ord = $m->ordering();
- } elseif ($this->table()->getOrdering()) {
- $ord = implode(', ', $this->table()->getOrdering());
- } elseif ($this->table()->hasTemplate('IPF_ORM_Template_Orderable')) {
- $ord = $this->table()->getTemplate('IPF_ORM_Template_Orderable')->getColumnName();
- } else {
- $ord = '1 desc';
- }
-
- $q = IPF_ORM_Query::create()->from($this->modelName)->orderby($ord);
-
- if ($searchValue) {
- $wh = array();
- $whv = array();
- foreach ($this->_searchFields() as $f) {
- $wh[] = $f.' like ?';
- $whv[] = '%'.$searchValue.'%';
- }
- $q->addWhere($wh, $whv);
- }
-
- foreach ($filters as $f)
- $f->applyToQuery($q);
-
- return $q;
- }
-
- protected function getItems($searchValue, $filters, $page, $pageSize)
- {
- $idColumn = $this->table()->getIdentifier();
-
- $result = array();
- foreach ($this->query($searchValue, $filters)->limit($pageSize)->offset(($page - 1) * $pageSize)->execute() as $o) {
- $id = $o->__get($idColumn);
- $result[$id] = $o;
- }
- return $result;
- }
-
- protected function itemsCount($searchValue, $filters)
- {
- return $this->query($searchValue, $filters)->count();
- }
-
- public function getObjectByID($id)
- {
- return $this->table()->find($id);
- }
-
- public function searcheable()
- {
- return count($this->_searchFields()) > 0;
- }
-
- protected function _searchFields()
- {
- return array();
- }
-
- public function list_display()
- {
- return $this->table()->getColumnNames();
- }
-
- public function renderCell($object, $column)
- {
- $value = $object->$column;
- switch ($object->getTable()->getTypeOf($column)) {
- case 'boolean':
- return $value
- ? '<span class="positive">✔</span>'
- : '<span class="negative">✘</span>';
- case 'timestamp':
- return Text::escape(IPF_Utils::formatDate($value));
- default:
- return Text::escape($value);
- }
- }
-
- protected function columnTitle($column)
- {
- $columns = $this->table()->getColumns();
- if (array_key_exists($column, $columns) && array_key_exists('verbose', $columns[$column]))
- return $columns[$column]['verbose'];
- else
- return parent::columnTitle($column);
- }
-
- public function _orderable()
- {
- return $this->_orderableColumn() !== null;
- }
-
- public function _orderableColumn()
- {
- if (method_exists($this, 'list_order'))
- return $this->list_order();
- elseif ($this->table()->hasTemplate('IPF_ORM_Template_Orderable'))
- return $this->table()->getTemplate('IPF_ORM_Template_Orderable')->getColumnName();
- else
- return null;
- }
-
- /* filters */
-
- protected function _listFilters()
- {
- return array();
- }
-
- public function listFilters()
- {
- $filters = array();
- foreach ($this->_listFilters() as $f) {
- if (is_string($f))
- $f = new IPF_Admin_Model_RelationFilter($this->modelName, $f);
-
- $filters[] = $f;
- }
- return $filters;
- }
-
- public function reorder($ids)
- {
- if (!$this->_orderable())
- return false;
-
- $ord_field = $this->_orderableColumn();
-
- $table = IPF_ORM::getTable($this->modelName);
- $conn = $table->getConnection();
-
- $idColumn = $table->getIdentifier();
- if (is_array($idColumn))
- $idColumn = $idColumn[0];
-
- $questions = str_repeat('?,', count($ids)-1) . '?';
- $query = 'SELECT ' . $conn->quoteIdentifier($ord_field) .
- ' FROM ' . $conn->quoteIdentifier($table->getTableName()) .
- ' WHERE ' . $conn->quoteIdentifier($idColumn) . ' IN (' . $questions . ')' .
- ' ORDER BY ' . $conn->quoteIdentifier($ord_field);
- $ords = $conn->fetchColumn($query, $ids);
-
- $i = 0;
- foreach ($ids as $id) {
- $item = $table->find($id);
- $item[$ord_field] = $ords[$i];
- $item->save();
- $i++;
- }
-
- return true;
- }
-
- /* edit */
-
- protected function _getForm($model, $data)
- {
- $extra = array(
- 'user_fields' => $this->fields(),
- 'inlines' => $this->inlines(),
- );
- return new IPF_Admin_ModelForm($data, $model, $this->modelName, $extra);
- }
-
- public function inlines()
- {
- return array();
- }
-
- public function saveObject($form, $model)
- {
- $model = $form->save();
- $idColumn = $this->table()->getIdentifier();
- return array($model->$idColumn, $model);
- }
-
- public function deleteObject($model)
- {
- $model->delete();
- }
-
- public function fields()
- {
- return null;
- }
-}
-
-class IPF_Admin_ModelForm extends IPF_Form_Model
-{
- private $inlines = array();
-
- function __construct($data=null, $model=null, $modelClass='', $extra=array())
- {
- $editMode = $model !== null;
-
- if (!$model)
- $model = new $modelClass;
-
- $extra['model'] = $model;
-
- if ($extra['inlines']) {
- foreach ($extra['inlines'] as $inlineClassName) {
- $this->inlines[] = new $inlineClassName($model);
- }
- }
-
- if ($editMode) {
- $extra['initial'] = $this->getFormData($model, $this->inlines);
- }
-
- parent::__construct($data, $extra);
- }
-
- function initFields($extra=array())
- {
- parent::initFields($extra);
-
- if ($this->inlines) {
- $this->field_groups[] = array('fields' => array_keys($this->fields));
- }
-
- foreach ($this->inlines as $inline) {
- $name = $inline->getModelName();
- $this->fields[$name] = $inline->createField();
-
- $this->field_groups[] = array(
- 'label' => $inline->getLegend(),
- 'fields' => array($name),
- );
- }
- }
-
- private function getFormData($o, $inlines)
- {
- $data = $o->getData();
- foreach ($o->getTable()->getRelations() as $rname => $rel) {
- $fields = $this->fields();
- if (!$fields || in_array($rname, $fields)) {
- if ($rel->getType() == IPF_ORM_Relation::MANY_AGGREGATE) {
- $data[$rname] = array();
- foreach ($rel->fetchRelatedFor($o) as $ri) {
- $data[$rname][] = $ri->pk();
- }
- }
- }
- }
-
- foreach ($inlines as $inline) {
- $objs = array();
- foreach ($inline->getObjects() as $io) {
- $d = $io->getData();
- $d['id'] = $io->pk();
- $objs[] = $d;
- }
- $data[$inline->getModelName()] = $objs;
- }
-
- return $data;
- }
-
- function save($commit=true)
- {
- $model = parent::save($commit);
-
- foreach ($this->inlines as $inline) {
- $this->saveInlineObject($inline, $model);
- }
-
- return $model;
- }
-
- private function saveInlineObject($inline, $model)
- {
- $objIndex = array();
- foreach ($inline->getObjects() as $obj)
- $objIndex[$obj->pk()] = $obj;
-
- $modelName = $inline->getModelName();
- $fk_local = $inline->getFkLocal();
-
- foreach ($this->cleaned_data[$modelName] as $objData) {
- if (array_key_exists('id', $objData) && array_key_exists($objData['id'], $objIndex)) {
- $obj = $objIndex[$objData['id']];
- if (isset($objData['is_remove'])) {
- $obj->delete();
- } else {
- $obj->synchronizeWithArray($objData);
- $obj->save();
- }
- } else {
- $objData[$fk_local] = $model->id;
- $obj = new $modelName;
- $obj->synchronizeWithArray($objData);
- $obj->save();
- }
- }
- }
-}
-
-abstract class IPF_Admin_Model_Filter implements IPF_Admin_ListFilter
-{
- abstract function applyToQuery($q);
-
- protected $title, $paramName;
-
- function title()
- {
- return $this->title;
- }
-
- protected function link($params, $value, $title)
- {
- if ($value !== null)
- $params[$this->paramName] = $value;
- else
- unset($params[$this->paramName]);
-
- return Tag::a()
- ->attr('href', '?'.IPF_HTTP_URL::generateParams($params, false))
- ->append($title);
- }
-}
-
-class IPF_Admin_Model_RelationFilter extends IPF_Admin_Model_Filter
-{
- private $relation, $selected = null;
-
- function __construct($modelName, $relName)
- {
- $this->relation = IPF_ORM::getTable($modelName)->getRelation($relName);
- $this->title = 'By '.IPF_Utils::humanTitle($relName);
-
- $this->paramName = 'filter_'.$this->relation['local'];
- }
-
- function setParams($params)
- {
- $this->selected = \PFF\Arr::get($params, $this->paramName);
- }
-
- function applyToQuery($q)
- {
- // check ???
- if ($this->selected)
- $q->addWhere($this->relation['local'].' = ?', array($this->selected));
- }
-
- function render($extraParams)
- {
- $ul = Tag::ul();
-
- // reset filter
- $ul->append(Tag::li()
- ->toggleClass('selected', !$this->selected)
- ->append($this->link($extraParams, null, __('All'))));
-
- // query related
- $table = IPF_ORM::getTable($this->relation['class']);
-
- $query = $table->createQuery();
- if ($table->getOrdering()) {
- $query->orderBy(implode(', ', $table->getOrdering()));
- } elseif ($table->hasTemplate('IPF_ORM_Template_Orderable')) {
- $query->orderBy($table->getTemplate('IPF_ORM_Template_Orderable')->getColumnName());
- }
-
- $foreign = $this->relation['foreign'];
- foreach ($query->execute() as $val) {
- $id = $val[$foreign];
- $name = (string)$val;
-
- $ul->append(Tag::li()
- ->toggleClass('selected', $this->selected == $id)
- ->append($this->link($extraParams, $id, $name)));
- }
-
- return $ul->html();
- }
-}
-
-class IPF_Admin_Model_OwnedFilter extends IPF_Admin_Model_Filter
-{
- private $column, $selected = null;
-
- function __construct($modelName, $column, $title)
- {
- $this->column = $column;
- $this->title = $title;
- $this->paramName = 'filter_'.$this->column;
- }
-
- function setParams($params)
- {
- $this->selected = \PFF\Arr::get($params, $this->paramName);
- }
-
- function applyToQuery($q)
- {
- // check ???
- if ($this->selected)
- $q->addWhere($this->column.' = ?', array($this->selected));
- }
-
- function render($extraParams)
- {
- $ul = Tag::ul();
-
- // reset filter
- $ul->append(Tag::li()
- ->toggleClass('selected', !$this->selected)
- ->append($this->link($extraParams, null, __('All'))));
-
- foreach (User::query()->fetchAll() as $val) {
- $id = $val->id;
- $name = (string)$val;
-
- $ul->append(Tag::li()
- ->toggleClass('selected', $this->selected == $id)
- ->append($this->link($extraParams, $id, $name)));
- }
-
- return $ul->html();
- }
-}
-
-class BooleanFilter extends IPF_Admin_Model_Filter
-{
- private $column, $trueTitle, $falseTitle, $selected;
-
- public function __construct($column, $title, $trueTitle='Yes', $falseTitle='No')
- {
- $this->column = $column;
- $this->title = $title;
- $this->trueTitle = $trueTitle;
- $this->falseTitle = $falseTitle;
-
- $this->paramName = 'filter_'.$column;
- }
-
- function setParams($params)
- {
- $this->selected = \PFF\Arr::get($params, $this->paramName);
- }
-
- function render($extraParams)
- {
- return Tag::ul(null,
- Tag::li()
- ->toggleClass('selected', $this->selected !== 'y' && $this->selected !== 'n')
- ->append($this->link($extraParams, null, __('All'))),
- Tag::li()
- ->toggleClass('selected', $this->selected === 'y')
- ->append($this->link($extraParams, 'y', $this->trueTitle)),
- Tag::li()
- ->toggleClass('selected', $this->selected === 'n')
- ->append($this->link($extraParams, 'n', $this->falseTitle)));
- }
-
- function applyToQuery($q)
- {
- switch ($this->selected) {
- case 'y':
- $query->addWhere($this->column);
- break;
- case 'n':
- $query->addWhere('NOT '.$this->column);
- break;
- }
- }
-}
-
-class DateHierarchyListFilter extends IPF_Admin_Model_Filter
-{
- private $model, $name;
- private $day, $month, $year;
-
- function __construct($title, $modelName, $fieldName)
- {
- $this->title = $title;
- $this->modelName = $modelName;
- $this->name = $fieldName;
-
- $this->paramName = $fieldName;
- }
-
- function setParams($params)
- {
- $date = \PFF\Arr::get($params, $this->paramName, '');
- $matches = array();
- if (preg_match('/(\d{4})-(\d{2})-(\d{2})/', $date, $matches)) {
- $this->year = intval($matches[1]);
- $this->month = intval($matches[2]);
- $this->day = intval($matches[3]);
- }
- }
-
- function render($extraParams)
- {
- $ul = Tag::ul();
-
- $ul->append(Tag::li()
- ->toggleClass('selected', !$this->year)
- ->append($this->link($extraParams, null, __('All'))));
-
- if ($this->year)
- $ul->append($this->renderChoice($extraParams, $this->year, 0, 0, $this->year)->addClass('selected'));
-
- if ($this->month)
- $ul->append($this->renderChoice($extraParams, $this->year, $this->month, 0, $this->monthName($this->month))->addClass('selected'));
-
- if ($this->day)
- $ul->append($this->renderChoice($extraParams, $this->year, $this->month, $this->day, $this->day)->addClass('selected'));
-
- if ($this->day) {
- } elseif ($this->month) {
- $days = $this->choices('DAY(' . $this->name . ')',
- 'YEAR(' . $this->name . ')', $this->year,
- 'MONTH(' . $this->name . ')', $this->month);
- foreach ($days as $day) {
- $ul->append($this->renderChoice($extraParams, $this->year, $this->month, $day, $day));
- }
- } elseif ($this->year) {
- $months = $this->choices('MONTH(' . $this->name . ')',
- 'YEAR(' . $this->name . ')', $this->year);
- foreach ($months as $month) {
- $ul->append($this->renderChoice($extraParams, $this->year, $month, 0, $this->monthName($month)));
- }
- } else {
- $years = $this->choices('YEAR(' . $this->name . ')');
- foreach ($years as $year) {
- $ul->append($this->renderChoice($extraParams, $year, 0, 0, $year));
- }
- }
-
- return $ul->html();
- }
-
- function applyToQuery($q)
- {
- if ($this->day)
- $q->addWhere('DAY(' . $this->name . ') = ?', array($this->day));
- if ($this->month)
- $q->addWhere('MONTH(' . $this->name . ') = ?', array($this->month));
- if ($this->year)
- $q->addWhere('YEAR(' . $this->name . ') = ?', array($this->year));
- }
-
- private function choices(/* $what, [$expr, $value, ...]*/)
- {
- $args = func_get_args();
- $what = array_shift($args);
-
- $q = IPF_ORM_Query::create()
- ->from($this->modelName)
- ->select($what . ' AS value')
- ->groupBy('1')
- ->orderBy('1');
-
- while ($args) {
- $expr = array_shift($args);
- $value = array_shift($args);
-
- $q->addWhere($expr . ' = ?', array($value));
- }
- return \PFF\Arr::create($q->fetchArray())->pluck('value')->arr();
- }
-
- private function renderChoice($extraParams, $year, $month, $day, $label)
- {
- return Tag::li(null,
- $this->link($extraParams, sprintf('%04d-%02d-%02d', $year, $month, $day), $label));
- }
-
- protected function monthName($month)
- {
- return date('F', mktime(0, 0, 0, $month, 1));
- }
-}
-
+++ /dev/null
-<?php
-
-abstract class IPF_Admin_ModelInline
-{
- public $parentModel = null;
- public $orderby = 'id';
-
- function __construct($parentModel)
- {
- $this->parentModel = $parentModel;
- }
-
- abstract function getModelName();
-
- public function getApplication()
- {
- foreach (IPF_Project::getInstance()->appList() as $app)
- foreach (IPF_Legacy_ORM_App::appModelList($app) as $model)
- if ($model == $this->getModelName())
- return $app;
- return null;
- }
-
- function getAddNum()
- {
- return 4;
- }
-
- function getLegend()
- {
- return $this->getModelName();
- }
-
- function getFkName()
- {
- foreach($this->getTable()->getRelations() as $rel) {
- if ($rel->getClass()==get_class($this->parentModel))
- return $rel->getAlias();
- }
- throw new IPF_Exception(__('Cannot get fkName for '.$this->getModelName()));
- }
-
- function getFkLocal()
- {
- foreach($this->getTable()->getRelations() as $rel) {
- if ($rel->getClass() == get_class($this->parentModel))
- return $rel->getLocal();
- }
- throw new IPF_Exception(__('Cannot get fkLocal for '.$this->getModelName()));
- }
-
- function getObjects()
- {
- if (!$this->parentModel->exists())
- return array();
-
- $query = IPF_ORM_Query::create()
- ->from($this->getModelName())
- ->where($this->getFkLocal().'='.$this->parentModel->id);
-
- $o = $this->_orderableColumn();
- if ($o)
- $query->orderby($o);
- else
- $query->orderby($this->orderby);
-
- return $query->execute();
- }
-
- public function _orderable()
- {
- return $this->_orderableColumn() !== null;
- }
-
- public function _orderableColumn()
- {
- if ($this->getTable()->hasTemplate('IPF_ORM_Template_Orderable'))
- return $this->getTable()->getTemplate('IPF_ORM_Template_Orderable')->getColumnName();
- else
- return null;
- }
-
- private function getTable()
- {
- return IPF_ORM::getTable($this->getModelName());
- }
-
- public function createField()
- {
- $exclude = array(
- $this->getFkName(),
- $this->getFkLocal(),
- );
- $o = $this->_orderableColumn();
- if ($o)
- $exclude[] = $o;
-
- $fields = array();
- foreach (IPF_Form_Model::suggestFields($this->getTable(), null, $exclude, null) as $field) {
- list($n, $f) = $field;
- $fields[$n] = $f;
- }
-
- return new IPF_Form_Field_Set(array(
- 'fields' => $fields,
- 'addCount' => $this->getAddNum(),
- ));
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_Legacy_ORM_App extends IPF_Application
-{
- private static $appModels = array();
-
- public static function appModelList($app)
- {
- if (!array_key_exists($app->getName(), self::$appModels)) {
- $models = array();
- try {
- $it = new DirectoryIterator($app->getPath().DIRECTORY_SEPARATOR.'models');
- foreach ($it as $file) {
- $e = explode('.', $file->getFileName(), 2);
- if (count($e) == 2 && $e[1] === 'php') {
- $models[] = $e[0];
- }
- }
- } catch (RuntimeException $e) {
- // nothing to do
- }
-
- self::$appModels[$app->getName()] = $models;
- }
- return self::$appModels[$app->getName()];
- }
-
- public function commands()
- {
- return array(
- new IPF_Legacy_ORM_Command_BuildModels,
- new IPF_Legacy_ORM_Command_Sql,
- new IPF_Legacy_ORM_Command_SyncDB,
- new IPF_Legacy_ORM_Command_Fixtures,
- );
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_Legacy_ORM_Command_BuildModels
-{
- public $command = 'buildmodels';
- public $description = 'Build all model classes';
-
- public function run($args=null)
- {
- print "Build all model classes\n\n";
-
- $project = IPF_Project::getInstance();
-
- $paths = array();
- foreach ($project->appList() as $app)
- $paths[] = $app->getPath();
-
- IPF_ORM::generateModelsFromYaml($paths, array());
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_Legacy_ORM_Command_Fixtures
-{
- public $command = 'fixtures';
- public $description = 'Load fixtures into database';
-
- public function run($args=null)
- {
- print "Load project fixtures to database\n";
-
- $project = IPF_Project::getInstance();
-
- $paths = array(IPF::get('project_path'));
- foreach ($project->appList() as $app)
- $paths[] = $app->path;
-
- $fixtures = array();
- foreach ($paths as $path) {
- $path .= DIRECTORY_SEPARATOR.'fixtures.php';
- if (is_file($path))
- $fixtures = array_merge($fixtures, require $path);
- }
-
- if (!count($fixtures)) {
- echo "No fixtures found\n";
- return;
- }
-
- foreach ($fixtures as $fixture) {
- $modelClass = $fixture['model'];
- $key = $fixture['key'];
- if (!is_array($key))
- $key = array($key);
- $records = $fixture['records'];
- echo "Loading $modelClass ";
- $table = IPF_ORM::getTable($modelClass);
- $table->getConnection()->beginTransaction();
-
- $query = $table
- ->createQuery()
- ->limit(1);
- foreach ($key as $k)
- $query->addWhere($k . ' = ?');
-
- foreach ($records as $record) {
- $params = array();
- foreach ($key as $k)
- $params[] = $record[$k];
-
- $model = $query->execute($params);
- if ($model)
- $model = $model[0];
- else
- $model = new $modelClass;
-
- foreach ($record as $k => $v)
- $model->$k = $v;
-
- $model->save();
- echo '.';
- }
- $table->getConnection()->commit();
- echo "\n";
- }
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_Legacy_ORM_Command_Sql
-{
- public $command = 'sql';
- public $description = 'Show all SQL DDL from model classes';
-
- public function run($args=null)
- {
- print "Show all SQL DDL from model classes\n";
-
- foreach (IPF_Project::getInstance()->appList() as $app)
- print IPF_ORM::generateSqlFromModels($app)."\n";
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_Legacy_ORM_Command_SyncDB
-{
- public $command = 'syncdb';
- public $description = 'Create tables from model classes';
-
- public function run($args=null)
- {
- print "Create tables from model classes\n";
-
- $project = IPF_Project::getInstance();
-
- $models = array();
- foreach ($project->appList() as $app)
- $models = array_merge($models, IPF_Legacy_ORM_App::appModelList($app));
-
- IPF_ORM::createTablesFromModels($models);
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_Form_Model extends IPF_Form
-{
- public $model = null;
- public $user_fields = null;
-
- function initFields($extra=array())
- {
- if (isset($extra['model']))
- $this->model = $extra['model'];
- else
- throw new IPF_Exception_Form(__('Unknown model for form'));
-
- if (isset($extra['user_fields']))
- $this->user_fields = $extra['user_fields'];
-
- $exclude = \PFF\Arr::get($extra, 'exclude', array());
- $fields = self::suggestFields($this->model->getTable(), $this->fields(), $exclude, $this);
- foreach ($fields as $field) {
- list($n, $f) = $field;
- $this->fields[$n] = $f;
- }
- }
-
- public static function suggestFields($table, $fields=null, $exclude=array(), $form=null)
- {
- $result = array();
-
- $db_columns = $table->getColumns();
- $db_relations = $table->getRelations();
-
- if ($fields === null) {
- foreach ($db_columns as $name => $col) {
- if (array_search($name, $exclude) !== false)
- continue;
- if (isset($col['exclude']) && $col['exclude'])
- continue;
-
- if ($form && method_exists($form, 'add__'.$name.'__field')) {
- $f = call_user_func(array($form, 'add__'.$name.'__field'));
- if ($f)
- $field = array($name, $f);
- } else {
- $field = self::createDBField($name, $table, $col);
- }
-
- if ($field)
- $result[] = $field;
- }
-
- foreach ($db_relations as $name => $relation) {
- if (array_search($name, $exclude) !== false)
- continue;
- if (isset($relation['exclude']) && $relation['exclude'])
- continue;
-
- if ($form && method_exists($form, 'add__'.$name.'__field')) {
- $f = call_user_func(array($form, 'add__'.$name.'__field'));
- if ($f)
- $field = array($name, $f);
- } else {
- $field = self::createDBRelation($name, $relation, $db_columns);
- }
-
- if ($field)
- $result[] = $field;
- }
- } else {
- foreach ($fields as $uname) {
- $field = null;
- if ($form && method_exists($form, 'add__'.$uname.'__field')) {
- $f = call_user_func(array($form, 'add__'.$uname.'__field'));
- if ($f)
- $field = array($uname, $f);
- } elseif (array_key_exists($uname, $db_columns)) {
- $field = self::createDBField($uname, $table, $db_columns[$uname]);
- } elseif (array_key_exists($uname, $db_relations)) {
- $field = self::createDBRelation($uname, $db_relations[$uname], $db_columns);
- } else {
- throw new IPF_Exception_Form(sprintf(__("Model '%s' has no column '%s'."), $table->getComponentName(), $uname));
- }
- if ($field)
- $result[] = $field;
- }
- }
- return $result;
- }
-
- public static function createDBField($name, $table, $col)
- {
- if ($name == $table->getIdentifier())
- return null;
-
- $required = isset($col['notblank']) && $col['notblank'];
- $label = isset($col['verbose']) ? $col['verbose'] : IPF_Utils::humanTitle($name);
-
- $params = array(
- 'required' => $required,
- 'label' => $label,
- 'help_text' => '',
- );
-
- switch ($col['type']) {
- case 'string':
- if (isset($col['length']))
- $params['max_length'] = (int)($col['length']);
-
- if (isset($col['uploadTo']))
- $params['uploadTo'] = $col['uploadTo'];
-
- if (isset($col['email']) && $col['email']) { $form_field = new IPF_Form_Field_Email($params); }
- elseif (isset($col['file']) && $col['file']) { $form_field = new IPF_Form_Field_File($params); }
- elseif (isset($col['image']) && $col['image']) { $form_field = new IPF_Form_Field_Image($params); }
- elseif (isset($col['html']) && $col['html']) { $form_field = new IPF_Form_Field_Html($params); }
- else { $form_field = new IPF_Form_Field_Varchar($params); }
-
- break;
- case 'boolean':
- $form_field = new IPF_Form_Field_Boolean($params);
- break;
- case 'integer':
- $params['widget_attrs'] = array('style' => 'width:140px;');
- $form_field = new IPF_Form_Field_Integer($params);
- break;
- case 'double':
- case 'decimal':
- $params['widget_attrs'] = array('style' => 'width:140px;');
- $form_field = new IPF_Form_Field_Float($params);
- break;
- case 'date':
- $format = IPF::get('date_format');
- if ($format)
- $params['widget_attrs'] = array('format' => $format);
- $form_field = new IPF_Form_Field_Date($params);
- break;
- case 'datetime':
- case 'timestamp':
- $format = IPF::get('datetime_format');
- if ($format)
- $params['widget_attrs'] = array('format' => $format);
- $form_field = new IPF_Form_Field_Datetime($params);
- break;
- default:
- throw new IPF_Exception_Form(__('Unsupported column type \''.$col['type'].'\'.'));
- }
-
- if ($form_field !== null)
- return array($name, $form_field);
- else
- return null;
- }
-
- public static function createDBRelation($name, $relation, $db_columns)
- {
- $rt = $relation->getType();
- if ($rt !== IPF_ORM_Relation::ONE_AGGREGATE && $rt !== IPF_ORM_Relation::MANY_AGGREGATE)
- return null;
-
- $lfn = $relation->getLocalFieldName();
- if (isset($db_columns[$lfn]))
- $col = $db_columns[$lfn];
- else
- $col = array();
-
- $table = IPF_ORM::getTable($relation->getClass());
-
- $params = array(
- 'required' => isset($col['notblank']),
- 'label' => isset($col['verbose']) ? $col['verbose'] : IPF_Utils::humanTitle($name),
- 'help_text' => '',
- 'table' => $table,
- 'model' => $relation->getClass(),
- );
-
- if ($rt === IPF_ORM_Relation::ONE_AGGREGATE) {
- $choices = array('--------' => '');
- $pk = $table->getIdentifier();
- foreach ($table->findAll() as $o) {
- $choices[$o->__toString()] = $o->$pk;
- }
-
- $params['choices'] = $choices;
- return array($name.'_id', new IPF_Form_Field_Choice($params));
- } elseif ($rt === IPF_ORM_Relation::MANY_AGGREGATE) {
- $choices = array();
- $pk = $table->getIdentifier();
- foreach ($table->findAll() as $o) {
- $choices[$o->__toString()] = $o->$pk;
- }
-
- $params['choices'] = $choices;
- return array($name, new IPF_Form_Field_MultipleChoice($params));
- } else {
- return null;
- }
- }
-
- function fields()
- {
- return $this->user_fields;
- }
-
- function save($commit=true)
- {
- if (!$this->isValid())
- throw new IPF_Exception_Form(__('Cannot save the model from an invalid form.'));
-
- $this->model->SetFromFormData($this->cleaned_data);
- $this->model->save();
- $rels = $this->model->getTable()->getRelations();
- foreach ($rels as $rname => $rel) {
- if (isset($this->cleaned_data[$rname])) {
- $this->model->unlink($rel->getAlias());
- if (is_array($this->cleaned_data[$rname])) {
- $this->model->link($rel->getAlias(),$this->cleaned_data[$rname]);
- }
- }
- }
- return $this->model;
- }
-}
-
+++ /dev/null
-<?php
-
-final class IPF_ORM
-{
- const ERR = -1;
- const ERR_SYNTAX = -2;
- const ERR_CONSTRAINT = -3;
- const ERR_NOT_FOUND = -4;
- const ERR_ALREADY_EXISTS = -5;
- const ERR_UNSUPPORTED = -6;
- const ERR_MISMATCH = -7;
- const ERR_INVALID = -8;
- const ERR_NOT_CAPABLE = -9;
- const ERR_TRUNCATED = -10;
- const ERR_INVALID_NUMBER = -11;
- const ERR_INVALID_DATE = -12;
- const ERR_DIVZERO = -13;
- const ERR_NODBSELECTED = -14;
- const ERR_CANNOT_CREATE = -15;
- const ERR_CANNOT_DELETE = -16;
- const ERR_CANNOT_DROP = -17;
- const ERR_NOSUCHTABLE = -18;
- const ERR_NOSUCHFIELD = -19;
- const ERR_NEED_MORE_DATA = -20;
- const ERR_NOT_LOCKED = -21;
- const ERR_VALUE_COUNT_ON_ROW = -22;
- const ERR_INVALID_DSN = -23;
- const ERR_CONNECT_FAILED = -24;
- const ERR_EXTENSION_NOT_FOUND = -25;
- const ERR_NOSUCHDB = -26;
- const ERR_ACCESS_VIOLATION = -27;
- const ERR_CANNOT_REPLACE = -28;
- const ERR_CONSTRAINT_NOT_NULL = -29;
- const ERR_DEADLOCK = -30;
- const ERR_CANNOT_ALTER = -31;
- const ERR_MANAGER = -32;
- const ERR_MANAGER_PARSE = -33;
- const ERR_LOADMODULE = -34;
- const ERR_INSUFFICIENT_DATA = -35;
- const ERR_CLASS_NAME = -36;
-
- const CASE_LOWER = 2;
- const CASE_NATURAL = 0;
- const CASE_UPPER = 1;
- const CURSOR_FWDONLY = 0;
- const CURSOR_SCROLL = 1;
- const ERRMODE_EXCEPTION = 2;
- const ERRMODE_SILENT = 0;
- const ERRMODE_WARNING = 1;
- const FETCH_ASSOC = 2;
- const FETCH_BOTH = 4;
- const FETCH_BOUND = 6;
- const FETCH_CLASS = 8;
- const FETCH_CLASSTYPE = 262144;
- const FETCH_COLUMN = 7;
- const FETCH_FUNC = 10;
- const FETCH_GROUP = 65536;
- const FETCH_INTO = 9;
- const FETCH_LAZY = 1;
- const FETCH_NAMED = 11;
- const FETCH_NUM = 3;
- const FETCH_OBJ = 5;
- const FETCH_ORI_ABS = 4;
- const FETCH_ORI_FIRST = 2;
- const FETCH_ORI_LAST = 3;
- const FETCH_ORI_NEXT = 0;
- const FETCH_ORI_PRIOR = 1;
- const FETCH_ORI_REL = 5;
- const FETCH_SERIALIZE = 524288;
- const FETCH_UNIQUE = 196608;
- const NULL_EMPTY_STRING = 1;
- const NULL_NATURAL = 0;
- const NULL_TO_STRING = NULL;
- const PARAM_BOOL = 5;
- const PARAM_INPUT_OUTPUT = -2147483648;
- const PARAM_INT = 1;
- const PARAM_LOB = 3;
- const PARAM_NULL = 0;
- const PARAM_STMT = 4;
- const PARAM_STR = 2;
-
- const ATTR_AUTOCOMMIT = 0;
- const ATTR_PREFETCH = 1;
- const ATTR_TIMEOUT = 2;
- const ATTR_ERRMODE = 3;
- const ATTR_SERVER_VERSION = 4;
- const ATTR_CLIENT_VERSION = 5;
- const ATTR_SERVER_INFO = 6;
- const ATTR_CONNECTION_STATUS = 7;
- const ATTR_CASE = 8;
- const ATTR_CURSOR_NAME = 9;
- const ATTR_CURSOR = 10;
- const ATTR_ORACLE_NULLS = 11;
- const ATTR_PERSISTENT = 12;
- const ATTR_STATEMENT_CLASS = 13;
- const ATTR_FETCH_TABLE_NAMES = 14;
- const ATTR_FETCH_CATALOG_NAMES = 15;
- const ATTR_STRINGIFY_FETCHES = 17;
- const ATTR_MAX_COLUMN_LEN = 18;
-
- const ATTR_FIELD_CASE = 102;
- const ATTR_CMPNAME_FORMAT = 118;
- const ATTR_DBNAME_FORMAT = 117;
- const ATTR_TBLCLASS_FORMAT = 119;
- const ATTR_EXPORT = 140;
- const ATTR_DECIMAL_PLACES = 141;
-
- const ATTR_PORTABILITY = 106;
- const ATTR_COLL_KEY = 108;
- const ATTR_DEFAULT_TABLE_TYPE = 112;
- const ATTR_DEF_TEXT_LENGTH = 113;
- const ATTR_DEF_VARCHAR_LENGTH = 114;
- const ATTR_DEF_TABLESPACE = 115;
- const ATTR_EMULATE_DATABASE = 116;
- const ATTR_USE_NATIVE_ENUM = 117;
-
- //const ATTR_FETCHMODE = 118;
- const ATTR_NAME_PREFIX = 121;
- const ATTR_CREATE_TABLES = 122;
- const ATTR_COLL_LIMIT = 123;
-
- const ATTR_CACHE = 150;
- const ATTR_RESULT_CACHE = 150;
- const ATTR_CACHE_LIFESPAN = 151;
- const ATTR_RESULT_CACHE_LIFESPAN = 151;
- const ATTR_LOAD_REFERENCES = 153;
- const ATTR_DEFAULT_PARAM_NAMESPACE = 156;
- const ATTR_QUERY_CACHE = 157;
- const ATTR_QUERY_CACHE_LIFESPAN = 158;
- const ATTR_MODEL_LOADING = 161;
- const ATTR_RECURSIVE_MERGE_FIXTURES = 162;
- const ATTR_SINGULARIZE_IMPORT = 163;
-
- const FETCH_IMMEDIATE = 0;
- const FETCH_BATCH = 1;
- const FETCH_OFFSET = 3;
- const FETCH_LAZY_OFFSET = 4;
- const FETCH_VHOLDER = 1;
- const FETCH_RECORD = 2;
- const FETCH_ARRAY = 3;
-
- const PORTABILITY_NONE = 0;
- const PORTABILITY_FIX_CASE = 1;
- const PORTABILITY_RTRIM = 2;
- const PORTABILITY_DELETE_COUNT = 4;
- const PORTABILITY_EMPTY_TO_NULL = 8;
- const PORTABILITY_FIX_ASSOC_FIELD_NAMES = 16;
- const PORTABILITY_EXPR = 32;
- const PORTABILITY_ALL = 63;
-
- const LOCK_OPTIMISTIC = 0;
- const LOCK_PESSIMISTIC = 1;
-
- const EXPORT_NONE = 0;
- const EXPORT_TABLES = 1;
- const EXPORT_CONSTRAINTS = 2;
- const EXPORT_ALL = 7;
-
- const HYDRATE_RECORD = 2;
- const HYDRATE_ARRAY = 3;
-
- const HYDRATE_NONE = 4;
- const IDENTIFIER_AUTOINC = 1;
- const IDENTIFIER_NATURAL = 3;
- const IDENTIFIER_COMPOSITE = 4;
- const MODEL_LOADING_AGGRESSIVE = 1;
- const MODEL_LOADING_CONSERVATIVE= 2;
-
- const BASE_CLASSES_DIRECTORY = '_generated';
-
- private function __construct() {}
-
- public static function getTable($componentName)
- {
- return IPF_ORM_Manager::connection()->getTable($componentName);
- }
-
- public static function generateModelsFromYaml($directory, $extraAllwedReferences=array())
- {
- $files = array();
- foreach ((array)$directory as $dir)
- $files[] = $dir . '/models.yml';
-
- // load schema
- $import = new IPF_ORM_Import_Schema;
- $definitions = $import->importSchema($files, $extraAllwedReferences);
-
- // build
- $models = array();
- foreach ($definitions as $name => $definition) {
- print " $name\n";
- $targetPath = substr($definition['input_file'], 0, -4);
-
- $builder = new IPF_ORM_Import_Builder;
- $builder->buildRecord($definition, $targetPath);
-
- $models[] = $name;
- }
-
- return $models;
- }
-
- public static function createTablesFromModels($models)
- {
- IPF_ORM_Manager::connection()->export->exportClasses($models);
- }
-
- public static function generateSqlFromModels($app)
- {
- $sql = IPF_ORM_Manager::connection()->export->exportSortedClassesSql(IPF_Legacy_ORM_App::appModelList($app), false);
-
- $build = '';
- foreach ($sql as $query) {
- $build .= $query.";\n\n";
- }
- return $build;
- }
-
- public static function dump($var, $output = true, $indent = "")
- {
- $ret = array();
- switch (gettype($var)) {
- case 'array':
- $ret[] = 'Array(';
- $indent .= " ";
- foreach ($var as $k => $v) {
-
- $ret[] = $indent . $k . ' : ' . self::dump($v, false, $indent);
- }
- $indent = substr($indent,0, -4);
- $ret[] = $indent . ")";
- break;
- case 'object':
- $ret[] = 'Object(' . get_class($var) . ')';
- break;
- default:
- $ret[] = var_export($var, true);
- }
-
- if ($output) {
- print implode("\n", $ret);
- }
-
- return implode("\n", $ret);
- }
-
- public static function GetObjectOr404($object, $id)
- {
- $obj = IPF_ORM::getTable($object)->findOneById($id);
- if ($obj)
- return $obj;
- throw new IPF_HTTP_Error404();
- }
-}
-
+++ /dev/null
-<?php
-
-abstract class IPF_ORM_Access implements ArrayAccess
-{
- public function setArray(array $array)
- {
- foreach ($array as $k => $v) {
- $this->set($k, $v);
- }
-
- return $this;
- }
-
- public function __set($name, $value)
- {
- $this->set($name, $value);
- }
-
- public function __get($name)
- {
- return $this->get($name);
- }
-
- public function __isset($name)
- {
- return $this->contains($name);
- }
-
- public function __unset($name)
- {
- return $this->remove($name);
- }
-
- public function offsetExists($offset)
- {
- return $this->contains($offset);
- }
-
- public function offsetGet($offset)
- {
- return $this->get($offset);
- }
-
- public function offsetSet($offset, $value)
- {
- if ( ! isset($offset)) {
- $this->add($value);
- } else {
- $this->set($offset, $value);
- }
- }
-
- public function offsetUnset($offset)
- {
- return $this->remove($offset);
- }
-
- public function remove($offset)
- {
- throw new IPF_ORM_Exception('Remove is not supported for ' . get_class($this));
- }
-
- public function get($offset)
- {
- throw new IPF_ORM_Exception('Get is not supported for ' . get_class($this));
- }
-
- public function set($offset, $value)
- {
- throw new IPF_ORM_Exception('Set is not supported for ' . get_class($this));
- }
-
- public function contains($offset)
- {
- throw new IPF_ORM_Exception('Contains is not supported for ' . get_class($this));
- }
-
- public function add($value)
- {
- throw new IPF_ORM_Exception('Add is not supported for ' . get_class($this));
- }
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_Adapter
-{
- const ATTR_AUTOCOMMIT = 0;
- const ATTR_CASE = 8;
- const ATTR_CLIENT_VERSION = 5;
- const ATTR_CONNECTION_STATUS = 7;
- const ATTR_CURSOR = 10;
- const ATTR_CURSOR_NAME = 9;
- const ATTR_ERRMODE = 3;
- const ATTR_FETCH_CATALOG_NAMES = 15;
- const ATTR_FETCH_TABLE_NAMES = 14;
- const ATTR_MAX_COLUMN_LEN = 18;
- const ATTR_ORACLE_NULLS = 11;
- const ATTR_PERSISTENT = 12;
- const ATTR_PREFETCH = 1;
- const ATTR_SERVER_INFO = 6;
- const ATTR_SERVER_VERSION = 4;
- const ATTR_STATEMENT_CLASS = 13;
- const ATTR_STRINGIFY_FETCHES = 17;
- const ATTR_TIMEOUT = 2;
- const CASE_LOWER = 2;
- const CASE_NATURAL = 0;
- const CASE_UPPER = 1;
- const CURSOR_FWDONLY = 0;
- const CURSOR_SCROLL = 1;
- const ERR_ALREADY_EXISTS = NULL;
- const ERR_CANT_MAP = NULL;
- const ERR_CONSTRAINT = NULL;
- const ERR_DISCONNECTED = NULL;
- const ERR_MISMATCH = NULL;
- const ERR_NO_PERM = NULL;
- const ERR_NONE = '00000';
- const ERR_NOT_FOUND = NULL;
- const ERR_NOT_IMPLEMENTED = NULL;
- const ERR_SYNTAX = NULL;
- const ERR_TRUNCATED = NULL;
- const ERRMODE_EXCEPTION = 2;
- const ERRMODE_SILENT = 0;
- const ERRMODE_WARNING = 1;
- const FETCH_ASSOC = 2;
- const FETCH_BOTH = 4;
- const FETCH_BOUND = 6;
- const FETCH_CLASS = 8;
- const FETCH_CLASSTYPE = 262144;
- const FETCH_COLUMN = 7;
- const FETCH_FUNC = 10;
- const FETCH_GROUP = 65536;
- const FETCH_INTO = 9;
- const FETCH_LAZY = 1;
- const FETCH_NAMED = 11;
- const FETCH_NUM = 3;
- const FETCH_OBJ = 5;
- const FETCH_ORI_ABS = 4;
- const FETCH_ORI_FIRST = 2;
- const FETCH_ORI_LAST = 3;
- const FETCH_ORI_NEXT = 0;
- const FETCH_ORI_PRIOR = 1;
- const FETCH_ORI_REL = 5;
- const FETCH_SERIALIZE = 524288;
- const FETCH_UNIQUE = 196608;
- const NULL_EMPTY_STRING = 1;
- const NULL_NATURAL = 0;
- const NULL_TO_STRING = NULL;
- const PARAM_BOOL = 5;
- const PARAM_INPUT_OUTPUT = -2147483648;
- const PARAM_INT = 1;
- const PARAM_LOB = 3;
- const PARAM_NULL = 0;
- const PARAM_STMT = 4;
- const PARAM_STR = 2;
-}
+++ /dev/null
-<?php
-
-interface IPF_ORM_Cache_Interface
-{
- public function fetch($id, $testCacheValidity = true);
- public function contains($id);
- public function save($id, $data, $lifeTime = false);
- public function delete($id);
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_Collection extends IPF_ORM_Access implements Countable, IteratorAggregate, Serializable
-{
- protected $data = array();
- protected $_table;
- protected $_snapshot = array();
- protected $reference;
- protected $referenceField;
- protected $relation;
- protected $keyColumn;
-
- public function __construct($table, $keyColumn = null)
- {
- if ( ! ($table instanceof IPF_ORM_Table)) {
- $table = IPF_ORM::getTable($table);
- }
-
- $this->_table = $table;
-
- if ($keyColumn === null) {
- $keyColumn = $table->getBoundQueryPart('indexBy');
- }
-
- if ($keyColumn === null) {
- $keyColumn = $table->getAttribute(IPF_ORM::ATTR_COLL_KEY);
- }
-
- if ($keyColumn !== null) {
- $this->keyColumn = $keyColumn;
- }
- }
-
- public function getTable()
- {
- return $this->_table;
- }
-
- public function setData(array $data)
- {
- $this->data = $data;
- }
-
- public function serialize()
- {
- $vars = get_object_vars($this);
-
- unset($vars['reference']);
- unset($vars['reference_field']);
- unset($vars['relation']);
- unset($vars['expandable']);
- unset($vars['expanded']);
- unset($vars['generator']);
-
- $vars['_table'] = $vars['_table']->getComponentName();
-
- return serialize($vars);
- }
-
- public function unserialize($serialized)
- {
- $connection = IPF_ORM_Manager::connection();
-
- $array = unserialize($serialized);
-
- foreach ($array as $name => $values) {
- $this->$name = $values;
- }
-
- $this->_table = $connection->getTable($this->_table);
-
- $keyColumn = isset($array['keyColumn']) ? $array['keyColumn'] : null;
- if ($keyColumn === null) {
- $keyColumn = $this->_table->getBoundQueryPart('indexBy');
- }
-
- if ($keyColumn !== null) {
- $this->keyColumn = $keyColumn;
- }
- }
-
- public function setKeyColumn($column)
- {
- $this->keyColumn = $column;
-
- return $this;
- }
-
- public function getKeyColumn()
- {
- return $this->keyColumn;
- }
-
- public function getData()
- {
- return $this->data;
- }
-
- public function getFirst()
- {
- return reset($this->data);
- }
-
- public function getLast()
- {
- return end($this->data);
- }
-
- public function end()
- {
- return end($this->data);
- }
-
- public function key()
- {
- return key($this->data);
- }
-
- public function setReference(IPF_ORM_Record $record, IPF_ORM_Relation $relation)
- {
- $this->reference = $record;
- $this->relation = $relation;
-
- if ($relation instanceof IPF_ORM_Relation_ForeignKey ||
- $relation instanceof IPF_ORM_Relation_LocalKey) {
-
- $this->referenceField = $relation->getForeignFieldName();
-
- $value = $record->get($relation->getLocalFieldName());
-
- foreach ($this->data as $record) {
- if ($value !== null) {
- $record->set($this->referenceField, $value, false);
- } else {
- $record->set($this->referenceField, $this->reference, false);
- }
- }
- } elseif ($relation instanceof IPF_ORM_Relation_Association) {
-
- }
- }
-
- public function getReference()
- {
- return $this->reference;
- }
-
- public function remove($key)
- {
- $removed = $this->data[$key];
-
- unset($this->data[$key]);
- return $removed;
- }
-
- public function contains($key)
- {
- return isset($this->data[$key]);
- }
-
- public function search(IPF_ORM_Record $record)
- {
- return array_search($record, $this->data, true);
- }
-
- public function get($key)
- {
- if ( ! isset($this->data[$key])) {
- $record = $this->_table->create();
-
- if (isset($this->referenceField)) {
- $value = $this->reference->get($this->relation->getLocalFieldName());
-
- if ($value !== null) {
- $record->set($this->referenceField, $value, false);
- } else {
- $record->set($this->referenceField, $this->reference, false);
- }
- }
- if ($key === null) {
- $this->data[] = $record;
- } else {
- $this->data[$key] = $record;
- }
-
- if (isset($this->keyColumn)) {
- $record->set($this->keyColumn, $key);
- }
-
- return $record;
- }
-
- return $this->data[$key];
- }
-
- public function getPrimaryKeys()
- {
- $list = array();
- $name = $this->_table->getIdentifier();
-
- foreach ($this->data as $record) {
- if (is_array($record) && isset($record[$name])) {
- $list[] = $record[$name];
- } else {
- $list[] = $record->getIncremented();
- }
- }
- return $list;
- }
-
- public function getKeys()
- {
- return array_keys($this->data);
- }
-
- public function count()
- {
- return count($this->data);
- }
-
- public function set($key, $record)
- {
- if (isset($this->referenceField)) {
- $record->set($this->referenceField, $this->reference, false);
- }
-
- $this->data[$key] = $record;
- }
-
- public function add($record, $key = null)
- {
- if (isset($this->referenceField)) {
- $value = $this->reference->get($this->relation->getLocalFieldName());
-
- if ($value !== null) {
- $record->set($this->referenceField, $value, false);
- } else {
- $record->set($this->referenceField, $this->reference, false);
- }
- }
- /**
- * for some weird reason in_array cannot be used here (php bug ?)
- *
- * if used it results in fatal error : [ nesting level too deep ]
- */
- foreach ($this->data as $val) {
- if ($val === $record) {
- return false;
- }
- }
-
- if (isset($key)) {
- if (isset($this->data[$key])) {
- return false;
- }
- $this->data[$key] = $record;
- return true;
- }
-
- if (isset($this->keyColumn)) {
- $value = $record->get($this->keyColumn);
- if ($value === null) {
- throw new IPF_ORM_Exception("Couldn't create collection index. Record field '".$this->keyColumn."' was null.");
- }
- $this->data[$value] = $record;
- } else {
- $this->data[] = $record;
- }
-
- return true;
- }
-
- public function merge(IPF_ORM_Collection $coll)
- {
- $localBase = $this->getTable()->getComponentName();
- $otherBase = $coll->getTable()->getComponentName();
-
- if ($otherBase != $localBase && !is_subclass_of($otherBase, $localBase) ) {
- throw new IPF_ORM_Exception("Can't merge collections with incompatible record types");
- }
-
- foreach ($coll->getData() as $record) {
- $this->add($record);
- }
-
- return $this;
- }
-
- public function loadRelated($name = null)
- {
- $list = array();
- $query = new IPF_ORM_Query($this->_table->getConnection());
-
- if ( ! isset($name)) {
- foreach ($this->data as $record) {
- $value = $record->getIncremented();
- if ($value !== null) {
- $list[] = $value;
- }
- }
- $query->from($this->_table->getComponentName());
- $query->where($this->_table->getComponentName() . '.id IN (' . substr(str_repeat("?, ", count($list)),0,-2) . ')');
-
- return $query;
- }
-
- $rel = $this->_table->getRelation($name);
-
- if ($rel instanceof IPF_ORM_Relation_LocalKey || $rel instanceof IPF_ORM_Relation_ForeignKey) {
- foreach ($this->data as $record) {
- $list[] = $record[$rel->getLocal()];
- }
- } else {
- foreach ($this->data as $record) {
- $value = $record->getIncremented();
- if ($value !== null) {
- $list[] = $value;
- }
- }
- }
-
- $dql = $rel->getRelationDql(count($list), 'collection');
-
- $coll = $query->query($dql, $list);
-
- $this->populateRelated($name, $coll);
- }
-
- public function populateRelated($name, IPF_ORM_Collection $coll)
- {
- $rel = $this->_table->getRelation($name);
- $table = $rel->getTable();
- $foreign = $rel->getForeign();
- $local = $rel->getLocal();
-
- if ($rel instanceof IPF_ORM_Relation_LocalKey) {
- foreach ($this->data as $key => $record) {
- foreach ($coll as $k => $related) {
- if ($related[$foreign] == $record[$local]) {
- $this->data[$key]->setRelated($name, $related);
- }
- }
- }
- } elseif ($rel instanceof IPF_ORM_Relation_ForeignKey) {
- foreach ($this->data as $key => $record) {
- if ( ! $record->exists()) {
- continue;
- }
- $sub = new IPF_ORM_Collection($table);
-
- foreach ($coll as $k => $related) {
- if ($related[$foreign] == $record[$local]) {
- $sub->add($related);
- $coll->remove($k);
- }
- }
-
- $this->data[$key]->setRelated($name, $sub);
- }
- } elseif ($rel instanceof IPF_ORM_Relation_Association) {
- $identifier = $this->_table->getIdentifier();
- $asf = $rel->getAssociationFactory();
- $name = $table->getComponentName();
-
- foreach ($this->data as $key => $record) {
- if ( ! $record->exists()) {
- continue;
- }
- $sub = new IPF_ORM_Collection($table);
- foreach ($coll as $k => $related) {
- if ($related->get($local) == $record[$identifier]) {
- $sub->add($related->get($name));
- }
- }
- $this->data[$key]->setRelated($name, $sub);
-
- }
- }
- }
-
- public function getNormalIterator()
- {
- return new IPF_ORM_Collection_Iterator_Normal($this);
- }
-
- public function takeSnapshot()
- {
- $this->_snapshot = $this->data;
-
- return $this;
- }
-
- public function getSnapshot()
- {
- return $this->_snapshot;
- }
-
- public function processDiff()
- {
- foreach (array_udiff($this->_snapshot, $this->data, array($this, "compareRecords")) as $record) {
- $record->delete();
- }
-
- return $this;
- }
-
- public function toArray($deep = false, $prefixKey = false)
- {
- $data = array();
- foreach ($this as $key => $record) {
-
- $key = $prefixKey ? get_class($record) . '_' .$key:$key;
-
- $data[$key] = $record->toArray($deep, $prefixKey);
- }
-
- return $data;
- }
-
- public function fromArray($array, $deep = true)
- {
- $data = array();
- foreach ($array as $rowKey => $row) {
- $this[$rowKey]->fromArray($row, $deep);
- }
- }
-
- public function synchronizeWithArray(array $array)
- {
- foreach ($this as $key => $record) {
- if (isset($array[$key])) {
- $record->synchronizeWithArray($array[$key]);
- unset($array[$key]);
- } else {
- // remove records that don't exist in the array
- $this->remove($key);
- }
- }
- // create new records for each new row in the array
- foreach ($array as $rowKey => $row) {
- $this[$rowKey]->fromArray($row);
- }
- }
- public function synchronizeFromArray(array $array)
- {
- return $this->synchronizeWithArray($array);
- }
-
- public function getDeleteDiff()
- {
- return array_udiff($this->_snapshot, $this->data, array($this, 'compareRecords'));
- }
-
- public function getInsertDiff()
- {
- return array_udiff($this->data, $this->_snapshot, array($this, "compareRecords"));
- }
-
- protected function compareRecords($a, $b)
- {
- if ($a->getOid() == $b->getOid()) {
- return 0;
- }
-
- return ($a->getOid() > $b->getOid()) ? 1 : -1;
- }
-
- public function save(IPF_ORM_Connection $conn = null)
- {
- if ($conn == null) {
- $conn = $this->_table->getConnection();
- }
-
- $conn->beginInternalTransaction();
-
- $conn->transaction->addCollection($this);
-
- $this->processDiff();
-
- foreach ($this->getData() as $key => $record) {
- $record->save($conn);
- }
-
- $conn->commit();
-
- return $this;
- }
-
- public function delete(IPF_ORM_Connection $conn = null, $clearColl = true)
- {
- if ($conn == null) {
- $conn = $this->_table->getConnection();
- }
-
- $conn->beginInternalTransaction();
- $conn->transaction->addCollection($this);
-
- foreach ($this as $key => $record) {
- $record->delete($conn);
- }
-
- $conn->commit();
-
- if ($clearColl) {
- $this->clear();
- }
-
- return $this;
- }
-
- public function clear()
- {
- $this->data = array();
- }
-
- public function free($deep = false)
- {
- foreach ($this->getData() as $key => $record) {
- if ( ! ($record instanceof IPF_ORM_Null)) {
- $record->free($deep);
- }
- }
-
- $this->data = array();
-
- if ($this->reference) {
- $this->reference->free($deep);
- $this->reference = null;
- }
- }
-
- public function getIterator()
- {
- $data = $this->data;
- return new ArrayIterator($data);
- }
-
- public function __toString()
- {
- return IPF_ORM_Utils::getCollectionAsString($this);
- }
-
- public function getRelation()
- {
- return $this->relation;
- }
-
- public function __debugInfo()
- {
- $r = array();
- foreach ($this->data as $item)
- $r[] = $item;
- return $r;
- }
-}
-
+++ /dev/null
-<?php
-
-abstract class IPF_ORM_Configurable
-{
- protected $attributes = array();
- protected $parent;
- protected $_params = array();
-
- public function getAttributeFromString($stringAttributeName)
- {
- if (is_string($stringAttributeName)) {
- $upper = strtoupper($stringAttributeName);
- $const = 'IPF_ORM::ATTR_' . $upper;
- if (defined($const)) {
- return constant($const);
- } else {
- throw new IPF_ORM_Exception('Unknown attribute: "' . $stringAttributeName . '"');
- }
- } else {
- return false;
- }
- }
-
- public function getAttributeValueFromString($stringAttributeName, $stringAttributeValueName)
- {
- $const = 'IPF_ORM::' . strtoupper($stringAttributeName) . '_' . strtoupper($stringAttributeValueName);
- if (defined($const)) {
- return constant($const);
- } else {
- throw new IPF_ORM_Exception('Unknown attribute value: "' . $value . '"');
- }
- }
-
- public function setAttribute($attribute, $value)
- {
- if (is_string($attribute)) {
- $stringAttribute = $attribute;
- $attribute = $this->getAttributeFromString($attribute);
- $this->_state = $attribute;
- }
-
- if (is_string($value) && isset($stringAttribute)) {
- $value = $this->getAttributeValueFromString($stringAttribute, $value);
- }
-
- switch ($attribute) {
- case IPF_ORM::ATTR_COLL_KEY:
- if ( ! ($this instanceof IPF_ORM_Table)) {
- throw new IPF_ORM_Exception("This attribute can only be set at table level.");
- }
- if ($value !== null && ! $this->hasField($value)) {
- throw new IPF_ORM_Exception("Couldn't set collection key attribute. No such field '$value'.");
- }
- break;
- case IPF_ORM::ATTR_CACHE:
- case IPF_ORM::ATTR_RESULT_CACHE:
- case IPF_ORM::ATTR_QUERY_CACHE:
- if ($value !== null) {
- if ( ! ($value instanceof IPF_ORM_Cache_Interface)) {
- throw new IPF_ORM_Exception('Cache driver should implement IPF_ORM_Cache_Interface');
- }
- }
- break;
- case IPF_ORM::ATTR_PORTABILITY:
- case IPF_ORM::ATTR_DEFAULT_TABLE_TYPE:
- case IPF_ORM::ATTR_EMULATE_DATABASE:
- case IPF_ORM::ATTR_USE_NATIVE_ENUM:
- case IPF_ORM::ATTR_EXPORT:
- case IPF_ORM::ATTR_DECIMAL_PLACES:
- case IPF_ORM::ATTR_LOAD_REFERENCES:
- case IPF_ORM::ATTR_DEFAULT_PARAM_NAMESPACE:
- case IPF_ORM::ATTR_MODEL_LOADING:
- case IPF_ORM::ATTR_RESULT_CACHE_LIFESPAN:
- case IPF_ORM::ATTR_QUERY_CACHE_LIFESPAN:
- case IPF_ORM::ATTR_RECURSIVE_MERGE_FIXTURES;
- case IPF_ORM::ATTR_SINGULARIZE_IMPORT;
-
- break;
- case IPF_ORM::ATTR_FIELD_CASE:
- if ($value != 0 && $value != CASE_LOWER && $value != CASE_UPPER)
- throw new IPF_ORM_Exception('Field case attribute should be either 0, CASE_LOWER or CASE_UPPER constant.');
- break;
- default:
- throw new IPF_ORM_Exception("Unknown attribute.");
- }
-
- $this->attributes[$attribute] = $value;
- }
-
- public function getParams($namespace = null)
- {
- if ($namespace == null) {
- $namespace = $this->getAttribute(IPF_ORM::ATTR_DEFAULT_PARAM_NAMESPACE);
- }
-
- if (!isset($this->_params[$namespace])) {
- return null;
- }
-
- return $this->_params[$namespace];
- }
-
- public function getParamNamespaces()
- {
- return array_keys($this->_params);
- }
-
- public function setParam($name, $value, $namespace = null)
- {
- if ($namespace == null) {
- $namespace = $this->getAttribute(IPF_ORM::ATTR_DEFAULT_PARAM_NAMESPACE);
- }
-
- $this->_params[$namespace][$name] = $value;
-
- return $this;
- }
-
- public function getParam($name, $namespace = null)
- {
- if ($namespace == null) {
- $namespace = $this->getAttribute(IPF_ORM::ATTR_DEFAULT_PARAM_NAMESPACE);
- }
-
- if (!isset($this->_params[$name])) {
- if (isset($this->parent)) {
- return $this->parent->getParam($name, $namespace);
- }
- return null;
- }
-
- return $this->_params[$namespace][$name];
- }
-
- public function getAttribute($attribute)
- {
- if (is_string($attribute)) {
- $upper = strtoupper($attribute);
-
- $const = 'IPF_ORM::ATTR_' . $upper;
-
- if (defined($const)) {
- $attribute = constant($const);
- $this->_state = $attribute;
- } else {
- throw new IPF_ORM_Exception('Unknown attribute: "' . $attribute . '"');
- }
- }
-
- $attribute = (int) $attribute;
-
- if ($attribute < 0) {
- throw new IPF_ORM_Exception('Unknown attribute.');
- }
-
- if (isset($this->attributes[$attribute])) {
- return $this->attributes[$attribute];
- }
-
- if (isset($this->parent)) {
- return $this->parent->getAttribute($attribute);
- }
- return null;
- }
-
- public function getAttributes()
- {
- return $this->attributes;
- }
-
- public function setParent(IPF_ORM_Configurable $component)
- {
- $this->parent = $component;
- }
-
- public function getParent()
- {
- return $this->parent;
- }
-}
+++ /dev/null
-<?php
-
-abstract class IPF_ORM_Connection extends IPF_ORM_Configurable implements Countable, IteratorAggregate
-{
- protected $dbh;
- protected $tables = array();
- protected $_name = 0;
- protected $driverName;
- protected $pendingAttributes = array();
-
- private $modules = array('transaction' => false,
- 'expression' => false,
- 'export' => false,
- 'unitOfWork' => false,
- );
-
- protected $properties = array('sql_comments' => array(array('start' => '--', 'end' => "\n", 'escape' => false),
- array('start' => '/*', 'end' => '*/', 'escape' => false)),
- 'identifier_quoting' => '"',
- 'string_quoting' => array('start' => "'",
- 'end' => "'",
- 'escape' => false,
- 'escape_pattern' => false),
- 'wildcards' => array('%', '_'),
- 'varchar_max_length' => 255,
- );
-
- protected $options = array();
- private static $availableDrivers = array(
- 'Mysql',
- 'Pgsql',
- 'Oracle',
- 'Informix',
- 'Mssql',
- 'Sqlite',
- 'Firebird'
- );
- protected $_count = 0;
-
- public $dbListeners = array();
-
- public function __construct(IPF_ORM_Manager $manager, $pdo, $user = null, $pass = null)
- {
- $this->dbh = $pdo;
-
- $this->setParent($manager);
- $this->dbListeners = $manager->dbListeners;
-
- $this->setAttribute(IPF_ORM::ATTR_CASE, IPF_ORM::CASE_NATURAL);
- $this->setAttribute(IPF_ORM::ATTR_ERRMODE, IPF_ORM::ERRMODE_EXCEPTION);
-
- $this->notifyDBListeners('onOpen', $this);
- }
-
- public function getOptions()
- {
- return $this->options;
- }
-
- public function getOption($option)
- {
- if (isset($this->options[$option])) {
- return $this->options[$option];
- }
- }
-
- public function setOption($option, $value)
- {
- return $this->options[$option] = $value;
- }
-
- public function getAttribute($attribute)
- {
- if (is_string($attribute)) {
- $stringAttribute = $attribute;
- $attribute = $this->getAttributeFromString($attribute);
- }
-
- if ($attribute >= 100) {
- if ( ! isset($this->attributes[$attribute])) {
- return parent::getAttribute($attribute);
- }
- return $this->attributes[$attribute];
- }
-
- try {
- return $this->dbh->getAttribute($attribute);
- } catch (Exception $e) {
- throw new IPF_ORM_Exception('Attribute ' . $attribute . ' not found.');
- }
- }
-
- public static function getAvailableDrivers()
- {
- return PDO::getAvailableDrivers();
- }
-
- public function setAttribute($attribute, $value)
- {
- if (is_string($attribute)) {
- $attributeString = $attribute;
- $attribute = parent::getAttributeFromString($attribute);
- }
-
- if (is_string($value) && isset($attributeString)) {
- $value = parent::getAttributeValueFromString($attributeString, $value);
- }
-
- if ($attribute >= 100) {
- parent::setAttribute($attribute, $value);
- } else {
- $this->dbh->setAttribute($attribute, $value);
- }
-
- return $this;
- }
-
- public function getName()
- {
- return $this->_name;
- }
-
- public function getDriverName()
- {
- return $this->driverName;
- }
-
- public function __get($name)
- {
- if (isset($this->properties[$name])) {
- return $this->properties[$name];
- }
-
- if ( ! isset($this->modules[$name])) {
- throw new IPF_ORM_Exception('Unknown module / property ' . $name);
- }
- if ($this->modules[$name] === false) {
- switch ($name) {
- case 'unitOfWork':
- $this->modules[$name] = new IPF_ORM_Connection_UnitOfWork($this);
- break;
- default:
- $class = 'IPF_ORM_' . ucwords($name) . '_' . $this->getDriverName();
- $this->modules[$name] = new $class($this);
- }
- }
-
- return $this->modules[$name];
- }
-
- public function getManager()
- {
- return $this->getParent();
- }
-
- public function getDbh()
- {
- return $this->dbh;
- }
-
- public function incrementQueryCount()
- {
- $this->_count++;
- }
-
- public function replace(IPF_ORM_Table $table, array $fields, array $keys)
- {
- if (empty($keys)) {
- throw new IPF_ORM_Exception('Not specified which fields are keys');
- }
- $identifier = (array) $table->getIdentifier();
- $condition = array();
-
- foreach ($fields as $fieldName => $value) {
- if (in_array($fieldName, $keys)) {
- if ($value !== null) {
- $condition[] = $table->getColumnName($fieldName) . ' = ?';
- $conditionValues[] = $value;
- }
- }
- }
-
- $affectedRows = 0;
- if ( ! empty($condition) && ! empty($conditionValues)) {
- $query = 'DELETE FROM ' . $this->quoteIdentifier($table->getTableName())
- . ' WHERE ' . implode(' AND ', $condition);
-
- $affectedRows = $this->exec($query, $conditionValues);
- }
-
- $this->insert($table, $fields);
-
- $affectedRows++;
-
- return $affectedRows;
- }
-
- public function delete(IPF_ORM_Table $table, array $identifier)
- {
- $tmp = array();
-
- foreach (array_keys($identifier) as $id) {
- $tmp[] = $this->quoteIdentifier($table->getColumnName($id)) . ' = ?';
- }
-
- $query = 'DELETE FROM '
- . $this->quoteIdentifier($table->getTableName())
- . ' WHERE ' . implode(' AND ', $tmp);
-
- return $this->exec($query, array_values($identifier));
- }
-
- public function update(IPF_ORM_Table $table, array $fields, array $identifier)
- {
- if (empty($fields)) {
- return false;
- }
-
- $set = array();
- foreach ($fields as $fieldName => $value) {
- if ($value instanceof IPF_ORM_Expression) {
- $set[] = $this->quoteIdentifier($table->getColumnName($fieldName)) . ' = ' . $value->getSql();
- unset($fields[$fieldName]);
- } else {
- $set[] = $this->quoteIdentifier($table->getColumnName($fieldName)) . ' = ?';
- }
- }
-
- $params = array_merge(array_values($fields), array_values($identifier));
-
- $sql = 'UPDATE ' . $this->quoteIdentifier($table->getTableName())
- . ' SET ' . implode(', ', $set)
- . ' WHERE ' . implode(' = ? AND ', $table->getIdentifierColumnNames())
- . ' = ?';
-
- return $this->exec($sql, $params);
- }
-
- public function insert(IPF_ORM_Table $table, array $fields)
- {
- $tableName = $table->getTableName();
-
- // column names are specified as array keys
- $cols = array();
- // the query VALUES will contain either expresions (eg 'NOW()') or ?
- $a = array();
- foreach ($fields as $fieldName => $value) {
- $cols[] = $this->quoteIdentifier($table->getColumnName($fieldName));
- if ($value instanceof IPF_ORM_Expression) {
- $a[] = $value->getSql();
- unset($fields[$fieldName]);
- } else {
- $a[] = '?';
- }
- }
-
- // build the statement
- $query = 'INSERT INTO ' . $this->quoteIdentifier($tableName)
- . ' (' . implode(', ', $cols) . ')'
- . ' VALUES (' . implode(', ', $a) . ')';
-
- return $this->exec($query, array_values($fields));
- }
-
- public abstract function quoteIdentifier($str);
-
- public function convertBooleans($item)
- {
- if (is_array($item)) {
- foreach ($item as $k => $value) {
- if (is_bool($value)) {
- $item[$k] = (int) $value;
- }
- }
- } else {
- if (is_bool($item)) {
- $item = (int) $item;
- }
- }
- return $item;
- }
-
- public function quote($input, $type=null)
- {
- if ($type === null)
- $type = gettype($input);
-
- switch ($type) {
- case 'integer':
- case 'enum':
- case 'boolean':
- case 'double':
- case 'float':
- case 'bool':
- case 'decimal':
- case 'int':
- return $input;
- case 'array':
- case 'object':
- $input = serialize($input);
- case 'date':
- case 'time':
- case 'timestamp':
- case 'string':
- case 'char':
- case 'varchar':
- case 'text':
- case 'gzip':
- case 'blob':
- case 'clob':
- return $this->getDbh()->quote($input);
- default:
- throw new IPF_ORM_Exception('Unsupported type \''.$type.'\'.');
- }
- }
-
- public function escapePattern($text)
- {
- $q = $this->string_quoting['escape_pattern'];
- $text = str_replace($q, $q . $q, $text);
- foreach ($this->wildcards as $wildcard) {
- $text = str_replace($wildcard, $q . $wildcard, $text);
- }
- return $text;
- }
-
- public function setDateFormat($format = null)
- {
- }
-
- public function fetchAll($statement, array $params = array())
- {
- return $this->execute($statement, $params)->fetchAll(IPF_ORM::FETCH_ASSOC);
- }
-
- public function fetchOne($statement, array $params = array(), $colnum = 0)
- {
- return $this->execute($statement, $params)->fetchColumn($colnum);
- }
-
- public function fetchRow($statement, array $params = array())
- {
- return $this->execute($statement, $params)->fetch(IPF_ORM::FETCH_ASSOC);
- }
-
- public function fetchArray($statement, array $params = array())
- {
- return $this->execute($statement, $params)->fetch(IPF_ORM::FETCH_NUM);
- }
-
- public function fetchColumn($statement, array $params = array(), $colnum = 0)
- {
- return $this->execute($statement, $params)->fetchAll(IPF_ORM::FETCH_COLUMN, $colnum);
- }
-
- public function fetchAssoc($statement, array $params = array())
- {
- return $this->execute($statement, $params)->fetchAll(IPF_ORM::FETCH_ASSOC);
- }
-
- public function fetchBoth($statement, array $params = array())
- {
- return $this->execute($statement, $params)->fetchAll(IPF_ORM::FETCH_BOTH);
- }
-
- public function query($query, array $params = array(), $hydrationMode = null)
- {
- $parser = new IPF_ORM_Query($this);
- $res = $parser->query($query, $params, $hydrationMode);
- $parser->free();
-
- return $res;
- }
-
- public function prepare($statement)
- {
- try {
- $event = new IPF_ORM_Event($this, IPF_ORM_Event::CONN_PREPARE, $statement);
-
- $this->notifyDBListeners('prePrepare', $event);
-
- $stmt = false;
-
- if ( ! $event->skipOperation) {
- $stmt = $this->dbh->prepare($statement);
- }
-
- $this->notifyDBListeners('postPrepare', $event);
-
- return new IPF_ORM_Connection_Statement($this, $stmt);
- } catch (IPF_ORM_Exception_Adapter $e) {
- } catch (PDOException $e) {
- }
-
- $this->rethrowException($e, $this);
- }
-
- public function queryOne($query, array $params = array())
- {
- $parser = new IPF_ORM_Query($this);
-
- $coll = $parser->query($query, $params);
- if ( ! $coll->contains(0)) {
- return false;
- }
- return $coll[0];
- }
-
- public function select($query, $limit = 0, $offset = 0)
- {
- if ($limit > 0 || $offset > 0) {
- $query = $this->modifyLimitQuery($query, $limit, $offset);
- }
- return $this->execute($query);
- }
-
- public function standaloneQuery($query, $params = array())
- {
- return $this->execute($query, $params);
- }
-
- public function execute($query, array $params = array())
- {
- try {
- if ( ! empty($params)) {
- $stmt = $this->prepare($query);
- $stmt->execute($params);
-
- return $stmt;
- } else {
- $event = new IPF_ORM_Event($this, IPF_ORM_Event::CONN_QUERY, $query, $params);
-
- $this->notifyDBListeners('preQuery', $event);
-
- if ( ! $event->skipOperation) {
- $stmt = $this->dbh->query($query);
- $this->_count++;
- }
- $this->notifyDBListeners('postQuery', $event);
-
- return $stmt;
- }
- } catch (IPF_ORM_Exception_Adapter $e) {
- } catch (PDOException $e) {
- }
-
- $this->rethrowException($e, $this);
- }
-
- public function exec($query, array $params = array())
- {
- try {
- if ( ! empty($params)) {
- $stmt = $this->prepare($query);
- $stmt->execute($params);
-
- return $stmt->rowCount();
- } else {
- $event = new IPF_ORM_Event($this, IPF_ORM_Event::CONN_EXEC, $query, $params);
-
- $this->notifyDBListeners('preExec', $event);
- if ( ! $event->skipOperation) {
- $count = $this->dbh->exec($query);
-
- $this->_count++;
- }
- $this->notifyDBListeners('postExec', $event);
-
- return $count;
- }
- } catch (IPF_ORM_Exception_Adapter $e) {
- } catch (PDOException $e) { }
-
- $this->rethrowException($e, $this);
- }
-
- public function rethrowException(Exception $e, $invoker)
- {
- $event = new IPF_ORM_Event($this, IPF_ORM_Event::CONN_ERROR);
- $this->notifyDBListeners('onError', $event);
-
- $name = 'IPF_ORM_Exception_' . $this->driverName;
- $exc = new $name($e->getMessage(), (int) $e->getCode());
- if (!is_array($e->errorInfo)) {
- $e->errorInfo = array(null, null, null, null);
- }
- $exc->processErrorInfo($e->errorInfo);
- throw $exc;
- }
-
- public function hasTable($name)
- {
- return isset($this->tables[$name]);
- }
-
- public function getTable($name)
- {
- if (!isset($this->tables[$name]))
- $this->tables[$name] = new IPF_ORM_Table($name, $this);
- return $this->tables[$name];
- }
-
- public function getTables()
- {
- return $this->tables;
- }
-
- public function getIterator()
- {
- return new ArrayIterator($this->tables);
- }
-
- public function count()
- {
- return $this->_count;
- }
-
- public function addTable(IPF_ORM_Table $table)
- {
- $name = $table->getComponentName();
-
- if (isset($this->tables[$name])) {
- return false;
- }
- $this->tables[$name] = $table;
- return true;
- }
-
- public function create($name)
- {
- return $this->getTable($name)->create();
- }
-
- public function createQuery()
- {
- return new IPF_ORM_Query($this);
- }
-
- public function flush()
- {
- $this->beginInternalTransaction();
- $this->unitOfWork->saveAll();
- $this->commit();
- }
-
- public function clear()
- {
- foreach ($this->tables as $k => $table) {
- $table->getRepository()->evictAll();
- $table->clear();
- }
- }
-
- public function evictTables()
- {
- $this->tables = array();
- $this->exported = array();
- }
-
- public function getTransactionLevel()
- {
- return $this->transaction->getTransactionLevel();
- }
-
- public function errorCode()
- {
- return $this->dbh->errorCode();
- }
-
- public function errorInfo()
- {
- return $this->dbh->errorInfo();
- }
-
- public function getCacheDriver()
- {
- return $this->getResultCacheDriver();
- }
-
- public function getResultCacheDriver()
- {
- if ( ! $this->getAttribute(IPF_ORM::ATTR_RESULT_CACHE)) {
- throw new IPF_ORM_Exception('Result Cache driver not initialized.');
- }
-
- return $this->getAttribute(IPF_ORM::ATTR_RESULT_CACHE);
- }
-
- public function getQueryCacheDriver()
- {
- if ( ! $this->getAttribute(IPF_ORM::ATTR_QUERY_CACHE)) {
- throw new IPF_ORM_Exception('Query Cache driver not initialized.');
- }
-
- return $this->getAttribute(IPF_ORM::ATTR_QUERY_CACHE);
- }
-
- public function lastInsertId()
- {
- return $this->getDbh()->lastInsertId();
- }
-
- public function beginTransaction($savepoint = null)
- {
- return $this->transaction->beginTransaction($savepoint);
- }
-
- public function beginInternalTransaction($savepoint = null)
- {
- return $this->transaction->beginInternalTransaction($savepoint);
- }
-
- public function commit($savepoint = null)
- {
- return $this->transaction->commit($savepoint);
- }
-
- public function rollback($savepoint = null)
- {
- $this->transaction->rollback($savepoint);
- }
-
- public function modifyLimitQuery($query, $limit = false, $offset = false, $isManip = false)
- {
- return $query;
- }
-
- public function modifyLimitSubquery(IPF_ORM_Table $rootTable, $query, $limit = false,
- $offset = false, $isManip = false)
- {
- return $this->modifyLimitQuery($query, $limit, $offset, $isManip);
- }
-
- public function __toString()
- {
- return IPF_ORM_Utils::getConnectionAsString($this);
- }
-
- public function notifyDBListeners($method, $event)
- {
- foreach ($this->dbListeners as $listener)
- if (is_callable(array($listener, $method)))
- $listener->$method($event);
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Connection_Module
-{
- protected $conn;
- protected $moduleName;
-
- public function __construct($conn = null)
- {
- if ( ! ($conn instanceof IPF_ORM_Connection)) {
- $conn = IPF_ORM_Manager::connection();
- }
- $this->conn = $conn;
-
- $e = explode('_', get_class($this));
-
- $this->moduleName = $e[1];
- }
-
- public function getConnection()
- {
- return $this->conn;
- }
-
- public function getModuleName()
- {
- return $this->moduleName;
- }
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_Connection_Mysql extends IPF_ORM_Connection
-{
- protected $driverName = 'Mysql';
-
- protected static $keywords = array(
- 'ADD', 'ALL', 'ALTER', 'ANALYZE', 'AND', 'AS', 'ASC',
- 'ASENSITIVE', 'BEFORE', 'BETWEEN', 'BIGINT', 'BINARY', 'BLOB',
- 'BOTH', 'BY', 'BIT', 'CALL', 'CASCADE', 'CASE', 'CHANGE',
- 'CHAR', 'CHARACTER', 'CHECK', 'COLLATE', 'COLUMN',
- 'CONDITION', 'CONNECTION', 'CONSTRAINT', 'CONTINUE',
- 'CONVERT', 'CREATE', 'CROSS', 'CURRENT_DATE', 'CURRENT_TIME',
- 'CURRENT_TIMESTAMP', 'CURRENT_USER', 'CURSOR', 'DATABASE',
- 'DATABASES', 'DAY_HOUR', 'DAY_MICROSECOND', 'DAY_MINUTE',
- 'DAY_SECOND', 'DEC', 'DECIMAL', 'DECLARE', 'DEFAULT',
- 'DELAYED', 'DELETE', 'DESC', 'DESCRIBE', 'DETERMINISTIC',
- 'DISTINCT', 'DISTINCTROW', 'DIV', 'DOUBLE', 'DROP', 'DUAL',
- 'EACH', 'ELSE', 'ELSEIF', 'ENCLOSED', 'ESCAPED', 'EXISTS',
- 'EXIT', 'EXPLAIN', 'FALSE', 'FETCH', 'FLOAT', 'FLOAT4',
- 'FLOAT8', 'FOR', 'FORCE', 'FOREIGN', 'FROM', 'FULLTEXT',
- 'GRANT', 'GROUP', 'HAVING', 'HIGH_PRIORITY',
- 'HOUR_MICROSECOND', 'HOUR_MINUTE', 'HOUR_SECOND', 'IF',
- 'IGNORE', 'IN', 'INDEX', 'INFILE', 'INNER', 'INOUT',
- 'INSENSITIVE', 'INSERT', 'INT', 'INT1', 'INT2', 'INT3',
- 'INT4', 'INT8', 'INTEGER', 'INTERVAL', 'INTO', 'IS',
- 'ITERATE', 'JOIN', 'KEY', 'KEYS', 'KILL', 'LEADING', 'LEAVE',
- 'LEFT', 'LIKE', 'LIMIT', 'LINES', 'LOAD', 'LOCALTIME',
- 'LOCALTIMESTAMP', 'LOCK', 'LONG', 'LONGBLOB', 'LONGTEXT',
- 'LOOP', 'LOW_PRIORITY', 'MATCH', 'MEDIUMBLOB', 'MEDIUMINT',
- 'MEDIUMTEXT', 'MIDDLEINT', 'MINUTE_MICROSECOND',
- 'MINUTE_SECOND', 'MOD', 'MODIFIES', 'NATURAL', 'NOT',
- 'NO_WRITE_TO_BINLOG', 'NULL', 'NUMERIC', 'ON', 'OPTIMIZE',
- 'OPTION', 'OPTIONALLY', 'OR', 'ORDER', 'OUT', 'OUTER',
- 'OUTFILE', 'PRECISION', 'PRIMARY', 'PROCEDURE', 'PURGE',
- 'RAID0', 'READ', 'READS', 'REAL', 'REFERENCES', 'REGEXP',
- 'RELEASE', 'RENAME', 'REPEAT', 'REPLACE', 'REQUIRE',
- 'RESTRICT', 'RETURN', 'REVOKE', 'RIGHT', 'RLIKE', 'SCHEMA',
- 'SCHEMAS', 'SECOND_MICROSECOND', 'SELECT', 'SENSITIVE',
- 'SEPARATOR', 'SET', 'SHOW', 'SMALLINT', 'SONAME', 'SPATIAL',
- 'SPECIFIC', 'SQL', 'SQLEXCEPTION', 'SQLSTATE', 'SQLWARNING',
- 'SQL_BIG_RESULT', 'SQL_CALC_FOUND_ROWS', 'SQL_SMALL_RESULT',
- 'SSL', 'STARTING', 'STRAIGHT_JOIN', 'TABLE', 'TERMINATED',
- 'THEN', 'TINYBLOB', 'TINYINT', 'TINYTEXT', 'TO', 'TRAILING',
- 'TRIGGER', 'TRUE', 'UNDO', 'UNION', 'UNIQUE', 'UNLOCK',
- 'UNSIGNED', 'UPDATE', 'USAGE', 'USE', 'USING', 'UTC_DATE',
- 'UTC_TIME', 'UTC_TIMESTAMP', 'VALUES', 'VARBINARY', 'VARCHAR',
- 'VARCHARACTER', 'VARYING', 'WHEN', 'WHERE', 'WHILE', 'WITH',
- 'WRITE', 'X509', 'XOR', 'YEAR_MONTH', 'ZEROFILL'
- );
-
- public function __construct(IPF_ORM_Manager $manager, $adapter)
- {
- $this->attributes[IPF_ORM::ATTR_DEFAULT_TABLE_TYPE] = 'INNODB';
-
- $this->properties['string_quoting'] = array('start' => "'",
- 'end' => "'",
- 'escape' => '\\',
- 'escape_pattern' => '\\');
-
- $this->properties['identifier_quoting'] = '`';
-
- $this->properties['sql_comments'] = array(
- array('start' => '-- ', 'end' => "\n", 'escape' => false),
- array('start' => '#', 'end' => "\n", 'escape' => false),
- array('start' => '/*', 'end' => '*/', 'escape' => false),
- );
-
- $this->properties['varchar_max_length'] = 255;
-
- parent::__construct($manager, $adapter);
- }
-
- public function quoteIdentifier($str)
- {
- $quote = $this->identifier_quoting;
- $q = array();
- foreach (explode('.', $str) as $s) {
- if (in_array(strtoupper($s), self::$keywords))
- $q[] = $quote . str_replace($quote, $quote . $quote, $s) . $quote;
- else
- $q[] = $s;
- }
- return implode('.', $q);
- }
-
- public function getDatabaseName()
- {
- return $this->fetchOne('SELECT DATABASE()');
- }
-
- public function replace(IPF_ORM_Table $table, array $fields, array $keys)
- {
- if (empty($keys)) {
- throw new IPF_ORM_Exception('Not specified which fields are keys');
- }
- $columns = array();
- $values = array();
- $params = array();
- foreach ($fields as $fieldName => $value) {
- $columns[] = $table->getColumnName($fieldName);
- $values[] = '?';
- $params[] = $value;
- }
- $query = 'REPLACE INTO ' . $table->getTableName() . ' (' . implode(',', $columns) . ') VALUES (' . implode(',', $values) . ')';
- return $this->exec($query, $params);
- }
-
- public function modifyLimitQuery($query, $limit = false,$offset = false,$isManip=false)
- {
- $limit = (int) $limit;
- $offset = (int) $offset;
-
- if ($limit && $offset) {
- $query .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
- } elseif ($limit && ! $offset) {
- $query .= ' LIMIT ' . $limit;
- } elseif ( ! $limit && $offset) {
- $query .= ' LIMIT 999999999999 OFFSET ' . $offset;
- }
- return $query;
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Connection_Statement
-{
- protected $_conn;
- protected $_stmt;
- public function __construct(IPF_ORM_Connection $conn, $stmt)
- {
- $this->_conn = $conn;
- $this->_stmt = $stmt;
-
- if ($stmt === false) {
- throw new IPF_ORM_Exception('Unknown statement object given.');
- }
- }
-
- public function getConnection()
- {
- return $this->_conn;
- }
- public function getStatement()
- {
- return $this->_stmt;
- }
- public function getQuery()
- {
- return $this->_stmt->queryString;
- }
-
- public function bindColumn($column, $param, $type = null)
- {
- if ($type === null) {
- return $this->_stmt->bindColumn($column, $param);
- } else {
- return $this->_stmt->bindColumn($column, $param, $type);
- }
- }
-
- public function bindValue($param, $value, $type = null)
- {
- if ($type === null) {
- return $this->_stmt->bindValue($param, $value);
- } else {
- return $this->_stmt->bindValue($param, $value, $type);
- }
- }
-
- public function bindParam($column, &$variable, $type = null, $length = null, $driverOptions = array())
- {
- if ($type === null) {
- return $this->_stmt->bindParam($column, $variable);
- } else {
- return $this->_stmt->bindParam($column, $variable, $type, $length, $driverOptions);
- }
- }
-
- public function closeCursor()
- {
- return $this->_stmt->closeCursor();
- }
-
- public function columnCount()
- {
- return $this->_stmt->columnCount();
- }
-
- public function errorCode()
- {
- return $this->_stmt->errorCode();
- }
-
- public function errorInfo()
- {
- return $this->_stmt->errorInfo();
- }
-
- public function execute($params = null)
- {
- try {
- $event = new IPF_ORM_Event($this, IPF_ORM_Event::STMT_EXECUTE, $this->getQuery(), $params);
- $this->_conn->notifyDBListeners('preStmtExecute', $event);
-
- $result = true;
- if ( ! $event->skipOperation) {
- $result = $this->_stmt->execute($params);
- $this->_conn->incrementQueryCount();
- }
-
- $this->_conn->notifyDBListeners('postStmtExecute', $event);
-
- return $result;
- } catch (PDOException $e) {
- } catch (IPF_ORM_Exception_Adapter $e) {
- }
-
- $this->_conn->rethrowException($e, $this);
-
- return false;
- }
-
- public function fetch($fetchMode = IPF_ORM::FETCH_BOTH,
- $cursorOrientation = IPF_ORM::FETCH_ORI_NEXT,
- $cursorOffset = null)
- {
- $event = new IPF_ORM_Event($this, IPF_ORM_Event::STMT_FETCH, $this->getQuery());
-
- $event->fetchMode = $fetchMode;
- $event->cursorOrientation = $cursorOrientation;
- $event->cursorOffset = $cursorOffset;
-
- $data = $this->_conn->notifyDBListeners('preFetch', $event);
-
- if ( ! $event->skipOperation) {
- $data = $this->_stmt->fetch($fetchMode, $cursorOrientation, $cursorOffset);
- }
-
- $this->_conn->notifyDBListeners('postFetch', $event);
-
- return $data;
- }
-
- public function fetchAll($fetchMode = IPF_ORM::FETCH_BOTH,
- $columnIndex = null)
- {
- $event = new IPF_ORM_Event($this, IPF_ORM_Event::STMT_FETCHALL, $this->getQuery());
- $event->fetchMode = $fetchMode;
- $event->columnIndex = $columnIndex;
-
- $this->_conn->notifyDBListeners('preFetchAll', $event);
-
- if ( ! $event->skipOperation) {
- if ($columnIndex !== null) {
- $data = $this->_stmt->fetchAll($fetchMode, $columnIndex);
- } else {
- $data = $this->_stmt->fetchAll($fetchMode);
- }
-
- $event->data = $data;
- }
-
- $this->_conn->notifyDBListeners('postFetchAll', $event);
-
- return $data;
- }
-
- public function fetchColumn($columnIndex = 0)
- {
- return $this->_stmt->fetchColumn($columnIndex);
- }
-
- public function fetchObject($className = 'stdClass', $args = array())
- {
- return $this->_stmt->fetchObject($className, $args);
- }
-
- public function getAttribute($attribute)
- {
- return $this->_stmt->getAttribute($attribute);
- }
-
- public function getColumnMeta($column)
- {
- return $this->_stmt->getColumnMeta($column);
- }
-
- public function nextRowset()
- {
- return $this->_stmt->nextRowset();
- }
-
- public function rowCount()
- {
- return $this->_stmt->rowCount();
- }
-
- public function setAttribute($attribute, $value)
- {
- return $this->_stmt->setAttribute($attribute, $value);
- }
-
- public function setFetchMode($mode, $arg1 = null, $arg2 = null)
- {
- return $this->_stmt->setFetchMode($mode, $arg1, $arg2);
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Connection_UnitOfWork extends IPF_ORM_Connection_Module
-{
- public function saveGraph(IPF_ORM_Record $record)
- {
- $conn = $this->getConnection();
-
- $state = $record->state();
- if ($state === IPF_ORM_Record::STATE_LOCKED) {
- return false;
- }
-
- $record->state(IPF_ORM_Record::STATE_LOCKED);
-
- $conn->beginInternalTransaction();
- $saveLater = $this->saveRelated($record);
-
- $record->state($state);
-
- if ($record->isValid()) {
- $event = new IPF_ORM_Event($record, IPF_ORM_Event::RECORD_SAVE);
- $record->preSave($event);
- $record->getTable()->notifyRecordListeners('preSave', $event);
- $state = $record->state();
-
- if ( ! $event->skipOperation) {
- switch ($state) {
- case IPF_ORM_Record::STATE_TDIRTY:
- $this->insert($record);
- break;
- case IPF_ORM_Record::STATE_DIRTY:
- case IPF_ORM_Record::STATE_PROXY:
- $this->update($record);
- break;
- case IPF_ORM_Record::STATE_CLEAN:
- case IPF_ORM_Record::STATE_TCLEAN:
- break;
- }
- }
-
- // NOTE: what about referential integrity issues?
- foreach ($record->getPendingDeletes() as $pendingDelete) {
- $pendingDelete->delete();
- }
-
- $record->getTable()->notifyRecordListeners('postSave', $event);
- $record->postSave($event);
- } else {
- $conn->transaction->addInvalid($record);
- }
-
- $state = $record->state();
-
- $record->state(IPF_ORM_Record::STATE_LOCKED);
-
- foreach ($saveLater as $fk) {
- $alias = $fk->getAlias();
-
- if ($record->hasReference($alias)) {
- $obj = $record->$alias;
- // check that the related object is not an instance of IPF_ORM_Null
- if ( ! ($obj instanceof IPF_ORM_Null)) {
- $obj->save($conn);
- }
- }
- }
-
- // save the MANY-TO-MANY associations
- $this->saveAssociations($record);
-
- $record->state($state);
- $conn->commit();
-
- return true;
- }
-
- public function save(IPF_ORM_Record $record)
- {
- $event = new IPF_ORM_Event($record, IPF_ORM_Event::RECORD_SAVE);
-
- $record->preSave($event);
-
- $record->getTable()->notifyRecordListeners('preSave', $event);
-
- if ( ! $event->skipOperation) {
- switch ($record->state()) {
- case IPF_ORM_Record::STATE_TDIRTY:
- $this->insert($record);
- break;
- case IPF_ORM_Record::STATE_DIRTY:
- case IPF_ORM_Record::STATE_PROXY:
- $this->update($record);
- break;
- case IPF_ORM_Record::STATE_CLEAN:
- case IPF_ORM_Record::STATE_TCLEAN:
- // do nothing
- break;
- }
- }
-
- $record->getTable()->notifyRecordListeners('postSave', $event);
-
- $record->postSave($event);
- }
-
- public function delete(IPF_ORM_Record $record)
- {
- $deletions = array();
- $this->_collectDeletions($record, $deletions);
- return $this->_executeDeletions($deletions);
- }
-
- private function _collectDeletions(IPF_ORM_Record $record, array &$deletions)
- {
- if ( ! $record->exists()) {
- return;
- }
-
- $deletions[$record->getOid()] = $record;
- $this->_cascadeDelete($record, $deletions);
- }
-
- private function _executeDeletions(array $deletions)
- {
- // collect class names
- $classNames = array();
- foreach ($deletions as $record) {
- $classNames[] = $record->getTable()->getComponentName();
- }
- $classNames = array_unique($classNames);
-
- // order deletes
- $executionOrder = $this->buildFlushTree($classNames);
-
- // execute
- try {
- $this->conn->beginInternalTransaction();
-
- for ($i = count($executionOrder) - 1; $i >= 0; $i--) {
- $className = $executionOrder[$i];
- $table = $this->conn->getTable($className);
-
- // collect identifiers
- $identifierMaps = array();
- $deletedRecords = array();
- foreach ($deletions as $oid => $record) {
- if ($record->getTable()->getComponentName() == $className) {
- $veto = $this->_preDelete($record);
- if ( ! $veto) {
- $identifierMaps[] = $record->identifier();
- $deletedRecords[] = $record;
- unset($deletions[$oid]);
- }
- }
- }
-
- if (count($deletedRecords) < 1) {
- continue;
- }
-
- // extract query parameters (only the identifier values are of interest)
- $params = array();
- $columnNames = array();
- foreach ($identifierMaps as $idMap) {
- while (list($fieldName, $value) = each($idMap)) {
- $params[] = $value;
- $columnNames[] = $table->getColumnName($fieldName);
- }
- }
- $columnNames = array_unique($columnNames);
-
- // delete
- $tableName = $table->getTableName();
- $sql = "DELETE FROM " . $this->conn->quoteIdentifier($tableName) . " WHERE ";
-
- if ($table->isIdentifierComposite()) {
- $sql .= $this->_buildSqlCompositeKeyCondition($columnNames, count($identifierMaps));
- $this->conn->exec($sql, $params);
- } else {
- $sql .= $this->_buildSqlSingleKeyCondition($columnNames, count($params));
- $this->conn->exec($sql, $params);
- }
-
- // adjust state, remove from identity map and inform postDelete listeners
- foreach ($deletedRecords as $record) {
- $record->state(IPF_ORM_Record::STATE_TCLEAN);
- $record->getTable()->removeRecord($record);
- $this->_postDelete($record);
- }
- }
-
- $this->conn->commit();
- // trigger postDelete for records skipped during the deletion (veto!)
- foreach ($deletions as $skippedRecord) {
- $this->_postDelete($skippedRecord);
- }
-
- return true;
- } catch (Exception $e) {
- $this->conn->rollback();
- throw $e;
- }
- }
-
- private function _buildSqlSingleKeyCondition($columnNames, $numRecords)
- {
- $idColumn = $this->conn->quoteIdentifier($columnNames[0]);
- return implode(' OR ', array_fill(0, $numRecords, "$idColumn = ?"));
- }
-
- private function _buildSqlCompositeKeyCondition($columnNames, $numRecords)
- {
- $singleCondition = "";
- foreach ($columnNames as $columnName) {
- $columnName = $this->conn->quoteIdentifier($columnName);
- if ($singleCondition === "") {
- $singleCondition .= "($columnName = ?";
- } else {
- $singleCondition .= " AND $columnName = ?";
- }
- }
- $singleCondition .= ")";
- $fullCondition = implode(' OR ', array_fill(0, $numRecords, $singleCondition));
-
- return $fullCondition;
- }
-
- protected function _cascadeDelete(IPF_ORM_Record $record, array &$deletions)
- {
- foreach ($record->getTable()->getRelations() as $relation) {
- if ($relation->isCascadeDelete()) {
- $fieldName = $relation->getAlias();
- // if it's a xToOne relation and the related object is already loaded
- // we don't need to refresh.
- if ( ! ($relation->getType() == IPF_ORM_Relation::ONE && isset($record->$fieldName))) {
- $record->refreshRelated($relation->getAlias());
- }
- $relatedObjects = $record->get($relation->getAlias());
- if ($relatedObjects instanceof IPF_ORM_Record && $relatedObjects->exists()
- && ! isset($deletions[$relatedObjects->getOid()])) {
- $this->_collectDeletions($relatedObjects, $deletions);
- } else if ($relatedObjects instanceof IPF_ORM_Collection && count($relatedObjects) > 0) {
- // cascade the delete to the other objects
- foreach ($relatedObjects as $object) {
- if ( ! isset($deletions[$object->getOid()])) {
- $this->_collectDeletions($object, $deletions);
- }
- }
- }
- }
- }
- }
-
- public function saveRelated(IPF_ORM_Record $record)
- {
- $saveLater = array();
- foreach ($record->getReferences() as $k => $v) {
- $rel = $record->getTable()->getRelation($k);
-
- $local = $rel->getLocal();
- $foreign = $rel->getForeign();
-
- if ($rel instanceof IPF_ORM_Relation_ForeignKey) {
- $saveLater[$k] = $rel;
- } else if ($rel instanceof IPF_ORM_Relation_LocalKey) {
- // ONE-TO-ONE relationship
- $obj = $record->get($rel->getAlias());
-
- // Protection against infinite function recursion before attempting to save
- if ($obj instanceof IPF_ORM_Record && $obj->isModified()) {
- $obj->save($this->conn);
-
- /** Can this be removed?
- $id = array_values($obj->identifier());
-
- foreach ((array) $rel->getLocal() as $k => $field) {
- $record->set($field, $id[$k]);
- }
- */
- }
- }
- }
-
- return $saveLater;
- }
-
- public function saveAssociations(IPF_ORM_Record $record)
- {
- foreach ($record->getReferences() as $k => $v) {
- $rel = $record->getTable()->getRelation($k);
- //print get_class($rel);
- if ($rel instanceof IPF_ORM_Relation_Association) {
- $v->save($this->conn);
-
- $assocTable = $rel->getAssociationTable();
-
- foreach ($v->getDeleteDiff() as $r) {
- $query = 'DELETE FROM ' . $assocTable->getTableName()
- . ' WHERE ' . $rel->getForeign() . ' = ?'
- . ' AND ' . $rel->getLocal() . ' = ?';
-
- $this->conn->execute($query, array($r->getIncremented(), $record->getIncremented()));
- }
-
- foreach ($v->getInsertDiff() as $r) {
- $assocRecord = $assocTable->create();
- $assocRecord->set($assocTable->getFieldName($rel->getForeign()), $r);
- $assocRecord->set($assocTable->getFieldName($rel->getLocal()), $record);
- $this->saveGraph($assocRecord);
- }
- }
- }
- }
-
- private function _preDelete(IPF_ORM_Record $record)
- {
- $event = new IPF_ORM_Event($record, IPF_ORM_Event::RECORD_DELETE);
- $record->preDelete($event);
- $record->getTable()->notifyRecordListeners('preDelete', $event);
-
- return $event->skipOperation;
- }
-
- private function _postDelete(IPF_ORM_Record $record)
- {
- $event = new IPF_ORM_Event($record, IPF_ORM_Event::RECORD_DELETE);
- $record->postDelete($event);
- $record->getTable()->notifyRecordListeners('postDelete', $event);
- }
-
- public function saveAll()
- {
- // get the flush tree
- $tree = $this->buildFlushTree($this->conn->getTables());
-
- // save all records
- foreach ($tree as $name) {
- $table = $this->conn->getTable($name);
- foreach ($table->getRepository() as $record) {
- $this->saveGraph($record);
- }
- }
- }
-
- public function update(IPF_ORM_Record $record)
- {
- $event = new IPF_ORM_Event($record, IPF_ORM_Event::RECORD_UPDATE);
- $record->preUpdate($event);
- $table = $record->getTable();
- $table->notifyRecordListeners('preUpdate', $event);
-
- if ( ! $event->skipOperation) {
- $identifier = $record->identifier();
- $array = $record->getPrepared();
- $this->conn->update($table, $array, $identifier);
- $record->assignIdentifier(true);
- }
-
- $table->notifyRecordListeners('postUpdate', $event);
-
- $record->postUpdate($event);
-
- return true;
- }
-
- public function insert(IPF_ORM_Record $record)
- {
- // listen the onPreInsert event
- $event = new IPF_ORM_Event($record, IPF_ORM_Event::RECORD_INSERT);
- $record->preInsert($event);
- $table = $record->getTable();
- $table->notifyRecordListeners('preInsert', $event);
-
- if ( ! $event->skipOperation) {
- $this->processSingleInsert($record);
- }
-
- $table->addRecord($record);
- $table->notifyRecordListeners('postInsert', $event);
- $record->postInsert($event);
-
- return true;
- }
-
- public function processSingleInsert(IPF_ORM_Record $record)
- {
- $fields = $record->getPrepared();
- $table = $record->getTable();
-
- // Populate fields with a blank array so that a blank records can be inserted
- if (empty($fields)) {
- foreach ($table->getFieldNames() as $field) {
- $fields[$field] = null;
- }
- }
-
- $identifier = (array) $table->getIdentifier();
-
- $this->conn->insert($table, $fields);
-
- if (count($identifier) == 1 && $table->getIdentifierType() !== IPF_ORM::IDENTIFIER_NATURAL) {
- $id = $this->conn->lastInsertId();
- if (!$id)
- throw new IPF_ORM_Exception("Couldn't get last insert identifier.");
-
- $record->assignIdentifier($id);
- } else {
- $record->assignIdentifier(true);
- }
- }
-
- public function buildFlushTree(array $tables)
- {
- // determine classes to order. only necessary because the $tables param
- // can contain strings or table objects...
- $classesToOrder = array();
- foreach ($tables as $table) {
- if ( ! ($table instanceof IPF_ORM_Table)) {
- $table = $this->conn->getTable($table, false);
- }
- $classesToOrder[] = $table->getComponentName();
- }
- $classesToOrder = array_unique($classesToOrder);
-
- if (count($classesToOrder) < 2) {
- return $classesToOrder;
- }
-
- // build the correct order
- $flushList = array();
- foreach ($classesToOrder as $class) {
- $table = $this->conn->getTable($class, false);
- $currentClass = $table->getComponentName();
-
- $index = array_search($currentClass, $flushList);
-
- if ($index === false) {
- //echo "adding $currentClass to flushlist";
- $flushList[] = $currentClass;
- $index = max(array_keys($flushList));
- }
-
- $rels = $table->getRelations();
-
- // move all foreignkey relations to the beginning
- foreach ($rels as $key => $rel) {
- if ($rel instanceof IPF_ORM_Relation_ForeignKey) {
- unset($rels[$key]);
- array_unshift($rels, $rel);
- }
- }
-
- foreach ($rels as $rel) {
- $relatedClassName = $rel->getTable()->getComponentName();
-
- if ( ! in_array($relatedClassName, $classesToOrder)) {
- continue;
- }
-
- $relatedCompIndex = array_search($relatedClassName, $flushList);
- $type = $rel->getType();
-
- // skip self-referenced relations
- if ($relatedClassName === $currentClass) {
- continue;
- }
-
- if ($rel instanceof IPF_ORM_Relation_ForeignKey) {
- // the related component needs to come after this component in
- // the list (since it holds the fk)
-
- if ($relatedCompIndex !== false) {
- // the component is already in the list
- if ($relatedCompIndex >= $index) {
- // it's already in the right place
- continue;
- }
-
- unset($flushList[$index]);
- // the related comp has the fk. so put "this" comp immediately
- // before it in the list
- array_splice($flushList, $relatedCompIndex, 0, $currentClass);
- $index = $relatedCompIndex;
- } else {
- $flushList[] = $relatedClassName;
- }
-
- } else if ($rel instanceof IPF_ORM_Relation_LocalKey) {
- // the related component needs to come before the current component
- // in the list (since this component holds the fk).
-
- if ($relatedCompIndex !== false) {
- // already in flush list
- if ($relatedCompIndex <= $index) {
- // it's in the right place
- continue;
- }
-
- unset($flushList[$relatedCompIndex]);
- // "this" comp has the fk. so put the related comp before it
- // in the list
- array_splice($flushList, $index, 0, $relatedClassName);
- } else {
- array_unshift($flushList, $relatedClassName);
- $index++;
- }
- } else if ($rel instanceof IPF_ORM_Relation_Association) {
- // the association class needs to come after both classes
- // that are connected through it in the list (since it holds
- // both fks)
-
- $assocTable = $rel->getAssociationFactory();
- $assocClassName = $assocTable->getComponentName();
-
- if ($relatedCompIndex !== false) {
- unset($flushList[$relatedCompIndex]);
- }
-
- array_splice($flushList, $index, 0, $relatedClassName);
- $index++;
-
- $index3 = array_search($assocClassName, $flushList);
-
- if ($index3 !== false) {
- if ($index3 >= $index) {
- continue;
- }
-
- unset($flushList[$index]);
- array_splice($flushList, $index3, 0, $assocClassName);
- $index = $relatedCompIndex;
- } else {
- $flushList[] = $assocClassName;
- }
- }
- }
- }
-
- return array_values($flushList);
- }
-
- private function _formatDataSet(IPF_ORM_Record $record)
- {
- $table = $record->getTable();
- $dataSet = array();
- $component = $table->getComponentName();
- $array = $record->getPrepared();
-
- foreach ($table->getColumns() as $columnName => $definition) {
- if ( ! isset($dataSet[$component])) {
- $dataSet[$component] = array();
- }
-
- $fieldName = $table->getFieldName($columnName);
- if (isset($definition['primary']) && $definition['primary']) {
- continue;
- }
-
- if ( ! array_key_exists($fieldName, $array)) {
- continue;
- }
-
- if (isset($definition['owner'])) {
- $dataSet[$definition['owner']][$fieldName] = $array[$fieldName];
- } else {
- $dataSet[$component][$fieldName] = $array[$fieldName];
- }
- }
-
- return $dataSet;
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Event
-{
- const CONN_QUERY = 1;
- const CONN_EXEC = 2;
- const CONN_PREPARE = 3;
- const CONN_CONNECT = 4;
- const CONN_CLOSE = 5;
- const CONN_ERROR = 6;
-
- const STMT_EXECUTE = 10;
- const STMT_FETCH = 11;
- const STMT_FETCHALL = 12;
-
- const TX_BEGIN = 31;
- const TX_COMMIT = 32;
- const TX_ROLLBACK = 33;
- const SAVEPOINT_CREATE = 34;
- const SAVEPOINT_ROLLBACK = 35;
- const SAVEPOINT_COMMIT = 36;
-
- const HYDRATE = 40;
-
- const RECORD_DELETE = 21;
- const RECORD_SAVE = 22;
- const RECORD_UPDATE = 23;
- const RECORD_INSERT = 24;
- const RECORD_SERIALIZE = 25;
- const RECORD_UNSERIALIZE = 26;
- const RECORD_DQL_SELECT = 28;
- const RECORD_DQL_DELETE = 27;
- const RECORD_DQL_UPDATE = 29;
-
- protected $_invoker;
-
- protected $_query;
-
- protected $_params;
-
- protected $_code;
-
- protected $_startedMicrotime;
-
- protected $_endedMicrotime;
-
- protected $_options = array();
-
- public function __construct($invoker, $code, $query = null, $params = array())
- {
- $this->_invoker = $invoker;
- $this->_code = $code;
- $this->_query = $query;
- $this->_params = $params;
- }
-
- public function getQuery()
- {
- return $this->_query;
- }
-
- public function getName()
- {
- switch ($this->_code) {
- case self::CONN_QUERY:
- return 'query';
- case self::CONN_EXEC:
- return 'exec';
- case self::CONN_PREPARE:
- return 'prepare';
- case self::CONN_CONNECT:
- return 'connect';
- case self::CONN_CLOSE:
- return 'close';
- case self::CONN_ERROR:
- return 'error';
-
- case self::STMT_EXECUTE:
- return 'execute';
- case self::STMT_FETCH:
- return 'fetch';
- case self::STMT_FETCHALL:
- return 'fetch all';
-
- case self::TX_BEGIN:
- return 'begin';
- case self::TX_COMMIT:
- return 'commit';
- case self::TX_ROLLBACK:
- return 'rollback';
-
- case self::SAVEPOINT_CREATE:
- return 'create savepoint';
- case self::SAVEPOINT_ROLLBACK:
- return 'rollback savepoint';
- case self::SAVEPOINT_COMMIT:
- return 'commit savepoint';
-
- case self::RECORD_DELETE:
- return 'delete record';
- case self::RECORD_SAVE:
- return 'save record';
- case self::RECORD_UPDATE:
- return 'update record';
- case self::RECORD_INSERT:
- return 'insert record';
- case self::RECORD_SERIALIZE:
- return 'serialize record';
- case self::RECORD_UNSERIALIZE:
- return 'unserialize record';
- case self::RECORD_DQL_SELECT:
- return 'select records';
- case self::RECORD_DQL_DELETE:
- return 'delete records';
- case self::RECORD_DQL_UPDATE:
- return 'update records';
- }
- }
-
- public function getCode()
- {
- return $this->_code;
- }
-
- public function __get($option)
- {
- if ( ! isset($this->_options[$option])) {
- return null;
- }
-
- return $this->_options[$option];
- }
-
- public function skipOperation()
- {
- $this->_options['skipOperation'] = true;
-
- return $this;
- }
-
- public function __set($option, $value)
- {
- $this->_options[$option] = $value;
-
- return $this;
- }
-
- public function set($option, &$value)
- {
- $this->_options[$option] =& $value;
-
- return $this;
- }
-
- public function start()
- {
- $this->_startedMicrotime = microtime(true);
- }
-
- public function hasEnded()
- {
- return ($this->_endedMicrotime != null);
- }
-
- public function end()
- {
- $this->_endedMicrotime = microtime(true);
-
- return $this;
- }
-
- public function getInvoker()
- {
- return $this->_invoker;
- }
-
- public function setInvoker($invoker)
- {
- $this->_invoker = $invoker;
- }
-
- public function getParams()
- {
- return $this->_params;
- }
-
- public function getElapsedSecs()
- {
- if (is_null($this->_endedMicrotime)) {
- return false;
- }
- return ($this->_endedMicrotime - $this->_startedMicrotime);
- }
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_Exception extends IPF_Exception
-{
- protected static $_errorMessages = array(
- IPF_ORM::ERR => 'unknown error',
- IPF_ORM::ERR_ALREADY_EXISTS => 'already exists',
- IPF_ORM::ERR_CANNOT_CREATE => 'can not create',
- IPF_ORM::ERR_CANNOT_ALTER => 'can not alter',
- IPF_ORM::ERR_CANNOT_REPLACE => 'can not replace',
- IPF_ORM::ERR_CANNOT_DELETE => 'can not delete',
- IPF_ORM::ERR_CANNOT_DROP => 'can not drop',
- IPF_ORM::ERR_CONSTRAINT => 'constraint violation',
- IPF_ORM::ERR_CONSTRAINT_NOT_NULL=> 'null value violates not-null constraint',
- IPF_ORM::ERR_DIVZERO => 'division by zero',
- IPF_ORM::ERR_INVALID => 'invalid',
- IPF_ORM::ERR_INVALID_DATE => 'invalid date or time',
- IPF_ORM::ERR_INVALID_NUMBER => 'invalid number',
- IPF_ORM::ERR_MISMATCH => 'mismatch',
- IPF_ORM::ERR_NODBSELECTED => 'no database selected',
- IPF_ORM::ERR_NOSUCHFIELD => 'no such field',
- IPF_ORM::ERR_NOSUCHTABLE => 'no such table',
- IPF_ORM::ERR_NOT_CAPABLE => 'IPF backend not capable',
- IPF_ORM::ERR_NOT_FOUND => 'not found',
- IPF_ORM::ERR_NOT_LOCKED => 'not locked',
- IPF_ORM::ERR_SYNTAX => 'syntax error',
- IPF_ORM::ERR_UNSUPPORTED => 'not supported',
- IPF_ORM::ERR_VALUE_COUNT_ON_ROW => 'value count on row',
- IPF_ORM::ERR_INVALID_DSN => 'invalid DSN',
- IPF_ORM::ERR_CONNECT_FAILED => 'connect failed',
- IPF_ORM::ERR_NEED_MORE_DATA => 'insufficient data supplied',
- IPF_ORM::ERR_EXTENSION_NOT_FOUND=> 'extension not found',
- IPF_ORM::ERR_NOSUCHDB => 'no such database',
- IPF_ORM::ERR_ACCESS_VIOLATION => 'insufficient permissions',
- IPF_ORM::ERR_LOADMODULE => 'error while including on demand module',
- IPF_ORM::ERR_TRUNCATED => 'truncated',
- IPF_ORM::ERR_DEADLOCK => 'deadlock detected',
- );
-
- public function errorMessage($value = null)
- {
- if (is_null($value)) {
- return self::$_errorMessages;
- }
-
- return isset(self::$_errorMessages[$value]) ?
- self::$_errorMessages[$value] : self::$_errorMessages[IPF_ORM::ERR];
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Exception_Adapter extends IPF_ORM_Exception{}
+++ /dev/null
-<?php
-
-class IPF_ORM_Exception_Connection extends IPF_ORM_Exception
-{
- static protected $errorMessages = array(
- IPF_ORM::ERR => 'unknown error',
- IPF_ORM::ERR_ALREADY_EXISTS => 'already exists',
- IPF_ORM::ERR_CANNOT_CREATE => 'can not create',
- IPF_ORM::ERR_CANNOT_ALTER => 'can not alter',
- IPF_ORM::ERR_CANNOT_REPLACE => 'can not replace',
- IPF_ORM::ERR_CANNOT_DELETE => 'can not delete',
- IPF_ORM::ERR_CANNOT_DROP => 'can not drop',
- IPF_ORM::ERR_CONSTRAINT => 'constraint violation',
- IPF_ORM::ERR_CONSTRAINT_NOT_NULL=> 'null value violates not-null constraint',
- IPF_ORM::ERR_DIVZERO => 'division by zero',
- IPF_ORM::ERR_INVALID => 'invalid',
- IPF_ORM::ERR_INVALID_DATE => 'invalid date or time',
- IPF_ORM::ERR_INVALID_NUMBER => 'invalid number',
- IPF_ORM::ERR_MISMATCH => 'mismatch',
- IPF_ORM::ERR_NODBSELECTED => 'no database selected',
- IPF_ORM::ERR_NOSUCHFIELD => 'no such field',
- IPF_ORM::ERR_NOSUCHTABLE => 'no such table',
- IPF_ORM::ERR_NOT_CAPABLE => 'IPF_ORM backend not capable',
- IPF_ORM::ERR_NOT_FOUND => 'not found',
- IPF_ORM::ERR_NOT_LOCKED => 'not locked',
- IPF_ORM::ERR_SYNTAX => 'syntax error',
- IPF_ORM::ERR_UNSUPPORTED => 'not supported',
- IPF_ORM::ERR_VALUE_COUNT_ON_ROW => 'value count on row',
- IPF_ORM::ERR_INVALID_DSN => 'invalid DSN',
- IPF_ORM::ERR_CONNECT_FAILED => 'connect failed',
- IPF_ORM::ERR_NEED_MORE_DATA => 'insufficient data supplied',
- IPF_ORM::ERR_EXTENSION_NOT_FOUND=> 'extension not found',
- IPF_ORM::ERR_NOSUCHDB => 'no such database',
- IPF_ORM::ERR_ACCESS_VIOLATION => 'insufficient permissions',
- IPF_ORM::ERR_LOADMODULE => 'error while including on demand module',
- IPF_ORM::ERR_TRUNCATED => 'truncated',
- IPF_ORM::ERR_DEADLOCK => 'deadlock detected',
- );
-
- protected $portableCode;
-
- public function getPortableCode()
- {
- return $this->portableCode;
- }
-
- public function getPortableMessage()
- {
- return self::errorMessage($this->portableCode);
- }
-
- public function errorMessage($value = null)
- {
- return isset(self::$errorMessages[$value]) ?
- self::$errorMessages[$value] : self::$errorMessages[IPF_ORM::ERR];
- }
-
- public function processErrorInfo(array $errorInfo)
- { }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-class IPF_ORM_Exception_Locator extends IPF_ORM_Exception_Base{}
+++ /dev/null
-<?php
-
-class IPF_ORM_Exception_Mysql extends IPF_ORM_Exception_Connection
-{
- protected static $errorCodeMap = array(
- 1004 => IPF_ORM::ERR_CANNOT_CREATE,
- 1005 => IPF_ORM::ERR_CANNOT_CREATE,
- 1006 => IPF_ORM::ERR_CANNOT_CREATE,
- 1007 => IPF_ORM::ERR_ALREADY_EXISTS,
- 1008 => IPF_ORM::ERR_CANNOT_DROP,
- 1022 => IPF_ORM::ERR_ALREADY_EXISTS,
- 1044 => IPF_ORM::ERR_ACCESS_VIOLATION,
- 1046 => IPF_ORM::ERR_NODBSELECTED,
- 1048 => IPF_ORM::ERR_CONSTRAINT,
- 1049 => IPF_ORM::ERR_NOSUCHDB,
- 1050 => IPF_ORM::ERR_ALREADY_EXISTS,
- 1051 => IPF_ORM::ERR_NOSUCHTABLE,
- 1054 => IPF_ORM::ERR_NOSUCHFIELD,
- 1061 => IPF_ORM::ERR_ALREADY_EXISTS,
- 1062 => IPF_ORM::ERR_ALREADY_EXISTS,
- 1064 => IPF_ORM::ERR_SYNTAX,
- 1091 => IPF_ORM::ERR_NOT_FOUND,
- 1100 => IPF_ORM::ERR_NOT_LOCKED,
- 1136 => IPF_ORM::ERR_VALUE_COUNT_ON_ROW,
- 1142 => IPF_ORM::ERR_ACCESS_VIOLATION,
- 1146 => IPF_ORM::ERR_NOSUCHTABLE,
- 1216 => IPF_ORM::ERR_CONSTRAINT,
- 1217 => IPF_ORM::ERR_CONSTRAINT,
- 1451 => IPF_ORM::ERR_CONSTRAINT,
- );
- public function processErrorInfo(array $errorInfo)
- {
- $code = $errorInfo[1];
- if (isset(self::$errorCodeMap[$code])) {
- $this->portableCode = self::$errorCodeMap[$code];
- return true;
- }
- return false;
- }
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_Exception_Profiler extends IPF_ORM_Exception{}
+++ /dev/null
-<?php
-
-class IPF_ORM_Exception_Validator extends IPF_ORM_Exception
-{
- private $invalid = array();
-
- public function __construct(array $invalid)
- {
- $this->invalid = $invalid;
- parent::__construct($this->generateMessage());
- }
-
- public function getInvalidRecords()
- {
- return $this->invalid;
- }
-
- private function generateMessage()
- {
- $message = "";
- foreach ($this->invalid as $record) {
- $errors = array();
- foreach ($record->getErrors() as $field => $validators)
- $errors[] = 'Field "' . $field . '" failed following validators: ' . implode(', ', $validators) . '.';
- $message .= "Validaton error in class " . get_class($record) . ' (' . implode(' ', $errors) . ') ';
- }
- return $message;
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Export extends IPF_ORM_Connection_Module
-{
- protected $valid_default_values = array(
- 'text' => '',
- 'boolean' => true,
- 'integer' => 0,
- 'decimal' => 0.0,
- 'float' => 0.0,
- 'double' => 0.0,
- 'timestamp' => '1970-01-01 00:00:00',
- 'time' => '00:00:00',
- 'date' => '1970-01-01',
- 'clob' => '',
- 'blob' => '',
- 'string' => ''
- );
-
- protected function getIndexName($name)
- {
- return $name . '_idx';
- }
-
- public function dropTableSql($table)
- {
- return 'DROP TABLE ' . $this->conn->quoteIdentifier($table);
- }
-
- public function dropTable($table)
- {
- $this->conn->execute($this->dropTableSql($table));
- }
-
- public function dropIndex($table, $name)
- {
- return $this->conn->exec($this->dropIndexSql($table, $name));
- }
-
- public function dropIndexSql($table, $name)
- {
- $name = $this->conn->quoteIdentifier($this->getIndexName($name));
-
- return 'DROP INDEX ' . $name;
- }
-
- public function dropConstraint($table, $name, $primary = false)
- {
- $table = $this->conn->quoteIdentifier($table);
- $name = $this->conn->quoteIdentifier($name);
-
- return $this->conn->exec('ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $name);
- }
-
- public function dropForeignKey($table, $name)
- {
- return $this->dropConstraint($table, $name);
- }
-
- public function createConstraint($table, $name, $definition)
- {
- $sql = $this->createConstraintSql($table, $name, $definition);
-
- return $this->conn->exec($sql);
- }
-
- public function createConstraintSql($table, $name, $definition)
- {
- $table = $this->conn->quoteIdentifier($table);
- $name = $this->conn->quoteIdentifier($this->getIndexName($name));
- $query = 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $name;
-
- if (isset($definition['primary']) && $definition['primary']) {
- $query .= ' PRIMARY KEY';
- } elseif (isset($definition['unique']) && $definition['unique']) {
- $query .= ' UNIQUE';
- }
-
- $fields = array();
- foreach (array_keys($definition['fields']) as $field) {
- $fields[] = $this->conn->quoteIdentifier($field);
- }
- $query .= ' ('. implode(', ', $fields) . ')';
-
- return $query;
- }
-
- public function createIndex($table, $name, array $definition)
- {
- return $this->conn->execute($this->createIndexSql($table, $name, $definition));
- }
-
- public function createIndexSql($table, $name, array $definition)
- {
- $table = $this->conn->quoteIdentifier($table);
- $name = $this->conn->quoteIdentifier($name);
- $type = '';
-
- if (isset($definition['type'])) {
- switch (strtolower($definition['type'])) {
- case 'unique':
- $type = strtoupper($definition['type']) . ' ';
- break;
- default:
- throw new IPF_ORM_Exception('Unknown index type ' . $definition['type']);
- }
- }
-
- $query = 'CREATE ' . $type . 'INDEX ' . $name . ' ON ' . $table;
-
- $fields = array();
- foreach ($definition['fields'] as $field) {
- $fields[] = $this->conn->quoteIdentifier($field);
- }
- $query .= ' (' . implode(', ', $fields) . ')';
-
- return $query;
- }
-
- public function createForeignKeySql($table, array $definition)
- {
- $table = $this->conn->quoteIdentifier($table);
-
- $query = 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclaration($definition);
-
- return $query;
- }
-
- public function createForeignKey($table, array $definition)
- {
- $sql = $this->createForeignKeySql($table, $definition);
-
- return $this->conn->execute($sql);
- }
-
- public function alterTable($name, array $changes, $check = false)
- {
- $sql = $this->alterTableSql($name, $changes, $check);
-
- if (is_string($sql) && $sql) {
- $this->conn->execute($sql);
- }
- }
-
- public function alterTableSql($name, array $changes, $check = false)
- {
- throw new IPF_ORM_Exception('Alter table not supported by this driver.');
- }
-
- public function getFieldDeclarationList(array $fields)
- {
- foreach ($fields as $fieldName => $field) {
- $query = $this->getDeclaration($fieldName, $field);
-
- $queryFields[] = $query;
- }
- return implode(', ', $queryFields);
- }
-
- public function getCheckDeclaration(array $definition)
- {
- $constraints = array();
- foreach ($definition as $field => $def) {
- if (is_string($def)) {
- $constraints[] = 'CHECK (' . $def . ')';
- } else {
- if (isset($def['min'])) {
- $constraints[] = 'CHECK (' . $field . ' >= ' . $def['min'] . ')';
- }
-
- if (isset($def['max'])) {
- $constraints[] = 'CHECK (' . $field . ' <= ' . $def['max'] . ')';
- }
- }
- }
-
- return implode(', ', $constraints);
- }
-
- public function getIndexDeclaration($name, array $definition)
- {
- $name = $this->conn->quoteIdentifier($name);
- $type = '';
-
- if (isset($definition['type'])) {
- if (strtolower($definition['type']) == 'unique') {
- $type = strtoupper($definition['type']) . ' ';
- } else {
- throw new IPF_ORM_Exception('Unknown index type ' . $definition['type']);
- }
- }
-
- if ( ! isset($definition['fields']) || ! is_array($definition['fields'])) {
- throw new IPF_ORM_Exception('No index columns given.');
- }
-
- $query = $type . 'INDEX ' . $name;
-
- $query .= ' (' . $this->getIndexFieldDeclarationList($definition['fields']) . ')';
-
- return $query;
- }
-
- public function getIndexFieldDeclarationList(array $fields)
- {
- $ret = array();
- foreach ($fields as $field => $definition) {
- if (is_array($definition)) {
- $ret[] = $this->conn->quoteIdentifier($field);
- } else {
- $ret[] = $this->conn->quoteIdentifier($definition);
- }
- }
- return implode(', ', $ret);
- }
-
- public function getTemporaryTableQuery()
- {
- return 'TEMPORARY';
- }
-
- public function getForeignKeyDeclaration(array $definition)
- {
- $sql = $this->getForeignKeyBaseDeclaration($definition);
- $sql .= $this->getAdvancedForeignKeyOptions($definition);
- return $sql;
- }
-
- public function getAdvancedForeignKeyOptions(array $definition)
- {
- $query = '';
- if ( ! empty($definition['onUpdate'])) {
- $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialAction($definition['onUpdate']);
- }
- if ( ! empty($definition['onDelete'])) {
- $query .= ' ON DELETE ' . $this->getForeignKeyReferentialAction($definition['onDelete']);
- }
- return $query;
- }
-
- public function getForeignKeyReferentialAction($action)
- {
- $upper = strtoupper($action);
- switch ($upper) {
- case 'CASCADE':
- case 'SET NULL':
- case 'NO ACTION':
- case 'RESTRICT':
- case 'SET DEFAULT':
- return $upper;
- break;
- default:
- throw new IPF_ORM_Exception('Unknown foreign key referential action \'' . $upper . '\' given.');
- }
- }
-
- public function getForeignKeyBaseDeclaration(array $definition)
- {
- $sql = '';
- if (isset($definition['name'])) {
- $sql .= ' CONSTRAINT ' . $this->conn->quoteIdentifier($definition['name']) . ' ';
- }
- $sql .= 'FOREIGN KEY (';
-
- if ( ! isset($definition['local'])) {
- throw new IPF_ORM_Exception('Local reference field missing from definition.');
- }
- if ( ! isset($definition['foreign'])) {
- throw new IPF_ORM_Exception('Foreign reference field missing from definition.');
- }
- if ( ! isset($definition['foreignTable'])) {
- throw new IPF_ORM_Exception('Foreign reference table missing from definition.');
- }
-
- if ( ! is_array($definition['local'])) {
- $definition['local'] = array($definition['local']);
- }
- if ( ! is_array($definition['foreign'])) {
- $definition['foreign'] = array($definition['foreign']);
- }
-
- $sql .= implode(', ', array_map(array($this->conn, 'quoteIdentifier'), $definition['local']))
- . ') REFERENCES '
- . $this->conn->quoteIdentifier($definition['foreignTable']) . '('
- . implode(', ', array_map(array($this->conn, 'quoteIdentifier'), $definition['foreign'])) . ')';
-
- return $sql;
- }
-
- public function exportSortedClassesSql($classes, $groupByConnection = true)
- {
- $connections = array();
- foreach ($classes as $class) {
- $connection = IPF_ORM_Manager::connection();
- $connectionName = $connection->getName();
-
- if ( ! isset($connections[$connectionName])) {
- $connections[$connectionName] = array(
- 'create_tables' => array(),
- 'create_indexes' => array(),
- 'alters' => array()
- );
- }
-
- $sql = $this->exportClassesSql($class);
-
- // Build array of all the creates
- // We need these to happen first
- foreach ($sql as $key => $query) {
- // If create table statement
- if (substr($query, 0, strlen('CREATE TABLE')) == 'CREATE TABLE') {
- $connections[$connectionName]['create_tables'][] = $query;
-
- unset($sql[$key]);
- continue;
- }
-
- // If create index statement
- if (preg_grep("/CREATE .* INDEX/", array($query))) {
- $connections[$connectionName]['create_indexes'][] = $query;
-
- unset($sql[$key]);
- continue;
- }
-
- // If alter table statement
- if (substr($query, 0, strlen('ALTER TABLE')) == 'ALTER TABLE') {
- $connections[$connectionName]['alters'][] = $query;
-
- unset($sql[$key]);
- continue;
- }
- }
- }
-
- // Loop over all the sql again to merge everything together so it is in the correct order
- $build = array();
- foreach ($connections as $connectionName => $sql) {
- $build[$connectionName] = array_merge($sql['create_tables'], $sql['create_indexes'], $sql['alters']);
- }
-
- if ( ! $groupByConnection) {
- $new = array();
- foreach($build as $connectionname => $sql) {
- $new = array_merge($new, $sql);
- }
- $build = $new;
- }
- return $build;
- }
-
- public function exportClasses(array $classes)
- {
- $queries = $this->exportSortedClassesSql($classes);
-
- foreach ($queries as $connectionName => $sql) {
- $connection = IPF_ORM_Manager::connection();
-
- $connection->beginTransaction();
-
- foreach ($sql as $query) {
- try {
- $connection->exec($query);
- } catch (IPF_ORM_Exception $e) {
- // we only want to silence table already exists errors
- if ($e->getPortableCode() !== IPF_ORM::ERR_ALREADY_EXISTS) {
- $connection->rollback();
- throw new IPF_ORM_Exception($e->getMessage() . '. Failing Query: ' . $query);
- }
- }
- }
- $connection->commit();
- }
- }
-
- public function exportClassesSql($name)
- {
- $sql = array();
-
- $table = IPF_ORM::getTable($name);
-
- $data = $this->getExportableFormat($table);
-
- $query = $this->createTableSql($data['tableName'], $data['columns'], $data['options']);
-
- if (is_array($query)) {
- $sql = array_merge($sql, $query);
- } else {
- $sql[] = $query;
- }
-
- $sql = array_unique($sql);
-
- rsort($sql);
-
- return $sql;
- }
-
- private function getExportableFormat($table)
- {
- $columns = array();
- $primary = array();
-
- foreach ($table->getColumns() as $name => $definition) {
-
- if (isset($definition['owner'])) {
- continue;
- }
-
- switch ($definition['type']) {
- case 'enum':
- if (isset($definition['default'])) {
- $definition['default'] = $table->enumIndex($name, $definition['default']);
- }
- break;
- case 'boolean':
- if (isset($definition['default'])) {
- $definition['default'] = $table->getConnection()->convertBooleans($definition['default']);
- }
- break;
- }
- $columns[$name] = $definition;
-
- if (isset($definition['primary']) && $definition['primary']) {
- $primary[] = $name;
- }
- }
-
- $options['foreignKeys'] = $table->getOption('foreignKeys', array());
-
- if ($table->getAttribute(IPF_ORM::ATTR_EXPORT) & IPF_ORM::EXPORT_CONSTRAINTS) {
- $constraints = array();
-
- $emptyIntegrity = array('onUpdate' => null,
- 'onDelete' => null);
-
- foreach ($table->getRelations() as $name => $relation) {
- $fk = $relation->toArray();
- $fk['foreignTable'] = $relation->getTable()->getTableName();
-
- if ($relation->getTable() === $table && in_array($relation->getLocal(), $primary)) {
- if ($relation->hasConstraint()) {
- throw new IPF_ORM_Exception("Badly constructed integrity constraints.");
- }
- continue;
- }
-
- $integrity = array('onUpdate' => $fk['onUpdate'],
- 'onDelete' => $fk['onDelete']);
-
- if ($relation instanceof IPF_ORM_Relation_LocalKey) {
- $def = array('local' => $relation->getLocal(),
- 'foreign' => $relation->getForeign(),
- 'foreignTable' => $relation->getTable()->getTableName());
-
- if (($key = array_search($def, $options['foreignKeys'])) === false) {
- $options['foreignKeys'][] = $def;
- $constraints[] = $integrity;
- } else {
- if ($integrity !== $emptyIntegrity) {
- $constraints[$key] = $integrity;
- }
- }
- }
- }
-
- foreach ($constraints as $k => $def) {
- $options['foreignKeys'][$k] = array_merge($options['foreignKeys'][$k], $def);
- }
- }
-
- $options['primary'] = $primary;
-
- return array('tableName' => $table->getOption('tableName'),
- 'columns' => $columns,
- 'options' => array_merge($table->getOptions(), $options));
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Export_Mysql extends IPF_ORM_Export
-{
- public function createTableSql($name, array $fields, array $options = array())
- {
- if ( ! $name)
- throw new IPF_ORM_Exception('no valid table name specified');
-
- if (empty($fields)) {
- throw new IPF_ORM_Exception('no fields specified for table "'.$name.'"');
- }
- $queryFields = $this->getFieldDeclarationList($fields);
-
- // build indexes for all foreign key fields (needed in MySQL!!)
- if (isset($options['foreignKeys'])) {
- foreach ($options['foreignKeys'] as $fk) {
- $local = $fk['local'];
- $found = false;
- if (isset($options['indexes'])) {
- foreach ($options['indexes'] as $definition) {
- if (is_string($definition['fields'])) {
- // Check if index already exists on the column
- $found = ($local == $definition['fields']);
- } else if (in_array($local, $definition['fields']) && count($definition['fields']) === 1) {
- // Index already exists on the column
- $found = true;
- }
- }
- }
- if (isset($options['primary']) && !empty($options['primary']) &&
- in_array($local, $options['primary'])) {
- // field is part of the PK and therefore already indexed
- $found = true;
- }
-
- if ( ! $found) {
- if (is_array($local)) {
- foreach($local as $localidx) {
- $options['indexes'][$localidx] = array('fields' => array($localidx => array()));
- }
- } else {
- $options['indexes'][$local] = array('fields' => array($local => array()));
- }
- }
- }
- }
-
- // add all indexes
- if (isset($options['indexes']) && ! empty($options['indexes'])) {
- foreach($options['indexes'] as $index => $definition) {
- $queryFields .= ', ' . $this->getIndexDeclaration($index, $definition);
- }
- }
-
- // attach all primary keys
- if (isset($options['primary']) && ! empty($options['primary'])) {
- $keyColumns = array_values($options['primary']);
- $keyColumns = array_map(array($this->conn, 'quoteIdentifier'), $keyColumns);
- $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')';
- }
-
- $query = 'CREATE TABLE ' . $this->conn->quoteIdentifier($name) . ' (' . $queryFields . ')';
-
- $optionStrings = array();
-
- if (isset($options['comment'])) {
- $optionStrings['comment'] = 'COMMENT = ' . $this->conn->quote($options['comment'], 'text');
- }
- if (isset($options['charset'])) {
- $optionStrings['charset'] = 'DEFAULT CHARACTER SET ' . $options['charset'];
- }
- if (isset($options['collate'])) {
- $optionStrings['collate'] = 'COLLATE ' . $options['collate'];
- }
-
- $type = false;
-
- // get the type of the table
- if (isset($options['type'])) {
- $type = $options['type'];
- } else {
- $type = $this->conn->getAttribute(IPF_ORM::ATTR_DEFAULT_TABLE_TYPE);
- }
-
- if ($type) {
- $optionStrings[] = 'ENGINE = ' . $type;
- }
-
- if ( ! empty($optionStrings)) {
- $query.= ' '.implode(' ', $optionStrings);
- }
- $sql[] = $query;
-
- if (isset($options['foreignKeys'])) {
-
- foreach ((array) $options['foreignKeys'] as $k => $definition) {
- if (is_array($definition)) {
- $sql[] = $this->createForeignKeySql($name, $definition);
- }
- }
- }
- return $sql;
- }
-
- public function getDeclaration($name, array $field)
- {
- $declaration = $this->conn->quoteIdentifier($name) . ' ';
-
- if (!isset($field['type']))
- throw new IPF_ORM_Exception('Missing column type.');
-
- switch ($field['type']) {
- case 'char':
- $length = ( ! empty($field['length'])) ? $field['length'] : false;
-
- $declaration .= $length ? 'CHAR('.$length.')' : 'CHAR(255)';
- break;
- case 'varchar':
- case 'array':
- case 'object':
- case 'string':
- case 'gzip':
- if (!isset($field['length'])) {
- if (array_key_exists('default', $field)) {
- $field['length'] = $this->conn->varchar_max_length;
- } else {
- $field['length'] = false;
- }
- }
-
- $length = ($field['length'] <= $this->conn->varchar_max_length) ? $field['length'] : false;
- $fixed = (isset($field['fixed'])) ? $field['fixed'] : false;
-
- $declaration .= $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)')
- : ($length ? 'VARCHAR(' . $length . ')' : 'TEXT');
- break;
- case 'clob':
- if (!empty($field['length'])) {
- $length = $field['length'];
- if ($length <= 255) {
- return 'TINYTEXT';
- } elseif ($length <= 65532) {
- return 'TEXT';
- } elseif ($length <= 16777215) {
- return 'MEDIUMTEXT';
- }
- }
- $declaration .= 'LONGTEXT';
- break;
- case 'blob':
- if ( ! empty($field['length'])) {
- $length = $field['length'];
- if ($length <= 255) {
- return 'TINYBLOB';
- } elseif ($length <= 65532) {
- return 'BLOB';
- } elseif ($length <= 16777215) {
- return 'MEDIUMBLOB';
- }
- }
- $declaration .= 'LONGBLOB';
- break;
- case 'enum':
- if ($this->conn->getAttribute(IPF_ORM::ATTR_USE_NATIVE_ENUM)) {
- $values = array();
- foreach ($field['values'] as $value) {
- $values[] = $this->conn->quote($value, 'varchar');
- }
- $declaration .= 'ENUM('.implode(', ', $values).')';
- break;
- }
- // fall back to integer
- case 'integer':
- case 'int':
- $type = 'INT';
- if (!empty($field['length'])) {
- $length = $field['length'];
- if ($length <= 1) {
- $type = 'TINYINT';
- } elseif ($length == 2) {
- $type = 'SMALLINT';
- } elseif ($length == 3) {
- $type = 'MEDIUMINT';
- } elseif ($length == 4) {
- $type = 'INT';
- } elseif ($length > 4) {
- $type = 'BIGINT';
- }
- }
- $declaration .= $type;
- if (isset($field['unsigned']) && $field['unsigned'])
- $declaration .= ' UNSIGNED';
- break;
- case 'boolean':
- $declaration .= 'TINYINT(1)';
- break;
- case 'date':
- $declaration .= 'DATE';
- break;
- case 'time':
- $declaration .= 'TIME';
- break;
- case 'datetime':
- $declaration .= 'DATETIME';
- break;
- case 'timestamp':
- $declaration .= 'TIMESTAMP';
- break;
- case 'float':
- case 'double':
- $declaration .= 'DOUBLE';
- break;
- case 'decimal':
- $scale = !empty($field['scale']) ? $field['scale'] : $this->conn->getAttribute(IPF_ORM::ATTR_DECIMAL_PLACES);
- if (!empty($field['length'])) {
- $length = $field['length'];
- if (is_array($length)) {
- list($length, $scale) = $length;
- }
- } else {
- $length = 18;
- }
- $declaration .= 'DECIMAL('.$length.','.$scale.')';
- break;
- case 'bit':
- $declaration .= 'BIT';
- break;
- default:
- throw new IPF_ORM_Exception('Unknown field type \'' . $field['type'] . '\'.');
- }
-
- if (isset($field['charset']) && $field['charset'])
- $declaration .= ' CHARACTER SET ' . $field['charset'];
-
- if (isset($field['collate']) && $field['collate'])
- $declaration .= ' COLLATE ' . $field['collate'];
-
- if (isset($field['notnull']) && $field['notnull'])
- $declaration .= ' NOT NULL';
-
- if (!empty($field['autoincrement'])) {
- $declaration .= ' AUTO_INCREMENT';
- } else {
- $declaration .= $this->getDefaultFieldDeclaration($field);
-
- if (isset($field['unique']) && $field['unique'])
- $declaration .= ' UNIQUE';
- }
-
- if (isset($field['comment']) && $field['comment'])
- $declaration .= ' COMMENT ' . $this->conn->quote($field['comment'], 'varchar');
-
- return $declaration;
- }
-
- private function getDefaultFieldDeclaration($field)
- {
- $default = '';
- if (isset($field['default']) && (!isset($field['length']) || $field['length'] <= 255)) {
- if ($field['default'] === '') {
- $field['default'] = empty($field['notnull']) ? null : $this->valid_default_values[$field['type']];
- }
-
- if ($field['default'] === '' && ($this->conn->getAttribute(IPF_ORM::ATTR_PORTABILITY) & IPF_ORM::PORTABILITY_EMPTY_TO_NULL))
- $field['default'] = null;
-
- if (is_null($field['default'])) {
- $default = ' DEFAULT NULL';
- } else {
- if ($field['type'] === 'boolean') {
- $fieldType = 'boolean';
- $field['default'] = $this->conn->convertBooleans($field['default']);
- } elseif ($field['type'] == 'enum' && $this->conn->getAttribute(IPF_ORM::ATTR_USE_NATIVE_ENUM)) {
- $fieldType = 'varchar';
- } else {
- $fieldType = $field['type'];
- }
- $default = ' DEFAULT ' . $this->conn->quote($field['default'], $fieldType);
- }
- }
- return $default;
- }
-
- public function alterTableSql($name, array $changes, $check = false)
- {
- if ( ! $name) {
- throw new IPF_ORM_Exception('no valid table name specified');
- }
- foreach ($changes as $changeName => $change) {
- switch ($changeName) {
- case 'add':
- case 'remove':
- case 'change':
- case 'rename':
- case 'name':
- break;
- default:
- throw new IPF_ORM_Exception('change type "' . $changeName . '" not yet supported');
- }
- }
-
- if ($check) {
- return true;
- }
-
- $query = '';
- if ( ! empty($changes['name'])) {
- $change_name = $this->conn->quoteIdentifier($changes['name']);
- $query .= 'RENAME TO ' . $change_name;
- }
-
- if ( ! empty($changes['add']) && is_array($changes['add'])) {
- foreach ($changes['add'] as $fieldName => $field) {
- if ($query) {
- $query.= ', ';
- }
- $query.= 'ADD ' . $this->getDeclaration($fieldName, $field);
- }
- }
-
- if ( ! empty($changes['remove']) && is_array($changes['remove'])) {
- foreach ($changes['remove'] as $fieldName => $field) {
- if ($query) {
- $query .= ', ';
- }
- $fieldName = $this->conn->quoteIdentifier($fieldName);
- $query .= 'DROP ' . $fieldName;
- }
- }
-
- $rename = array();
- if ( ! empty($changes['rename']) && is_array($changes['rename'])) {
- foreach ($changes['rename'] as $fieldName => $field) {
- $rename[$field['name']] = $fieldName;
- }
- }
-
- if ( ! empty($changes['change']) && is_array($changes['change'])) {
- foreach ($changes['change'] as $fieldName => $field) {
- if ($query) {
- $query.= ', ';
- }
- if (isset($rename[$fieldName])) {
- $oldFieldName = $rename[$fieldName];
- unset($rename[$fieldName]);
- } else {
- $oldFieldName = $fieldName;
- }
- $oldFieldName = $this->conn->quoteIdentifier($oldFieldName);
- $query .= 'CHANGE ' . $oldFieldName . ' '
- . $this->getDeclaration($fieldName, $field['definition']);
- }
- }
-
- if ( ! empty($rename) && is_array($rename)) {
- foreach ($rename as $renameName => $renamedField) {
- if ($query) {
- $query.= ', ';
- }
- $field = $changes['rename'][$renamedField];
- $renamedField = $this->conn->quoteIdentifier($renamedField);
- $query .= 'CHANGE ' . $renamedField . ' '
- . $this->getDeclaration($field['name'], $field['definition']);
- }
- }
-
- if ( ! $query) {
- return false;
- }
-
- $name = $this->conn->quoteIdentifier($name);
-
- return 'ALTER TABLE ' . $name . ' ' . $query;
- }
-
- public function createIndexSql($table, $name, array $definition)
- {
- $name = $this->conn->quoteIdentifier($this->getIndexName($name));
- $type = '';
- if (isset($definition['type'])) {
- switch (strtolower($definition['type'])) {
- case 'fulltext':
- case 'unique':
- $type = strtoupper($definition['type']) . ' ';
- break;
- default:
- throw new IPF_ORM_Exception('Unknown index type ' . $definition['type']);
- }
- }
- $query = 'CREATE ' . $type . 'INDEX ' . $name . ' ON ' . $table;
- $query .= ' (' . $this->getIndexFieldDeclarationList($definition['fields']) . ')';
-
- return $query;
- }
-
- public function getIndexDeclaration($name, array $definition)
- {
- $name = $this->getIndexName($name);
- $type = '';
- if (isset($definition['type'])) {
- switch (strtolower($definition['type'])) {
- case 'fulltext':
- case 'unique':
- $type = strtoupper($definition['type']) . ' ';
- break;
- default:
- throw new IPF_ORM_Exception('Unknown index type ' . $definition['type']);
- }
- }
-
- if ( ! isset($definition['fields'])) {
- throw new IPF_ORM_Exception('No index columns given.');
- }
- if ( ! is_array($definition['fields'])) {
- $definition['fields'] = array($definition['fields']);
- }
-
- $query = $type . 'INDEX ' . $this->conn->quoteIdentifier($name);
-
- $query .= ' (' . $this->getIndexFieldDeclarationList($definition['fields']) . ')';
-
- return $query;
- }
-
- public function getIndexFieldDeclarationList(array $fields)
- {
- $declFields = array();
-
- foreach ($fields as $fieldName => $field) {
- $fieldString = $this->conn->quoteIdentifier($fieldName);
-
- if (is_array($field)) {
- if (isset($field['length'])) {
- $fieldString .= '(' . $field['length'] . ')';
- }
-
- if (isset($field['sorting'])) {
- $sort = strtoupper($field['sorting']);
- switch ($sort) {
- case 'ASC':
- case 'DESC':
- $fieldString .= ' ' . $sort;
- break;
- default:
- throw new IPF_ORM_Exception('Unknown index sorting option given.');
- }
- }
- } else {
- $fieldString = $this->conn->quoteIdentifier($field);
- }
- $declFields[] = $fieldString;
- }
- return implode(', ', $declFields);
- }
-
- public function getAdvancedForeignKeyOptions(array $definition)
- {
- $query = '';
- if ( ! empty($definition['match'])) {
- $query .= ' MATCH ' . $definition['match'];
- }
- if ( ! empty($definition['onUpdate'])) {
- $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialAction($definition['onUpdate']);
- }
- if ( ! empty($definition['onDelete'])) {
- $query .= ' ON DELETE ' . $this->getForeignKeyReferentialAction($definition['onDelete']);
- }
- return $query;
- }
-
- public function dropIndexSql($table, $name)
- {
- $table = $this->conn->quoteIdentifier($table);
- $name = $this->conn->quoteIdentifier($this->getIndexName($name));
- return 'DROP INDEX ' . $name . ' ON ' . $table;
- }
-
- public function dropTableSql($table)
- {
- $table = $this->conn->quoteIdentifier($table);
- return 'DROP TABLE ' . $table;
- }
-
- public function dropForeignKey($table, $name)
- {
- $table = $this->conn->quoteIdentifier($table);
- $name = $this->conn->quoteIdentifier($name);
-
- return $this->conn->exec('ALTER TABLE ' . $table . ' DROP FOREIGN KEY ' . $name);
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Expression
-{
- protected $_expression;
- protected $_conn;
- protected $_tokenizer;
-
- public function __construct($expr, $conn = null)
- {
- $this->_tokenizer = new IPF_ORM_Query_Tokenizer();
- $this->setExpression($expr);
- if ($conn !== null) {
- $this->_conn = $conn;
- }
- }
-
- public function getConnection()
- {
- if ( ! isset($this->_conn)) {
- return IPF_ORM_Manager::connection();
- }
-
- return $this->_conn;
- }
-
- public function setExpression($clause)
- {
- $this->_expression = $this->parseClause($clause);
- }
-
- public function parseExpression($expr)
- {
- $pos = strpos($expr, '(');
- $quoted = (substr($expr, 0, 1) === "'" && substr($expr, -1) === "'");
- if ($pos === false || $quoted) {
- return $expr;
- }
-
- // get the name of the function
- $name = substr($expr, 0, $pos);
- $argStr = substr($expr, ($pos + 1), -1);
-
- // parse args
- foreach ($this->_tokenizer->bracketExplode($argStr, ',') as $arg) {
- $args[] = $this->parseClause($arg);
- }
-
- return call_user_func_array(array($this->getConnection()->expression, $name), $args);
- }
-
- public function parseClause($clause)
- {
- $e = $this->_tokenizer->bracketExplode($clause, ' ');
-
- foreach ($e as $k => $expr) {
- $e[$k] = $this->parseExpression($expr);
- }
-
- return implode(' ', $e);
- }
-
- public function getSql()
- {
- return $this->_expression;
- }
-
- public function __toString()
- {
- return $this->getSql();
- }
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_Expression_Driver extends IPF_ORM_Connection_Module
-{
- public function getIdentifier($column)
- {
- return $column;
- }
- public function getIdentifiers($columns)
- {
- return $columns;
- }
-
- public function regexp()
- {
- throw new IPF_ORM_Exception('Regular expression operator is not supported by this database driver.');
- }
-
- public function avg($column)
- {
- $column = $this->getIdentifier($column);
- return 'AVG(' . $column . ')';
- }
-
- public function count($column)
- {
- $column = $this->getIdentifier($column);
- return 'COUNT(' . $column . ')';
- }
-
- public function max($column)
- {
- $column = $this->getIdentifier($column);
- return 'MAX(' . $column . ')';
- }
-
- public function min($column)
- {
- $column = $this->getIdentifier($column);
- return 'MIN(' . $column . ')';
- }
-
- public function sum($column)
- {
- $column = $this->getIdentifier($column);
- return 'SUM(' . $column . ')';
- }
-
- public function md5($column)
- {
- $column = $this->getIdentifier($column);
- return 'MD5(' . $column . ')';
- }
-
- public function length($column)
- {
- $column = $this->getIdentifier($column);
- return 'LENGTH(' . $column . ')';
- }
-
- public function round($column, $decimals = 0)
- {
- $column = $this->getIdentifier($column);
-
- return 'ROUND(' . $column . ', ' . $decimals . ')';
- }
-
- public function mod($expression1, $expression2)
- {
- $expression1 = $this->getIdentifier($expression1);
- $expression2 = $this->getIdentifier($expression2);
- return 'MOD(' . $expression1 . ', ' . $expression2 . ')';
- }
-
- public function trim($str)
- {
- return 'TRIM(' . $str . ')';
- }
-
- public function rtrim($str)
- {
- return 'RTRIM(' . $str . ')';
- }
-
- public function ltrim($str)
- {
- return 'LTRIM(' . $str . ')';
- }
-
- public function upper($str)
- {
- return 'UPPER(' . $str . ')';
- }
-
- public function lower($str)
- {
- return 'LOWER(' . $str . ')';
- }
-
- public function locate($str, $substr)
- {
- return 'LOCATE(' . $str . ', ' . $substr . ')';
- }
-
- public function now()
- {
- return 'NOW()';
- }
-
- public function coalesce($expression1, $expression2)
- {
- $expression1 = $this->getIdentifier($expression1);
- $expression2 = $this->getIdentifier($expression2);
- return 'COALESCE(' . $expression1 . ', ' . $expression2 . ')';
- }
-
- public function soundex($value)
- {
- throw new IPF_ORM_Exception('SQL soundex function not supported by this driver.');
- }
-
- public function substring($value, $from, $len = null)
- {
- $value = $this->getIdentifier($value);
- if ($len === null)
- return 'SUBSTRING(' . $value . ' FROM ' . $from . ')';
- else {
- $len = $this->getIdentifier($len);
- return 'SUBSTRING(' . $value . ' FROM ' . $from . ' FOR ' . $len . ')';
- }
- }
-
- public function concat()
- {
- $args = func_get_args();
-
- return 'CONCAT(' . join(', ', (array) $args) . ')';
- }
-
- public function not($expression)
- {
- $expression = $this->getIdentifier($expression);
- return 'NOT(' . $expression . ')';
- }
-
- private function basicMath($type, array $args)
- {
- $elements = $this->getIdentifiers($args);
- if (count($elements) < 1) {
- return '';
- }
- if (count($elements) == 1) {
- return $elements[0];
- } else {
- return '(' . implode(' ' . $type . ' ', $elements) . ')';
- }
- }
-
- public function add(array $args)
- {
- return $this->basicMath('+', $args);
- }
-
- public function sub(array $args)
- {
- return $this->basicMath('-', $args );
- }
-
- public function mul(array $args)
- {
- return $this->basicMath('*', $args);
- }
-
- public function div(array $args)
- {
- return $this->basicMath('/', $args);
- }
-
- public function eq($value1, $value2)
- {
- $value1 = $this->getIdentifier($value1);
- $value2 = $this->getIdentifier($value2);
- return $value1 . ' = ' . $value2;
- }
-
- public function neq($value1, $value2)
- {
- $value1 = $this->getIdentifier($value1);
- $value2 = $this->getIdentifier($value2);
- return $value1 . ' <> ' . $value2;
- }
-
- public function gt($value1, $value2)
- {
- $value1 = $this->getIdentifier($value1);
- $value2 = $this->getIdentifier($value2);
- return $value1 . ' > ' . $value2;
- }
-
- public function gte($value1, $value2)
- {
- $value1 = $this->getIdentifier($value1);
- $value2 = $this->getIdentifier($value2);
- return $value1 . ' >= ' . $value2;
- }
-
- public function lt($value1, $value2)
- {
- $value1 = $this->getIdentifier($value1);
- $value2 = $this->getIdentifier($value2);
- return $value1 . ' < ' . $value2;
- }
-
- public function lte($value1, $value2)
- {
- $value1 = $this->getIdentifier($value1);
- $value2 = $this->getIdentifier($value2);
- return $value1 . ' <= ' . $value2;
- }
-
- public function in($column, $values)
- {
- if ( ! is_array($values)) {
- $values = array($values);
- }
- $values = $this->getIdentifiers($values);
- $column = $this->getIdentifier($column);
-
- if (count($values) == 0) {
- throw new IPF_ORM_Exception('Values array for IN operator should not be empty.');
- }
- return $column . ' IN (' . implode(', ', $values) . ')';
- }
-
- public function isNull($expression)
- {
- $expression = $this->getIdentifier($expression);
- return $expression . ' IS NULL';
- }
-
- public function isNotNull($expression)
- {
- $expression = $this->getIdentifier($expression);
- return $expression . ' IS NOT NULL';
- }
-
- public function between($expression, $value1, $value2)
- {
- $expression = $this->getIdentifier($expression);
- $value1 = $this->getIdentifier($value1);
- $value2 = $this->getIdentifier($value2);
- return $expression . ' BETWEEN ' .$value1 . ' AND ' . $value2;
- }
-
- public function guid()
- {
- throw new IPF_ORM_Exception('method not implemented');
- }
-
- public function acos($value)
- {
- return 'ACOS(' . $value . ')';
- }
-
- public function sin($value)
- {
- return 'SIN(' . $value . ')';
- }
-
- public function pi()
- {
- return 'PI()';
- }
-
- public function cos($value)
- {
- return 'COS(' . $value . ')';
- }
-
- public function __call($m, $a)
- {
- if ($this->conn->getAttribute(IPF_ORM::ATTR_PORTABILITY) & IPF_ORM::PORTABILITY_EXPR) {
- throw new IPF_ORM_Exception('Unknown expression ' . $m);
- }
- return $m . '(' . implode(', ', $a) . ')';
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Expression_Mysql extends IPF_ORM_Expression_Driver
-{
- public function regexp()
- {
- return 'RLIKE';
- }
-
- public function random()
- {
- return 'RAND()';
- }
-
- public function guid()
- {
- return 'UUID()';
- }
-
- public function year($column)
- {
- $column = $this->getIdentifier($column);
- return 'YEAR(' . $column . ')';
- }
-
- public function month($column)
- {
- $column = $this->getIdentifier($column);
- return 'MONTH(' . $column . ')';
- }
-
- public function monthname($column)
- {
- $column = $this->getIdentifier($column);
- return 'MONTHNAME(' . $column . ')';
- }
-
- public function day($column)
- {
- $column = $this->getIdentifier($column);
- return 'DAY(' . $column . ')';
- }
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_Hydrator extends IPF_ORM_Hydrator_Abstract
-{
- public function hydrateResultSet($stmt, $tableAliases)
- {
- $hydrationMode = $this->_hydrationMode;
-
- $this->_tableAliases = $tableAliases;
-
- if ($hydrationMode == IPF_ORM::HYDRATE_NONE) {
- return $stmt->fetchAll(PDO::FETCH_NUM);
- }
-
- if ($hydrationMode == IPF_ORM::HYDRATE_ARRAY) {
- $driver = new IPF_ORM_Hydrator_ArrayDriver();
- } else {
- $driver = new IPF_ORM_Hydrator_RecordDriver();
- }
-
- // Used variables during hydration
- reset($this->_queryComponents);
- $rootAlias = key($this->_queryComponents);
- $rootComponentName = $this->_queryComponents[$rootAlias]['table']->getComponentName();
- // if only one component is involved we can make our lives easier
- $isSimpleQuery = count($this->_queryComponents) <= 1;
- // Holds the resulting hydrated data structure
- $result = array();
- // Lookup map to quickly discover/lookup existing records in the result
- $identifierMap = array();
- // Holds for each component the last previously seen element in the result set
- $prev = array();
- // holds the values of the identifier/primary key fields of components,
- // separated by a pipe '|' and grouped by component alias (r, u, i, ... whatever)
- // the $idTemplate is a prepared template. $id is set to a fresh template when
- // starting to process a row.
- $id = array();
- $idTemplate = array();
-
- $result = $driver->getElementCollection($rootComponentName);
-
- if ($stmt === false || $stmt === 0) {
- return $result;
- }
-
- // Initialize
- foreach ($this->_queryComponents as $dqlAlias => $data) {
- $componentName = $data['table']->getComponentName();
- $identifierMap[$dqlAlias] = array();
- $prev[$dqlAlias] = null;
- $idTemplate[$dqlAlias] = '';
- }
-
- $event = new IPF_ORM_Event(null, IPF_ORM_Event::HYDRATE, null);
-
- // Process result set
- $cache = array();
- while ($data = $stmt->fetch(IPF_ORM::FETCH_ASSOC)) {
- $id = $idTemplate; // initialize the id-memory
- $nonemptyComponents = array();
- $rowData = $this->_gatherRowData($data, $cache, $id, $nonemptyComponents);
-
- //
- // hydrate the data of the root component from the current row
- //
- $table = $this->_queryComponents[$rootAlias]['table'];
- $componentName = $table->getComponentName();
- // Ticket #1115 (getInvoker() should return the component that has addEventListener)
- $event->setInvoker($table);
- $event->set('data', $rowData[$rootAlias]);
- $table->notifyRecordListeners('preHydrate', $event);
-
- $index = false;
-
- // Check for an existing element
- if ($isSimpleQuery || ! isset($identifierMap[$rootAlias][$id[$rootAlias]])) {
- $element = $driver->getElement($rowData[$rootAlias], $componentName);
- $event->set('data', $element);
- $table->notifyRecordListeners('postHydrate', $event);
-
- // do we need to index by a custom field?
- if ($field = $this->_getCustomIndexField($rootAlias)) {
- if (isset($result[$field])) {
- throw new IPF_ORM_Exception("Couldn't hydrate. Found non-unique key mapping.");
- } else if ( ! isset($element[$field])) {
- throw new IPF_ORM_Exception("Couldn't hydrate. Found a non-existent key.");
- }
- $result[$element[$field]] = $element;
- } else {
- $result[] = $element;
- }
-
- $identifierMap[$rootAlias][$id[$rootAlias]] = $driver->getLastKey($result);
- } else {
- $index = $identifierMap[$rootAlias][$id[$rootAlias]];
- }
-
- $this->_setLastElement($prev, $result, $index, $rootAlias, false);
- unset($rowData[$rootAlias]);
-
- // end hydrate data of the root component for the current row
-
-
- // $prev[$rootAlias] now points to the last element in $result.
- // now hydrate the rest of the data found in the current row, that belongs to other
- // (related) components.
- foreach ($rowData as $dqlAlias => $data) {
- $index = false;
- $map = $this->_queryComponents[$dqlAlias];
- $table = $map['table'];
- $componentName = $table->getComponentName();
- $event->set('data', $data);
- $table->notifyRecordListeners('preHydrate', $event);
-
- $parent = $map['parent'];
- $relation = $map['relation'];
- $relationAlias = $map['relation']->getAlias();
-
- $path = $parent . '.' . $dqlAlias;
-
- if ( ! isset($prev[$parent])) {
- unset($prev[$dqlAlias]); // Ticket #1228
- continue;
- }
-
- // check the type of the relation
- if ( ! $relation->isOneToOne() && $driver->initRelated($prev[$parent], $relationAlias)) {
- $oneToOne = false;
- // append element
- if (isset($nonemptyComponents[$dqlAlias])) {
- $indexExists = isset($identifierMap[$path][$id[$parent]][$id[$dqlAlias]]);
- $index = $indexExists ? $identifierMap[$path][$id[$parent]][$id[$dqlAlias]] : false;
- $indexIsValid = $index !== false ? isset($prev[$parent][$relationAlias][$index]) : false;
- if ( ! $indexExists || ! $indexIsValid) {
- $element = $driver->getElement($data, $componentName);
- $event->set('data', $element);
- $table->notifyRecordListeners('postHydrate', $event);
-
- if ($field = $this->_getCustomIndexField($dqlAlias)) {
- if (isset($prev[$parent][$relationAlias][$element[$field]])) {
- throw new IPF_ORM_Exception("Couldn't hydrate. Found non-unique key mapping.");
- } else if ( ! isset($element[$field])) {
- throw new IPF_ORM_Exception("Couldn't hydrate. Found a non-existent key.");
- }
- $prev[$parent][$relationAlias][$element[$field]] = $element;
- } else {
- $prev[$parent][$relationAlias][] = $element;
- }
- $identifierMap[$path][$id[$parent]][$id[$dqlAlias]] = $driver->getLastKey($prev[$parent][$relationAlias]);
- }
- // register collection for later snapshots
- $driver->registerCollection($prev[$parent][$relationAlias]);
- }
- } else {
- // 1-1 relation
- $oneToOne = true;
-
- if ( ! isset($nonemptyComponents[$dqlAlias]) && ! isset($prev[$parent][$relationAlias])) {
- $prev[$parent][$relationAlias] = $driver->getNullPointer();
- } else if ( ! isset($prev[$parent][$relationAlias])) {
- $element = $driver->getElement($data, $componentName);
-
- // [FIX] Tickets #1205 and #1237
- $event->set('data', $element);
- $table->notifyRecordListeners('postHydrate', $event);
-
- $prev[$parent][$relationAlias] = $element;
- }
- }
-
- $coll =& $prev[$parent][$relationAlias];
- $this->_setLastElement($prev, $coll, $index, $dqlAlias, $oneToOne);
- }
- }
-
- $stmt->closeCursor();
- $driver->flush();
- //$e = microtime(true);
- //echo 'Hydration took: ' . ($e - $s) . ' for '.count($result).' records<br />';
-
- return $result;
- }
-
- protected function _setLastElement(&$prev, &$coll, $index, $dqlAlias, $oneToOne)
- {
- if (IPF_ORM_Null::isNull($coll) || $coll === null) {
- unset($prev[$dqlAlias]); // Ticket #1228
- return;
- }
-
- if ($index !== false) {
- // Link element at $index to previous element for the component
- // identified by the DQL alias $alias
- $prev[$dqlAlias] =& $coll[$index];
- return;
- }
-
- if (is_array($coll) && $coll) {
- if ($oneToOne) {
- $prev[$dqlAlias] =& $coll;
- } else {
- end($coll);
- $prev[$dqlAlias] =& $coll[key($coll)];
- }
- } else if (count($coll) > 0) {
- $prev[$dqlAlias] = $coll->getLast();
- }
- }
-
- protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents)
- {
- $rowData = array();
-
- foreach ($data as $key => $value) {
- // Parse each column name only once. Cache the results.
- if ( ! isset($cache[$key])) {
- // check ignored names. fastest solution for now. if we get more we'll start
- // to introduce a list.
- if ($key == 'IPF_ORM_ROWNUM') continue;
- $e = explode('__', $key);
- $last = strtolower(array_pop($e));
- $cache[$key]['dqlAlias'] = $this->_tableAliases[strtolower(implode('__', $e))];
- $table = $this->_queryComponents[$cache[$key]['dqlAlias']]['table'];
- $fieldName = $table->getFieldName($last);
- $cache[$key]['fieldName'] = $fieldName;
- if ($table->isIdentifier($fieldName)) {
- $cache[$key]['isIdentifier'] = true;
- } else {
- $cache[$key]['isIdentifier'] = false;
- }
- $type = $table->getTypeOfColumn($last);
- if ($type == 'integer' || $type == 'string') {
- $cache[$key]['isSimpleType'] = true;
- } else {
- $cache[$key]['type'] = $type;
- $cache[$key]['isSimpleType'] = false;
- }
- }
-
- $map = $this->_queryComponents[$cache[$key]['dqlAlias']];
- $table = $map['table'];
- $dqlAlias = $cache[$key]['dqlAlias'];
- $fieldName = $cache[$key]['fieldName'];
- if (isset($this->_queryComponents[$dqlAlias]['agg'][$fieldName])) {
- $fieldName = $this->_queryComponents[$dqlAlias]['agg'][$fieldName];
- }
-
- if ($cache[$key]['isIdentifier']) {
- $id[$dqlAlias] .= '|' . $value;
- }
-
- if ($cache[$key]['isSimpleType']) {
- $rowData[$dqlAlias][$fieldName] = $value;
- } else {
- $rowData[$dqlAlias][$fieldName] = $table->prepareValue(
- $fieldName, $value, $cache[$key]['type']);
- }
-
- if ( ! isset($nonemptyComponents[$dqlAlias]) && $value !== null) {
- $nonemptyComponents[$dqlAlias] = true;
- }
- }
-
- return $rowData;
- }
-
- protected function _getCustomIndexField($alias)
- {
- return isset($this->_queryComponents[$alias]['map']) ? $this->_queryComponents[$alias]['map'] : null;
- }
-}
+++ /dev/null
-<?php
-
-abstract class IPF_ORM_Hydrator_Abstract
-{
- protected $_queryComponents = array();
-
- protected $_hydrationMode = IPF_ORM::HYDRATE_RECORD;
-
- public function __construct() {}
-
- public function setHydrationMode($hydrationMode)
- {
- $this->_hydrationMode = $hydrationMode;
- }
-
- public function getHydrationMode()
- {
- return $this->_hydrationMode;
- }
-
- public function setQueryComponents(array $queryComponents)
- {
- $this->_queryComponents = $queryComponents;
- }
-
- public function getQueryComponents()
- {
- return $this->_queryComponents;
- }
-
- abstract public function hydrateResultSet($stmt, $tableAliases);
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_Hydrator_ArrayDriver
-{
- public function getElementCollection($component)
- {
- return array();
- }
- public function getElement(array $data, $component)
- {
- return $data;
- }
-
- public function registerCollection($coll)
- {
- }
-
- public function initRelated(array &$data, $name)
- {
- if ( ! isset($data[$name])) {
- $data[$name] = array();
- }
- return true;
- }
-
- public function getNullPointer()
- {
- return null;
- }
-
- public function getLastKey(&$data)
- {
- end($data);
- return key($data);
- }
-
- public function flush()
- {
- }
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_Hydrator_RecordDriver
-{
- protected $_collections = array();
- protected $_tables = array();
- private $_initializedRelations = array();
-
- public function getElementCollection($component)
- {
- $coll = new IPF_ORM_Collection($component);
- $this->_collections[] = $coll;
-
- return $coll;
- }
-
- public function getLastKey($coll)
- {
- $coll->end();
-
- return $coll->key();
- }
-
- public function initRelated(IPF_ORM_Record $record, $name)
- {
- if ( ! isset($this->_initializedRelations[$record->getOid()][$name])) {
- $relation = $record->getTable()->getRelation($name);
- $coll = new IPF_ORM_Collection($relation->getTable()->getComponentName());
- $coll->setReference($record, $relation);
- $record[$name] = $coll;
- $this->_initializedRelations[$record->getOid()][$name] = true;
- }
- return true;
- }
-
- public function registerCollection(IPF_ORM_Collection $coll)
- {
- $this->_collections[] = $coll;
- }
-
- public function getNullPointer()
- {
- return IPF_ORM_Null::getInstance();
- }
-
- public function getElement(array $data, $component)
- {
- $component = $this->_getClassNameToReturn($data, $component);
- if ( ! isset($this->_tables[$component])) {
- $this->_tables[$component] = IPF_ORM::getTable($component);
- $this->_tables[$component]->setAttribute(IPF_ORM::ATTR_LOAD_REFERENCES, false);
- }
-
- $this->_tables[$component]->setData($data);
- $record = $this->_tables[$component]->getRecord();
-
- return $record;
- }
-
- public function flush()
- {
- // take snapshots from all initialized collections
- foreach ($this->_collections as $key => $coll) {
- $coll->takeSnapshot();
- }
- foreach ($this->_tables as $table) {
- $table->setAttribute(IPF_ORM::ATTR_LOAD_REFERENCES, true);
- }
- $this->_initializedRelations = null;
- $this->_collections = null;
- $this->_tables = null;
- }
-
- protected function _getClassNameToReturn(array &$data, $component)
- {
- if ( ! isset($this->_tables[$component])) {
- $this->_tables[$component] = IPF_ORM::getTable($component);
- $this->_tables[$component]->setAttribute(IPF_ORM::ATTR_LOAD_REFERENCES, false);
- }
-
- if ( ! ($subclasses = $this->_tables[$component]->getOption('subclasses'))) {
- return $component;
- }
-
- foreach ($subclasses as $subclass) {
- $table = IPF_ORM::getTable($subclass);
- $inheritanceMap = $table->getOption('inheritanceMap');
- list($key, $value) = each($inheritanceMap);
- if ( ! isset($data[$key]) || $data[$key] != $value) {
- continue;
- } else {
- return $table->getComponentName();
- }
- }
- return $component;
- }
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_Import_Builder
-{
- private $_baseClassPrefix = 'Base';
- private $_baseClassesDirectory = IPF_ORM::BASE_CLASSES_DIRECTORY;
-
- private static function varExport($var)
- {
- $export = var_export($var, true);
- $export = preg_replace('#\s*\(\s*#', '(', $export);
- $export = preg_replace('#[\s,]+\)#', ')', $export);
- $export = preg_replace('#\s+#', ' ', $export);
- return $export;
- }
-
- private static function varExportList($list)
- {
- $export = array();
- foreach ($list as $var) {
- $export[] = self::varExport($var);
- }
- return 'array('.implode(', ', $export).')';
- }
-
- private function buildTableDefinition(array $definition)
- {
- if (isset($definition['inheritance']['type']) && ($definition['inheritance']['type'] == 'simple' || $definition['inheritance']['type'] == 'column_aggregation')) {
- return;
- }
-
- $ret = array(
- ' public static function setTableDefinition(IPF_ORM_Table $table)',
- ' {',
- );
-
- if (isset($definition['inheritance']['type']) && $definition['inheritance']['type'] == 'concrete')
- $ret[] = ' parent::setTableDefinition($table);';
-
- if (isset($definition['tableName']) && !empty($definition['tableName']))
- $ret[] = " ".'$table->setTableName(\''. $definition['tableName'].'\');';
-
- if (isset($definition['columns']) && is_array($definition['columns']) && !empty($definition['columns']))
- $ret[] = $this->buildColumns($definition['columns']);
-
- if (isset($definition['ordering']) && is_array($definition['ordering']) && !empty($definition['ordering']))
- $ret[] = ' $table->setOrdering('.self::varExportList($definition['ordering']).');';
-
- if (isset($definition['indexes']) && is_array($definition['indexes']) && !empty($definition['indexes']))
- foreach ($definition['indexes'] as $indexName => $definitions)
- $ret[] = " \$table->addIndex('" . $indexName . "', " . self::varExport($definitions) . ');';
-
- if (isset($definition['attributes']) && is_array($definition['attributes']) && !empty($definition['attributes']))
- $ret[] = $this->buildAttributes($definition['attributes']);
-
- if (isset($definition['options']) && is_array($definition['options']) && !empty($definition['options']))
- $ret[] = $this->buildOptions($definition['options']);
-
- if (isset($definition['inheritance']['subclasses']) && !empty($definition['inheritance']['subclasses'])) {
- $ret[] = " ".'$table->setSubClasses('. self::varExport($definition['inheritance']['subclasses']).');';
- }
-
- $ret[] = ' }';
-
- return implode(PHP_EOL, $ret);
- }
-
- private function buildSetUp(array $definition)
- {
- $ret = array();
-
- if (isset($definition['relations']) && is_array($definition['relations']) && !empty($definition['relations'])) {
- foreach ($definition['relations'] as $name => $relation) {
- $class = isset($relation['class']) ? $relation['class'] : $name;
- $alias = (isset($relation['alias']) && $relation['alias'] !== $relation['class']) ? $relation['alias'] : '';
-
- if (!isset($relation['type']))
- $relation['type'] = IPF_ORM_Relation::ONE;
-
- if ($relation['type'] === IPF_ORM_Relation::ONE ||
- $relation['type'] === IPF_ORM_Relation::ONE_COMPOSITE) {
- $r = " \$table->hasOne('$class', '$alias'";
- } else {
- $r = " \$table->hasMany('$class', '$alias'";
- }
-
- $a = array();
-
- if (isset($relation['refClass'])) {
- $a[] = '\'refClass\' => ' . self::varExport($relation['refClass']);
- }
-
- if (isset($relation['deferred']) && $relation['deferred']) {
- $a[] = '\'default\' => ' . self::varExport($relation['deferred']);
- }
-
- if (isset($relation['local']) && $relation['local']) {
- $a[] = '\'local\' => ' . self::varExport($relation['local']);
- }
-
- if (isset($relation['foreign']) && $relation['foreign']) {
- $a[] = '\'foreign\' => ' . self::varExport($relation['foreign']);
- }
-
- if (isset($relation['onDelete']) && $relation['onDelete']) {
- $a[] = '\'onDelete\' => ' . self::varExport($relation['onDelete']);
- }
-
- if (isset($relation['onUpdate']) && $relation['onUpdate']) {
- $a[] = '\'onUpdate\' => ' . self::varExport($relation['onUpdate']);
- }
-
- if (isset($relation['equal']) && $relation['equal']) {
- $a[] = '\'equal\' => ' . self::varExport($relation['equal']);
- }
-
- if (isset($relation['owningSide']) && $relation['owningSide']) {
- $a[] = '\'owningSide\' => ' . self::varExport($relation['owningSide']);
- }
-
- if (isset($relation['exclude']) && $relation['exclude']) {
- $a[] = '\'exclude\' => ' . self::varExport($relation['exclude']);
- }
-
- if (!empty($a))
- $r .= ', array(' . implode(', ', $a) . ')';
-
- $ret[] = $r.');';
- }
- }
-
- if (isset($definition['templates']) && is_array($definition['templates']) && !empty($definition['templates'])) {
- $this->buildTemplates($definition['templates'], $ret);
- }
-
- if (isset($definition['actAs']) && is_array($definition['actAs']) && !empty($definition['actAs'])) {
- $this->buildActAs($definition['actAs'], $ret);
- }
-
- if (isset($definition['listeners']) && is_array($definition['listeners']) && !empty($definition['listeners'])) {
- foreach ($definition['listeners'] as $listener) {
- $ret[] = " \$table->listeners['$listener'] = new $listener;";
- }
- }
-
- // If the body of the function has contents and we are using inheritance
- // then we need call the parent::setUp() before the body of the function
- // Class table inheritance is the only one we shouldn't call parent::setUp() for
- if (count($ret) && isset($definition['inheritance']['type']) && $definition['inheritance']['type'] != 'class_table') {
- array_unshift($ret, ' parent::setUp($table);');
- }
-
- // If we have some code for the function then lets define it and return it
- if (count($ret)) {
- array_unshift($ret,
- ' public static function setUp(IPF_ORM_Table $table)',
- ' {'
- );
- $ret[] = ' }';
- }
- return $ret;
- }
-
- private function buildColumns(array $columns)
- {
- $result = array();
- foreach ($columns as $name => $column) {
- $columnName = isset($column['name']) ? $column['name']:$name;
- $build = " ".'$table->setColumn(\'' . $columnName . '\', \'' . $column['type'] . '\'';
-
- if ($column['length']) {
- if (is_numeric($column['length']))
- $build .= ', ' . $column['length'];
- else
- $build .= ', array(' . $column['length'] . ')';
- } else {
- $build .= ', null';
- }
-
- $options = $column;
-
- // Remove name, alltypes, ntype. They are not needed in options array
- unset($options['name']);
- unset($options['alltypes']);
- unset($options['ntype']);
-
- // Remove notnull => true if the column is primary
- // Primary columns are implied to be notnull in IPF_ORM
- if (isset($options['primary']) && $options['primary'] == true && (isset($options['notnull']) && $options['notnull'] == true)) {
- unset($options['notnull']);
- }
-
- // Remove default if the value is 0 and the column is a primary key
- // IPF_ORM defaults to 0 if it is a primary key
- if (isset($options['primary']) && $options['primary'] == true && (isset($options['default']) && $options['default'] == 0)) {
- unset($options['default']);
- }
-
- // These can be removed if they are empty. They all default to a false/0/null value anyways
- $remove = array('fixed', 'primary', 'notnull', 'autoincrement', 'unsigned');
- foreach ($remove as $key) {
- if (isset($options[$key]) && empty($options[$key])) {
- unset($options[$key]);
- }
- }
-
- // Remove null and empty array values
- foreach ($options as $key => $value) {
- if (is_null($value) || (is_array($value) && empty($value))) {
- unset($options[$key]);
- }
- }
-
- if (is_array($options) && !empty($options)) {
- $build .= ', ' . self::varExport($options);
- }
-
- $build .= ');';
-
- $result[] = $build;
- }
-
- return implode(PHP_EOL, $result);
- }
-
- private function buildTemplates(array $templates, array &$build)
- {
- foreach ($templates as $name => $options) {
- if (is_array($options) && !empty($options)) {
- $build[] = " \$table->addTemplate('$name', " . self::varExport($options) . ");";
- } elseif (isset($templates[0])) {
- $build[] = " \$table->addTemplate('$options');";
- } else {
- $build[] = " \$table->addTemplate('$name');";
- }
- }
- }
-
- private function buildActAs($actAs, array &$build)
- {
- // rewrite special case of actAs: [Behavior] which gave [0] => Behavior
- if (is_array($actAs) && isset($actAs[0]) && !is_array($actAs[0])) {
- $actAs = array_flip($actAs);
- }
-
- // rewrite special case of actAs: Behavior
- if (!is_array($actAs))
- $actAs = array($actAs => '');
-
- foreach ($actAs as $template => $options) {
- // find class matching $name
- if (class_exists('IPF_ORM_Template_'.$template, true))
- $classname = 'IPF_ORM_Template_'.$template;
- else
- $classname = $template;
-
- if (is_array($options))
- $options = self::varExport($options);
- else
- $options = '';
-
- $build[] = " \$table->addTemplate(new $classname($options));";
- }
- }
-
- private function buildAttributes(array $attributes)
- {
- $build = PHP_EOL;
- foreach ($attributes as $key => $value) {
-
- if (is_bool($value)) {
- $values = $value ? 'true':'false';
- } else {
- if (!is_array($value))
- $value = array($value);
-
- $values = '';
- foreach ($value as $attr) {
- $values .= "IPF_ORM::" . strtoupper($key) . "_" . strtoupper($attr) . ' ^ ';
- }
-
- // Trim last ^
- $values = substr($values, 0, strlen($values) - 3);
- }
-
- $build .= " \$table->setAttribute(IPF_ORM::ATTR_" . strtoupper($key) . ", " . $values . ");" . PHP_EOL;
- }
-
- return $build;
- }
-
- private function buildOptions(array $options)
- {
- $build = '';
- foreach ($options as $name => $value) {
- $build .= " \$table->setOption('$name', " . self::varExport($value) . ");" . PHP_EOL;
- }
- return $build;
- }
-
- public function buildRecord(array $definition, $targetPath)
- {
- if (!isset($definition['className']))
- throw new IPF_ORM_Exception('Missing class name.');
-
- $this->writeBaseDefinition($definition, $targetPath);
- $this->writeModelDefinition($definition, $targetPath);
- }
-
- private function writeBaseDefinition(array $definition, $targetPath)
- {
- $code = array(
- '<?php',
- '',
- '/**',
- ' * This class has been auto-generated by the IPF_ORM Framework.',
- ' * Changes to this file may cause incorrect behavior',
- ' * and will be lost if the code is regenerated.',
- ' */',
- '',
- );
-
- $code[] = 'abstract class '.$this->_baseClassPrefix.$definition['className'].' extends IPF_ORM_Record';
- $code[] = '{';
- $code[] = $this->buildTableDefinition($definition);
- $code[] = '';
- $code = array_merge($code, $this->buildSetUp($definition));
- $code[] = '';
- $code = array_merge($code, $this->buildShortcuts($definition));
- $code[] = '}';
-
- $fileName = $this->_baseClassPrefix . $definition['className'] . '.php';
- $writePath = $targetPath . DIRECTORY_SEPARATOR . $this->_baseClassesDirectory;
- IPF_Utils::makeDirectories($writePath);
- $writePath .= DIRECTORY_SEPARATOR . $fileName;
-
- if (file_put_contents($writePath, implode(PHP_EOL, $code)) === false)
- throw new IPF_ORM_Exception("Couldn't write file " . $writePath);
- }
-
- private function buildShortcuts(array $definition)
- {
- return array(
- ' public static function table()',
- ' {',
- ' return IPF_ORM::getTable(\''.$definition['className'].'\');',
- ' }',
- '',
- ' public static function query($alias=\'\')',
- ' {',
- ' return IPF_ORM::getTable(\''.$definition['className'].'\')->createQuery($alias);',
- ' }',
- );
- }
-
- private function writeModelDefinition(array $definition, $targetPath)
- {
- $className = $definition['className'];
- $adminClassName = $className.'Admin';
-
- $writePath = $targetPath . DIRECTORY_SEPARATOR . $className . '.php';
- if (file_exists($writePath))
- return;
-
- $code = array(
- '<?php',
- '',
- sprintf('class %s extends %s%s', $className, $this->_baseClassPrefix, $className),
- '{',
- '}',
- '',
- '/*',
- 'class '.$adminClassName.' extends IPF_Admin_Model',
- '{',
- '}',
- '',
- '// Add following line to your admin.php to enable admin interface for this model',
- "new $adminClassName('$className'),",
- '*/',
- '',
- );
-
- IPF_Utils::makeDirectories($targetPath);
- if (file_put_contents($writePath, implode(PHP_EOL, $code)) === false)
- throw new IPF_ORM_Exception("Couldn't write file " . $writePath);
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Import_Schema
-{
- protected $_relations = array();
-
- protected $_validation = array(
- 'root' => array(
- 'abstract',
- 'connection',
- 'className',
- 'tableName',
- 'connection',
- 'relations',
- 'columns',
- 'indexes',
- 'attributes',
- 'templates',
- 'actAs',
- 'options',
- 'inheritance',
- 'detect_relations',
- 'listeners',
- 'ordering',
- ),
- 'column' => array(
- 'name',
- 'format',
- 'fixed',
- 'primary',
- 'autoincrement',
- 'type',
- 'length',
- 'size',
- 'default',
- 'scale',
- 'values',
- 'comment',
- 'protected',
- 'zerofill',
- 'owner',
- 'exclude',
- 'collate',
- 'charset',
- ),
- 'relation' => array(
- 'key',
- 'class',
- 'alias',
- 'type',
- 'refClass',
- 'local',
- 'exclude',
- 'foreign',
- 'foreignClass',
- 'foreignAlias',
- 'foreignType',
- 'foreignExclude',
- 'autoComplete',
- 'onDelete',
- 'onUpdate',
- 'equal',
- 'owningSide',
- ),
- 'inheritance' => array(
- 'type',
- 'extends',
- 'keyField',
- 'keyValue',
- ),
- );
-
- protected $_validators = array();
-
- public function getValidators()
- {
- if (empty($this->_validators)) {
- $this->_validators = IPF_ORM_Utils::getValidators();
- }
- return $this->_validators;
- }
-
- public function importSchema($filename, $extraAllwedReferences)
- {
- $filename = (array)$filename;
- $array = array();
- foreach ($filename as $fn) {
- $definitions = $this->parseSchema($fn);
- foreach ($definitions as &$definition)
- $definition['input_file'] = $fn;
- $array = array_merge($array, $definitions);
- }
-
- $array = $this->_buildRelationships($array, $extraAllwedReferences);
- $array = $this->_processInheritance($array);
- return $array;
- }
-
- private function parseSchema($schema)
- {
- $defaults = array('abstract' => false,
- 'className' => null,
- 'tableName' => null,
- 'connection' => null,
- 'relations' => array(),
- 'indexes' => array(),
- 'attributes' => array(),
- 'templates' => array(),
- 'actAs' => array(),
- 'options' => array(),
- 'inheritance' => array(),
- 'detect_relations' => false);
-
- $array = Spyc::YAMLLoad($schema);
-
- // Go through the schema and look for global values so we can assign them to each table/class
- $globals = array();
- $globalKeys = array('connection',
- 'attributes',
- 'templates',
- 'actAs',
- 'options',
- 'inheritance',
- 'detect_relations');
-
- // Loop over and build up all the global values and remove them from the array
- foreach ($array as $key => $value) {
- if (in_array($key, $globalKeys)) {
- unset($array[$key]);
- $globals[$key] = $value;
- }
- }
-
- // Apply the globals to each table if it does not have a custom value set already
- foreach ($array as $className => $table) {
- foreach ($globals as $key => $value) {
- if (!isset($array[$className][$key])) {
- $array[$className][$key] = $value;
- }
- }
- }
-
- $build = array();
-
- foreach ($array as $className => $table) {
- $this->_validateSchemaElement('root', array_keys($table), $className);
-
- $columns = array();
-
- $className = isset($table['className']) ? (string) $table['className']:(string) $className;
-
- if (isset($table['inheritance']['keyField']) || isset($table['inheritance']['keyValue'])) {
- $table['inheritance']['type'] = 'column_aggregation';
- }
-
- if (isset($table['tableName']) && $table['tableName']) {
- $tableName = $table['tableName'];
- } else {
- if (isset($table['inheritance']['type']) && ($table['inheritance']['type'] == 'column_aggregation')) {
- $tableName = null;
- } else {
- $tableName = IPF_ORM_Inflector::tableize($className);
- }
- }
-
- $connection = isset($table['connection']) ? $table['connection']:'current';
-
- $columns = isset($table['columns']) ? $table['columns']:array();
-
- if ( ! empty($columns)) {
- foreach ($columns as $columnName => $field) {
-
- // Support short syntax: my_column: integer(4)
- if ( ! is_array($field)) {
- $original = $field;
- $field = array();
- $field['type'] = $original;
- }
-
- $colDesc = array();
- if (isset($field['name'])) {
- $colDesc['name'] = $field['name'];
- } else {
- $colDesc['name'] = $columnName;
- }
-
- $this->_validateSchemaElement('column', array_keys($field), $className . '->columns->' . $colDesc['name']);
-
- // Support short type(length) syntax: my_column: { type: integer(4) }
- $e = explode('(', $field['type']);
- if (isset($e[0]) && isset($e[1])) {
- $colDesc['type'] = $e[0];
- $colDesc['length'] = substr($e[1], 0, strlen($e[1]) - 1);
- } else {
- $colDesc['type'] = isset($field['type']) ? (string) $field['type']:null;
- $colDesc['length'] = isset($field['length']) ? (int) $field['length']:null;
- $colDesc['length'] = isset($field['size']) ? (int) $field['size']:$colDesc['length'];
- }
-
- $colDesc['fixed'] = isset($field['fixed']) ? (int) $field['fixed']:null;
- $colDesc['primary'] = isset($field['primary']) ? (bool) (isset($field['primary']) && $field['primary']):null;
- $colDesc['default'] = isset($field['default']) ? $field['default']:null;
- $colDesc['autoincrement'] = isset($field['autoincrement']) ? (bool) (isset($field['autoincrement']) && $field['autoincrement']):null;
- $colDesc['values'] = isset($field['values']) ? (array) $field['values']:null;
-
- // Include all the specified and valid validators in the colDesc
- $validators = $this->getValidators();
-
- foreach ($validators as $validator) {
- if (isset($field[$validator])) {
- $colDesc[$validator] = $field[$validator];
- }
- }
-
- $columns[(string) $columnName] = $colDesc;
- }
- }
-
- // Apply the default values
- foreach ($defaults as $key => $defaultValue) {
- if (isset($table[$key]) && ! isset($build[$className][$key])) {
- $build[$className][$key] = $table[$key];
- } else {
- $build[$className][$key] = isset($build[$className][$key]) ? $build[$className][$key]:$defaultValue;
- }
- }
-
- $build[$className]['className'] = $className;
- $build[$className]['tableName'] = $tableName;
- $build[$className]['columns'] = $columns;
-
- // Make sure that anything else that is specified in the schema makes it to the final array
- $build[$className] = IPF_ORM_Utils::arrayDeepMerge($table, $build[$className]);
-
- // We need to keep track of the className for the connection
- $build[$className]['connectionClassName'] = $build[$className]['className'];
- }
-
- return $build;
- }
-
- protected function _processInheritance($array)
- {
- // Apply default inheritance configuration
- foreach ($array as $className => $definition) {
- if ( ! empty($array[$className]['inheritance'])) {
- $this->_validateSchemaElement('inheritance', array_keys($definition['inheritance']), $className . '->inheritance');
-
- // Default inheritance to concrete inheritance
- if ( ! isset($array[$className]['inheritance']['type'])) {
- $array[$className]['inheritance']['type'] = 'concrete';
- }
-
- // Some magic for setting up the keyField and keyValue column aggregation options
- // Adds keyField to the parent class automatically
- if ($array[$className]['inheritance']['type'] == 'column_aggregation') {
- // Set the keyField to 'type' by default
- if ( ! isset($array[$className]['inheritance']['keyField'])) {
- $array[$className]['inheritance']['keyField'] = 'type';
- }
-
- // Set the keyValue to the name of the child class if it does not exist
- if ( ! isset($array[$className]['inheritance']['keyValue'])) {
- $array[$className]['inheritance']['keyValue'] = $className;
- }
-
- // Add the keyType column to the parent if a definition does not already exist
- if ( ! isset($array[$array[$className]['inheritance']['extends']]['columns'][$array[$className]['inheritance']['keyField']])) {
- $array[$definition['inheritance']['extends']]['columns'][$array[$className]['inheritance']['keyField']] = array('name' => $array[$className]['inheritance']['keyField'], 'type' => 'string', 'length' => 255);
- }
- }
- }
- }
-
- // Array of the array keys to move to the parent, and the value to default the child definition to
- // after moving it. Will also populate the subclasses array for the inheritance parent
- $moves = array('columns' => array());
-
- foreach ($array as $className => $definition) {
- if (!isset($definition['className']))
- continue;
- $parent = $this->_findBaseSuperClass($array, $definition['className']);
- // Move any definitions on the schema to the parent
- if (isset($definition['inheritance']['extends']) && isset($definition['inheritance']['type']) && ($definition['inheritance']['type'] == 'simple' || $definition['inheritance']['type'] == 'column_aggregation')) {
- foreach ($moves as $move => $resetValue) {
- $array[$parent][$move] = IPF_ORM_Utils::arrayDeepMerge($array[$parent][$move], $definition[$move]);
- $array[$definition['className']][$move] = $resetValue;
- }
-
- // Populate the parents subclasses
- if ($definition['inheritance']['type'] == 'column_aggregation') {
- $array[$parent]['inheritance']['subclasses'][$definition['className']] = array($definition['inheritance']['keyField'] => $definition['inheritance']['keyValue']);
- }
- }
- }
-
- return $array;
- }
-
- protected function _findBaseSuperClass($array, $class)
- {
- if (isset($array[$class]['inheritance']['extends'])) {
- return $this->_findBaseSuperClass($array, $array[$class]['inheritance']['extends']);
- } else {
- return $class;
- }
- }
-
- protected function _buildRelationships($array, $extraAllwedReferences)
- {
- // Handle auto detecting relations by the names of columns
- // User.contact_id will automatically create User hasOne Contact local => contact_id, foreign => id
- foreach ($array as $className => $properties) {
- if (isset($properties['columns']) && ! empty($properties['columns']) && isset($properties['detect_relations']) && $properties['detect_relations']) {
- foreach ($properties['columns'] as $column) {
- // Check if the column we are inflecting has a _id on the end of it before trying to inflect it and find
- // the class name for the column
- if (strpos($column['name'], '_id')) {
- $columnClassName = IPF_ORM_Inflector::classify(str_replace('_id', '', $column['name']));
- if (isset($array[$columnClassName]) && !isset($array[$className]['relations'][$columnClassName])) {
- $array[$className]['relations'][$columnClassName] = array();
-
- // Set the detected foreign key type and length to the same as the primary key
- // of the related table
- $type = isset($array[$columnClassName]['columns']['id']['type']) ? $array[$columnClassName]['columns']['id']['type']:'integer';
- $length = isset($array[$columnClassName]['columns']['id']['length']) ? $array[$columnClassName]['columns']['id']['length']:8;
- $array[$className]['columns'][$column['name']]['type'] = $type;
- $array[$className]['columns'][$column['name']]['length'] = $length;
- }
- }
- }
- }
- }
-
- foreach ($array as $name => $properties) {
- if (!isset($properties['relations'])) {
- continue;
- }
-
- $className = $properties['className'];
- $relations = $properties['relations'];
-
- foreach ($relations as $alias => $relation) {
- $class = isset($relation['class']) ? $relation['class'] : $alias;
- if (!isset($array[$class]) && !in_array($class, $extraAllwedReferences)) {
- print "Warning: Ignoring relation to unknown model '$class' in model '$className'. \n";
- continue;
- }
- $relation['class'] = $class;
- $relation['alias'] = isset($relation['alias']) ? $relation['alias'] : $alias;
-
- // Attempt to guess the local and foreign
- if (isset($relation['refClass'])) {
- $relation['local'] = isset($relation['local']) ? $relation['local']:IPF_ORM_Inflector::tableize($name) . '_id';
- $relation['foreign'] = isset($relation['foreign']) ? $relation['foreign']:IPF_ORM_Inflector::tableize($class) . '_id';
- } else {
- $relation['local'] = isset($relation['local']) ? $relation['local']:IPF_ORM_Inflector::tableize($relation['class']) . '_id';
- $relation['foreign'] = isset($relation['foreign']) ? $relation['foreign']:'id';
- }
-
- if (isset($relation['refClass'])) {
- $relation['type'] = 'many';
- }
-
- if (isset($relation['type']) && $relation['type']) {
- $relation['type'] = $relation['type'] === 'one' ? IPF_ORM_Relation::ONE:IPF_ORM_Relation::MANY;
- } else {
- $relation['type'] = IPF_ORM_Relation::ONE;
- }
-
- if (isset($relation['foreignType']) && $relation['foreignType']) {
- $relation['foreignType'] = $relation['foreignType'] === 'one' ? IPF_ORM_Relation::ONE:IPF_ORM_Relation::MANY;
- }
-
- $relation['key'] = $this->_buildUniqueRelationKey($relation);
-
- $this->_validateSchemaElement('relation', array_keys($relation), $className . '->relation->' . $relation['alias']);
-
- $this->_relations[$className][$alias] = $relation;
- }
- }
-
- // Now we auto-complete opposite ends of relationships
- $this->_autoCompleteOppositeRelations($extraAllwedReferences);
-
- // Make sure we do not have any duplicate relations
- $this->_fixDuplicateRelations();
-
- //$array['relations'];
- // Set the full array of relationships for each class to the final array
- foreach ($this->_relations as $className => $relations) {
- if (!in_array($className, $extraAllwedReferences))
- $array[$className]['relations'] = $relations;
- }
-
- return $array;
- }
-
- protected function _autoCompleteOppositeRelations($extraAllwedReferences)
- {
- foreach ($this->_relations as $className => $relations) {
- if (in_array($className, $extraAllwedReferences))
- continue;
-
- foreach ($relations as $alias => $relation) {
- if ((isset($relation['equal']) && $relation['equal']) || (isset($relation['autoComplete']) && $relation['autoComplete'] === false)) {
- continue;
- }
-
- $newRelation = array();
- $newRelation['foreign'] = $relation['local'];
- $newRelation['local'] = $relation['foreign'];
- $newRelation['class'] = isset($relation['foreignClass']) ? $relation['foreignClass'] : $className;
- $newRelation['alias'] = isset($relation['foreignAlias']) ? $relation['foreignAlias'] : $className;
- $newRelation['exclude'] = isset($relation['foreignExclude']) ? $relation['foreignExclude'] : false;
-
- // this is so that we know that this relation was autogenerated and
- // that we do not need to include it if it is explicitly declared in the schema by the users.
- $newRelation['autogenerated'] = true;
-
- if (isset($relation['refClass'])) {
- $newRelation['refClass'] = $relation['refClass'];
- $newRelation['type'] = isset($relation['foreignType']) ? $relation['foreignType']:$relation['type'];
- } else {
- if(isset($relation['foreignType'])) {
- $newRelation['type'] = $relation['foreignType'];
- } else {
- $newRelation['type'] = $relation['type'] === IPF_ORM_Relation::ONE ? IPF_ORM_Relation::MANY:IPF_ORM_Relation::ONE;
- }
- }
-
- // Make sure it doesn't already exist
- if ( ! isset($this->_relations[$relation['class']][$newRelation['alias']])) {
- $newRelation['key'] = $this->_buildUniqueRelationKey($newRelation);
- $this->_relations[$relation['class']][$newRelation['alias']] = $newRelation;
- }
- }
- }
- }
-
- protected function _fixDuplicateRelations()
- {
- foreach($this->_relations as $className => $relations) {
- // This is for checking for duplicates between alias-relations and a auto-generated relations to ensure the result set of unique relations
- $existingRelations = array();
- $uniqueRelations = array();
- foreach ($relations as $relation) {
- if ( ! in_array($relation['key'], $existingRelations)) {
- $existingRelations[] = $relation['key'];
- $uniqueRelations = array_merge($uniqueRelations, array($relation['alias'] => $relation));
- } else {
- // check to see if this relationship is not autogenerated, if it's not, then the user must have explicitly declared it
- if ( ! isset($relation['autogenerated']) || $relation['autogenerated'] != true) {
- $uniqueRelations = array_merge($uniqueRelations, array($relation['alias'] => $relation));
- }
- }
- }
-
- $this->_relations[$className] = $uniqueRelations;
- }
- }
-
- protected function _buildUniqueRelationKey($relation)
- {
- return md5($relation['local'].$relation['foreign'].$relation['class'].(isset($relation['refClass']) ? $relation['refClass']:null));
- }
-
- protected function _validateSchemaElement($name, $element, $path)
- {
- $element = (array) $element;
-
- $validation = $this->_validation[$name];
-
- // Validators are a part of the column validation
- // This should be fixed, made cleaner
- if ($name == 'column') {
- $validators = $this->getValidators();
- $validation = array_merge($validation, $validators);
- }
-
- $validation = array_flip($validation);
- foreach ($element as $key => $value) {
- if ( ! isset($validation[$value])) {
- throw new IPF_ORM_Exception(sprintf('Invalid schema element named "' . $value .
- '" at path "' . $path . '"'));
- }
- }
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Inflector
-{
- public static function pluralize($word)
- {
- $plural = array('/(quiz)$/i' => '\1zes',
- '/^(ox)$/i' => '\1en',
- '/([m|l])ouse$/i' => '\1ice',
- '/(matr|vert|ind)ix|ex$/i' => '\1ices',
- '/(x|ch|ss|sh)$/i' => '\1es',
- '/([^aeiouy]|qu)ies$/i' => '\1y',
- '/([^aeiouy]|qu)y$/i' => '\1ies',
- '/(hive)$/i' => '\1s',
- '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves',
- '/sis$/i' => 'ses',
- '/([ti])um$/i' => '\1a',
- '/(buffal|tomat)o$/i' => '\1oes',
- '/(bu)s$/i' => '\1ses',
- '/(alias|status)/i' => '\1es',
- '/(octop|vir)us$/i' => '\1i',
- '/(ax|test)is$/i' => '\1es',
- '/s$/i' => 's',
- '/$/' => 's');
-
- $uncountable = array('equipment',
- 'information',
- 'rice',
- 'money',
- 'species',
- 'series',
- 'fish',
- 'sheep');
-
- $irregular = array('person' => 'people',
- 'man' => 'men',
- 'child' => 'children',
- 'sex' => 'sexes',
- 'move' => 'moves');
-
- $lowercasedWord = strtolower($word);
-
- foreach ($uncountable as $_uncountable) {
- if (substr($lowercasedWord, (-1 * strlen($_uncountable))) == $_uncountable) {
- return $word;
- }
- }
-
- foreach ($irregular as $_plural=> $_singular) {
- if (preg_match('/('.$_plural.')$/i', $word, $arr)) {
- return preg_replace('/('.$_plural.')$/i', substr($arr[0],0,1) . substr($_singular,1), $word);
- }
- }
-
- foreach ($plural as $rule => $replacement) {
- if (preg_match($rule, $word)) {
- return preg_replace($rule, $replacement, $word);
- }
- }
-
- return false;
- }
-
- public static function singularize($word)
- {
- $singular = array('/(quiz)zes$/i' => '\\1',
- '/(matr)ices$/i' => '\\1ix',
- '/(vert|ind)ices$/i' => '\\1ex',
- '/^(ox)en/i' => '\\1',
- '/(alias|status)es$/i' => '\\1',
- '/([octop|vir])i$/i' => '\\1us',
- '/(cris|ax|test)es$/i' => '\\1is',
- '/(shoe)s$/i' => '\\1',
- '/(o)es$/i' => '\\1',
- '/(bus)es$/i' => '\\1',
- '/([m|l])ice$/i' => '\\1ouse',
- '/(x|ch|ss|sh)es$/i' => '\\1',
- '/(m)ovies$/i' => '\\1ovie',
- '/(s)eries$/i' => '\\1eries',
- '/([^aeiouy]|qu)ies$/i' => '\\1y',
- '/([lr])ves$/i' => '\\1f',
- '/(tive)s$/i' => '\\1',
- '/(hive)s$/i' => '\\1',
- '/([^f])ves$/i' => '\\1fe',
- '/(^analy)ses$/i' => '\\1sis',
- '/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\\1\\2sis',
- '/([ti])a$/i' => '\\1um',
- '/(n)ews$/i' => '\\1ews',
- '/^(.{2,2})$/i' => '\\1',
- '/s$/i' => '');
-
- $uncountable = array('equipment',
- 'information',
- 'rice',
- 'money',
- 'species',
- 'series',
- 'fish',
- 'sheep',
- 'sms',
- 'status',
- 'access');
-
- $irregular = array('person' => 'people',
- 'man' => 'men',
- 'child' => 'children',
- 'sex' => 'sexes',
- 'move' => 'moves');
-
- $lowercasedWord = strtolower($word);
- foreach ($uncountable as $_uncountable) {
- if (substr($lowercasedWord, ( -1 * strlen($_uncountable))) == $_uncountable) {
- return $word;
- }
- }
-
- foreach ($irregular as $_singular => $_plural) {
- if (preg_match('/('.$_plural.')$/i', $word, $arr)) {
- return preg_replace('/('.$_plural.')$/i', substr($arr[0],0,1).substr($_singular,1), $word);
- }
- }
-
- foreach ($singular as $rule => $replacement) {
- if (preg_match($rule, $word)) {
- return preg_replace($rule, $replacement, $word);
- }
- }
-
- return $word;
- }
-
- public static function tableize($word)
- {
- // Would prefer this but it breaks unit tests. Forces the table underscore pattern
- // return self::pluralize(self::underscore($name));
- return strtolower(preg_replace('~(?<=\\w)([A-Z])~', '_$1', $word));
- }
-
- public static function classify($word)
- {
- return preg_replace_callback('~(_?)(_)([\w])~', array("IPF_ORM_Inflector", "classifyCallback"), ucfirst(strtolower($word)));
- }
-
- public static function classifyCallback($matches)
- {
- return $matches[1] . strtoupper($matches[3]);
- }
-
- public static function seemsUtf8($string)
- {
- for ($i = 0; $i < strlen($string); $i++) {
- if (ord($string[$i]) < 0x80) continue; # 0bbbbbbb
- elseif ((ord($string[$i]) & 0xE0) == 0xC0) $n=1; # 110bbbbb
- elseif ((ord($string[$i]) & 0xF0) == 0xE0) $n=2; # 1110bbbb
- elseif ((ord($string[$i]) & 0xF8) == 0xF0) $n=3; # 11110bbb
- elseif ((ord($string[$i]) & 0xFC) == 0xF8) $n=4; # 111110bb
- elseif ((ord($string[$i]) & 0xFE) == 0xFC) $n=5; # 1111110b
- else return false; # Does not match any model
- for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ?
- if ((++$i == strlen($string)) || ((ord($string[$i]) & 0xC0) != 0x80))
- return false;
- }
- }
- return true;
- }
-
- public static function unaccent($string)
- {
- if (!preg_match('/[\x80-\xff]/', $string)) {
- return $string;
- }
-
- if (self::seemsUtf8($string)) {
- $chars = array(
- // Decompositions for Latin-1 Supplement
- chr(195).chr(128) => 'A', chr(195).chr(129) => 'A',
- chr(195).chr(130) => 'A', chr(195).chr(131) => 'A',
- chr(195).chr(132) => 'A', chr(195).chr(133) => 'A',
- chr(195).chr(135) => 'C', chr(195).chr(136) => 'E',
- chr(195).chr(137) => 'E', chr(195).chr(138) => 'E',
- chr(195).chr(139) => 'E', chr(195).chr(140) => 'I',
- chr(195).chr(141) => 'I', chr(195).chr(142) => 'I',
- chr(195).chr(143) => 'I', chr(195).chr(145) => 'N',
- chr(195).chr(146) => 'O', chr(195).chr(147) => 'O',
- chr(195).chr(148) => 'O', chr(195).chr(149) => 'O',
- chr(195).chr(150) => 'O', chr(195).chr(153) => 'U',
- chr(195).chr(154) => 'U', chr(195).chr(155) => 'U',
- chr(195).chr(156) => 'U', chr(195).chr(157) => 'Y',
- chr(195).chr(159) => 's', chr(195).chr(160) => 'a',
- chr(195).chr(161) => 'a', chr(195).chr(162) => 'a',
- chr(195).chr(163) => 'a', chr(195).chr(164) => 'a',
- chr(195).chr(165) => 'a', chr(195).chr(167) => 'c',
- chr(195).chr(168) => 'e', chr(195).chr(169) => 'e',
- chr(195).chr(170) => 'e', chr(195).chr(171) => 'e',
- chr(195).chr(172) => 'i', chr(195).chr(173) => 'i',
- chr(195).chr(174) => 'i', chr(195).chr(175) => 'i',
- chr(195).chr(177) => 'n', chr(195).chr(178) => 'o',
- chr(195).chr(179) => 'o', chr(195).chr(180) => 'o',
- chr(195).chr(181) => 'o', chr(195).chr(182) => 'o',
- chr(195).chr(182) => 'o', chr(195).chr(185) => 'u',
- chr(195).chr(186) => 'u', chr(195).chr(187) => 'u',
- chr(195).chr(188) => 'u', chr(195).chr(189) => 'y',
- chr(195).chr(191) => 'y',
- // Decompositions for Latin Extended-A
- chr(196).chr(128) => 'A', chr(196).chr(129) => 'a',
- chr(196).chr(130) => 'A', chr(196).chr(131) => 'a',
- chr(196).chr(132) => 'A', chr(196).chr(133) => 'a',
- chr(196).chr(134) => 'C', chr(196).chr(135) => 'c',
- chr(196).chr(136) => 'C', chr(196).chr(137) => 'c',
- chr(196).chr(138) => 'C', chr(196).chr(139) => 'c',
- chr(196).chr(140) => 'C', chr(196).chr(141) => 'c',
- chr(196).chr(142) => 'D', chr(196).chr(143) => 'd',
- chr(196).chr(144) => 'D', chr(196).chr(145) => 'd',
- chr(196).chr(146) => 'E', chr(196).chr(147) => 'e',
- chr(196).chr(148) => 'E', chr(196).chr(149) => 'e',
- chr(196).chr(150) => 'E', chr(196).chr(151) => 'e',
- chr(196).chr(152) => 'E', chr(196).chr(153) => 'e',
- chr(196).chr(154) => 'E', chr(196).chr(155) => 'e',
- chr(196).chr(156) => 'G', chr(196).chr(157) => 'g',
- chr(196).chr(158) => 'G', chr(196).chr(159) => 'g',
- chr(196).chr(160) => 'G', chr(196).chr(161) => 'g',
- chr(196).chr(162) => 'G', chr(196).chr(163) => 'g',
- chr(196).chr(164) => 'H', chr(196).chr(165) => 'h',
- chr(196).chr(166) => 'H', chr(196).chr(167) => 'h',
- chr(196).chr(168) => 'I', chr(196).chr(169) => 'i',
- chr(196).chr(170) => 'I', chr(196).chr(171) => 'i',
- chr(196).chr(172) => 'I', chr(196).chr(173) => 'i',
- chr(196).chr(174) => 'I', chr(196).chr(175) => 'i',
- chr(196).chr(176) => 'I', chr(196).chr(177) => 'i',
- chr(196).chr(178) => 'IJ',chr(196).chr(179) => 'ij',
- chr(196).chr(180) => 'J', chr(196).chr(181) => 'j',
- chr(196).chr(182) => 'K', chr(196).chr(183) => 'k',
- chr(196).chr(184) => 'k', chr(196).chr(185) => 'L',
- chr(196).chr(186) => 'l', chr(196).chr(187) => 'L',
- chr(196).chr(188) => 'l', chr(196).chr(189) => 'L',
- chr(196).chr(190) => 'l', chr(196).chr(191) => 'L',
- chr(197).chr(128) => 'l', chr(197).chr(129) => 'L',
- chr(197).chr(130) => 'l', chr(197).chr(131) => 'N',
- chr(197).chr(132) => 'n', chr(197).chr(133) => 'N',
- chr(197).chr(134) => 'n', chr(197).chr(135) => 'N',
- chr(197).chr(136) => 'n', chr(197).chr(137) => 'N',
- chr(197).chr(138) => 'n', chr(197).chr(139) => 'N',
- chr(197).chr(140) => 'O', chr(197).chr(141) => 'o',
- chr(197).chr(142) => 'O', chr(197).chr(143) => 'o',
- chr(197).chr(144) => 'O', chr(197).chr(145) => 'o',
- chr(197).chr(146) => 'OE',chr(197).chr(147) => 'oe',
- chr(197).chr(148) => 'R', chr(197).chr(149) => 'r',
- chr(197).chr(150) => 'R', chr(197).chr(151) => 'r',
- chr(197).chr(152) => 'R', chr(197).chr(153) => 'r',
- chr(197).chr(154) => 'S', chr(197).chr(155) => 's',
- chr(197).chr(156) => 'S', chr(197).chr(157) => 's',
- chr(197).chr(158) => 'S', chr(197).chr(159) => 's',
- chr(197).chr(160) => 'S', chr(197).chr(161) => 's',
- chr(197).chr(162) => 'T', chr(197).chr(163) => 't',
- chr(197).chr(164) => 'T', chr(197).chr(165) => 't',
- chr(197).chr(166) => 'T', chr(197).chr(167) => 't',
- chr(197).chr(168) => 'U', chr(197).chr(169) => 'u',
- chr(197).chr(170) => 'U', chr(197).chr(171) => 'u',
- chr(197).chr(172) => 'U', chr(197).chr(173) => 'u',
- chr(197).chr(174) => 'U', chr(197).chr(175) => 'u',
- chr(197).chr(176) => 'U', chr(197).chr(177) => 'u',
- chr(197).chr(178) => 'U', chr(197).chr(179) => 'u',
- chr(197).chr(180) => 'W', chr(197).chr(181) => 'w',
- chr(197).chr(182) => 'Y', chr(197).chr(183) => 'y',
- chr(197).chr(184) => 'Y', chr(197).chr(185) => 'Z',
- chr(197).chr(186) => 'z', chr(197).chr(187) => 'Z',
- chr(197).chr(188) => 'z', chr(197).chr(189) => 'Z',
- chr(197).chr(190) => 'z', chr(197).chr(191) => 's',
- // Euro Sign
- chr(226).chr(130).chr(172) => 'E',
- // GBP (Pound) Sign
- chr(194).chr(163) => '');
-
- $string = strtr($string, $chars);
- } else {
- // Assume ISO-8859-1 if not UTF-8
- $chars['in'] = chr(128).chr(131).chr(138).chr(142).chr(154).chr(158)
- .chr(159).chr(162).chr(165).chr(181).chr(192).chr(193).chr(194)
- .chr(195).chr(196).chr(197).chr(199).chr(200).chr(201).chr(202)
- .chr(203).chr(204).chr(205).chr(206).chr(207).chr(209).chr(210)
- .chr(211).chr(212).chr(213).chr(214).chr(216).chr(217).chr(218)
- .chr(219).chr(220).chr(221).chr(224).chr(225).chr(226).chr(227)
- .chr(228).chr(229).chr(231).chr(232).chr(233).chr(234).chr(235)
- .chr(236).chr(237).chr(238).chr(239).chr(241).chr(242).chr(243)
- .chr(244).chr(245).chr(246).chr(248).chr(249).chr(250).chr(251)
- .chr(252).chr(253).chr(255);
-
- $chars['out'] = "EfSZszYcYuAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy";
-
- $string = strtr($string, $chars['in'], $chars['out']);
- $doubleChars['in'] = array(chr(140), chr(156), chr(198), chr(208), chr(222), chr(223), chr(230), chr(240), chr(254));
- $doubleChars['out'] = array('OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th');
- $string = str_replace($doubleChars['in'], $doubleChars['out'], $string);
- }
-
- return $string;
- }
-
- public static function translit($st)
- {
- $st = strtr($st, array(
- 'а' => 'a', 'б' => 'b', 'в' => 'v', 'г' => 'g', 'д' => 'd',
- 'е' => 'e', 'ё' => 'e', 'ж' => 'zh', 'з' => 'z', 'и' => 'i',
- 'й' => 'y', 'к' => 'k', 'л' => 'l', 'м' => 'm', 'н' => 'n',
- 'о' => 'o', 'п' => 'p', 'р' => 'r', 'с' => 's', 'т' => 't',
- 'у' => 'u', 'ф' => 'f', 'х' => 'h', 'ц' => 'ts', 'ч' => 'ch',
- 'ш' => 'sh', 'щ' => 'shch', 'ъ' => '\'', 'ы' => 'i', 'ь' => '',
- 'э' => 'e', 'ю' => 'yu', 'я' => 'ya', 'ї' => 'i', 'є' => 'ie',
-
- 'А' => 'A', 'Б' => 'B', 'В' => 'V', 'Г' => 'G', 'Д' => 'D',
- 'Е' => 'E', 'Ё' => 'E', 'Ж' => 'Zh', 'З' => 'Z', 'И' => 'I',
- 'Й' => 'Y', 'К' => 'K', 'Л' => 'L', 'М' => 'M', 'Н' => 'N',
- 'О' => 'O', 'П' => 'P', 'Р' => 'R', 'С' => 'S', 'Т' => 'T',
- 'У' => 'U', 'Ф' => 'F', 'Х' => 'H', 'Ц' => 'Ts', 'Ч' => 'Ch',
- 'Ш' => 'Sh', 'Щ' => 'Shch', 'Ъ' => '\'', 'Ы' => 'I', 'Ь' => '',
- 'Э' => 'E', 'Ю' => 'Yu', 'Я' => 'Ya', 'Ї' => 'Yi', 'Є' => 'Ye',
- ));
- return $st;
- }
-
- public static function urlize($text)
- {
- // Remove all non url friendly characters with the unaccent function
- $text = self::unaccent($text);
-
- // Transliterate cyrillic
- $text = self::translit($text);
-
- // Remove all none word characters
- $text = preg_replace('/\W/', ' ', $text);
-
- // More stripping. Replace spaces with dashes
- $text = strtolower(preg_replace('/[^A-Z^a-z^0-9^\/]+/', '-',
- preg_replace('/([a-z\d])([A-Z])/', '\1_\2',
- preg_replace('/([A-Z]+)([A-Z][a-z])/', '\1_\2',
- preg_replace('/::/', '/', $text)))));
-
- return trim($text, '-');
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Manager extends IPF_ORM_Configurable
-{
- protected $_connection = null;
- protected $_queryRegistry;
- public $dbListeners = array();
-
- private function __construct()
- {
- $this->attributes = array(
- IPF_ORM::ATTR_LOAD_REFERENCES => true,
- IPF_ORM::ATTR_PORTABILITY => IPF_ORM::PORTABILITY_ALL,
- IPF_ORM::ATTR_EXPORT => IPF_ORM::EXPORT_ALL,
- IPF_ORM::ATTR_DECIMAL_PLACES => 2,
- IPF_ORM::ATTR_DEFAULT_PARAM_NAMESPACE => 'ipf',
- );
- }
-
- public static function getInstance()
- {
- static $instance;
- if ( ! isset($instance)) {
- $instance = new self();
- }
- return $instance;
- }
-
- public function getQueryRegistry()
- {
- if ( ! isset($this->_queryRegistry)) {
- $this->_queryRegistry = new IPF_ORM_Query_Registry;
- }
- return $this->_queryRegistry;
- }
-
- public function setQueryRegistry(IPF_ORM_Query_Registry $registry)
- {
- $this->_queryRegistry = $registry;
-
- return $this;
- }
-
- public static function connection()
- {
- return IPF_ORM_Manager::getInstance()->getCurrentConnection();
- }
-
- public function getCurrentConnection()
- {
- if (!$this->_connection) {
- $pdo = \PFF\Container::databaseConnection();
- $this->_connection = new IPF_ORM_Connection_Mysql($this, $pdo);
- }
- return $this->_connection;
- }
-
- public function __toString()
- {
- return "<pre>\nIPF_ORM_Manager\n</pre>";
- }
-}
-
+++ /dev/null
-<?php
-
-final class IPF_ORM_Null
-{
- private static $instance = null;
- public static function getInstance()
- {
- if (!self::$instance)
- self::$instance = new IPF_ORM_Null;
- return self::$instance;
- }
-
- private function __construct()
- {
- }
-
- private function __clone()
- {
- }
-
- public static function isNull($obj)
- {
- return $obj === self::$instance;
- }
-
- public function exists()
- {
- return false;
- }
-
- public function __toString()
- {
- return '';
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Pager
-{
- protected $_query;
- protected $_countQuery;
- protected $_countQueryParams;
- protected $_numResults;
- protected $_maxPerPage;
- protected $_page;
- protected $_lastPage;
- protected $_executed;
-
- public function __construct($query, $page, $maxPerPage = 0)
- {
- $this->_setExecuted(false);
-
- $this->_setQuery($query);
- $this->_setPage($page);
-
- $this->setMaxPerPage($maxPerPage);
- }
-
- protected function _initialize($params = array())
- {
- // retrieve the number of items found
- $count = $this->getCountQuery()->count($this->getCountQueryParams($params));
-
- $this->_setNumResults($count);
- $this->_setExecuted(true); // _adjustOffset relies of _executed equals true = getNumResults()
-
- $this->_adjustOffset();
- }
-
- protected function _adjustOffset()
- {
- // Define new total of pages
- $this->_setLastPage(
- max(1, ceil($this->getNumResults() / $this->getMaxPerPage()))
- );
- $offset = ($this->getPage() - 1) * $this->getMaxPerPage();
-
- // Assign new offset and limit to IPF_ORM_Query object
- $p = $this->getQuery();
- $p->offset($offset);
- $p->limit($this->getMaxPerPage());
- }
-
- public function getExecuted()
- {
- return $this->_executed;
- }
-
- protected function _setExecuted($executed)
- {
- $this->_executed = $executed;
- }
-
- public function getRange($rangeStyle, $options = array())
- {
- $class = 'IPF_ORM_Pager_Range_' . ucfirst($rangeStyle);
- return new $class($options, $this);
- }
-
- public function getNumResults()
- {
- if ($this->getExecuted()) {
- return $this->_numResults;
- }
- throw new IPF_ORM_Exception(
- 'Cannot retrieve the number of results of a not yet executed Pager query'
- );
- }
-
- protected function _setNumResults($nb)
- {
- $this->_numResults = $nb;
- }
-
- public function getFirstPage()
- {
- return 1;
- }
-
- public function getLastPage()
- {
- if ($this->getExecuted()) {
- return $this->_lastPage;
- }
-
- throw new IPF_ORM_Exception(
- 'Cannot retrieve the last page number of a not yet executed Pager query'
- );
- }
-
- protected function _setLastPage($page)
- {
- $this->_lastPage = $page;
-
- if ($this->getPage() > $page) {
- $this->_setPage($page);
- }
- }
-
- public function getPage()
- {
- return $this->_page;
- }
-
- public function getNextPage()
- {
- if ($this->getExecuted()) {
- return min($this->getPage() + 1, $this->getLastPage());
- }
-
- throw new IPF_ORM_Exception(
- 'Cannot retrieve the last page number of a not yet executed Pager query'
- );
- }
-
- public function getPreviousPage()
- {
- if ($this->getExecuted()) {
- return max($this->getPage() - 1, $this->getFirstPage());
- }
-
- throw new IPF_ORM_Exception(
- 'Cannot retrieve the previous page number of a not yet executed Pager query'
- );
- }
-
- public function getFirstIndice()
- {
- return ($this->getPage() - 1) * $this->getMaxPerPage() + 1;
- }
-
- public function getLastIndice()
- {
- return min($this->getNumResults(), ($this->getPage() * $this->getMaxPerPage()));
- }
-
- public function haveToPaginate()
- {
- if ($this->getExecuted()) {
- return $this->getNumResults() > $this->getMaxPerPage();
- }
-
- throw new IPF_ORM_Exception(
- 'Cannot know if it is necessary to paginate a not yet executed Pager query'
- );
- }
-
- public function setPage($page)
- {
- $this->_setPage($page);
- $this->_setExecuted(false);
- }
-
- private function _setPage($page)
- {
- $page = intval($page);
- $this->_page = ($page <= 0) ? 1 : $page;
- }
-
- public function getMaxPerPage()
- {
- return $this->_maxPerPage;
- }
-
- public function setMaxPerPage($max)
- {
- if ($max > 0) {
- $this->_maxPerPage = $max;
- } else if ($max == 0) {
- $this->_maxPerPage = 25;
- } else {
- $this->_maxPerPage = abs($max);
- }
-
- $this->_setExecuted(false);
- }
-
- public function getResultsInPage()
- {
- $page = $this->getPage();
-
- if ($page != $this->getLastPage()) {
- return $this->getMaxPerPage();
- }
-
- $offset = ($this->getPage() - 1) * $this->getMaxPerPage();
-
- return abs($this->getNumResults() - $offset);
- }
-
- public function getQuery()
- {
- return $this->_query;
- }
-
- protected function _setQuery($query)
- {
- if (is_string($query)) {
- $query = IPF_ORM_Query::create()->parseQuery($query);
- }
-
- $this->_query = $query;
- }
-
- public function getCountQuery()
- {
- return ($this->_countQuery !== null) ? $this->_countQuery : $this->_query;
- }
-
- public function setCountQuery($query, $params = null)
- {
- if (is_string($query)) {
- $query = IPF_ORM_Query::create()->parseQuery($query);
- }
-
- $this->_countQuery = $query;
-
- $this->setCountQueryParams($params);
-
- $this->_setExecuted(false);
- }
-
- public function getCountQueryParams($defaultParams = array())
- {
- return ($this->_countQueryParams !== null) ? $this->_countQueryParams : $defaultParams;
- }
-
- public function setCountQueryParams($params = array(), $append = false)
- {
- if ($append && is_array($this->_countQueryParams)) {
- $this->_countQueryParams = array_merge($this->_countQueryParams, $params);
- } else {
- if ($params !== null && !is_array($params)) {
- $params = array($params);
- }
-
- $this->_countQueryParams = $params;
- }
-
- $this->_setExecuted(false);
- }
-
- public function execute($params = array(), $hydrationMode = IPF_ORM::FETCH_RECORD)
- {
- if (!$this->getExecuted()) {
- $this->_initialize($params);
- }
- return $this->getQuery()->execute($params, $hydrationMode);
- }
-
- public function __debugInfo()
- {
- return array(
- 'page' => $this->getPage(),
- 'size' => $this->getMaxPerPage(),
- 'query' => $this->getQuery(),
- );
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Pager_Layout
-{
- private $_pager;
- private $_pagerRange;
- private $_template;
- private $_selectedTemplate;
- private $_separatorTemplate;
- private $_urlMask;
- private $_maskReplacements = array();
-
- public function __construct($pager, $pagerRange, $urlMask)
- {
- $this->_setPager($pager);
- $this->_setPagerRange($pagerRange);
- $this->_setUrlMask($urlMask);
-
- $this->setTemplate('[<a href="{%url}">{%page}</a>]');
- $this->setSelectedTemplate('');
- $this->setSeparatorTemplate('');
- }
-
- public function getPager()
- {
- return $this->_pager;
- }
-
- protected function _setPager($pager)
- {
- $this->_pager = $pager;
- }
-
- public function execute($params = array(), $hydrationMode = IPF_ORM::FETCH_RECORD)
- {
- return $this->getPager()->execute($params, $hydrationMode);
- }
-
- public function getPagerRange()
- {
- return $this->_pagerRange;
- }
-
- protected function _setPagerRange($pagerRange)
- {
- $this->_pagerRange = $pagerRange;
- $this->getPagerRange()->setPager($this->getPager());
- }
-
- public function getUrlMask()
- {
- return $this->_urlMask;
- }
-
- protected function _setUrlMask($urlMask)
- {
- $this->_urlMask = $urlMask;
- }
-
- public function getTemplate()
- {
- return $this->_template;
- }
-
- public function setTemplate($template)
- {
- $this->_template = $template;
- }
-
- public function getSelectedTemplate()
- {
- return $this->_selectedTemplate;
- }
-
- public function setSelectedTemplate($selectedTemplate)
- {
- $this->_selectedTemplate = $selectedTemplate;
- }
-
- public function getSeparatorTemplate()
- {
- return $this->_separatorTemplate;
- }
-
- public function setSeparatorTemplate($separatorTemplate)
- {
- $this->_separatorTemplate = $separatorTemplate;
- }
-
- public function addMaskReplacement($oldMask, $newMask, $asValue = false)
- {
- if (($oldMask = trim($oldMask)) != 'page_number') {
- $this->_maskReplacements[$oldMask] = array(
- 'newMask' => $newMask,
- 'asValue' => ($asValue === false) ? false : true
- );
- }
- }
-
- public function removeMaskReplacement($oldMask)
- {
- if (isset($this->_maskReplacements[$oldMask])) {
- $this->_maskReplacements[$oldMask] = null;
- unset($this->_maskReplacements[$oldMask]);
- }
- }
-
- public function cleanMaskReplacements()
- {
- $this->_maskReplacements = null;
- $this->_maskReplacements = array();
- }
-
- public function display($options = array(), $return = true)
- {
- $range = $this->getPagerRange()->rangeAroundPage();
- $str = '';
-
- // For each page in range
- for ($i = 0, $l = count($range); $i < $l; $i++) {
- // Define some optional mask values
- $options['page_number'] = $range[$i];
-
- $str .= $this->processPage($options);
-
- // Apply separator between pages
- if ($i < $l - 1) {
- $str .= $this->getSeparatorTemplate();
- }
- }
-
- // Possible wish to return value instead of print it on screen
- if ($return) {
- return $str;
- }
-
- echo $str;
- }
-
- public function processPage($options = array())
- {
- // Check if at least basic options are defined
- if (!isset($options['page_number'])) {
- throw new IPF_ORM_Exception(
- 'Cannot process template of the given page. ' .
- 'Missing at least one of needed parameters: \'page\' or \'page_number\''
- );
-
- // Should never reach here
- return '';
- }
-
- // Assign "page" options index if not defined yet
- if (!isset($this->_maskReplacements['page']) && !isset($options['page'])) {
- $options['page'] = $options['page_number'];
- }
-
- return $this->_parseTemplate($options);
- }
-
- public function __toString()
- {
- return $this->display(array(), true);
- }
-
- protected function _parseTemplate($options = array())
- {
- $str = $this->_parseUrlTemplate($options);
- $replacements = $this->_parseReplacementsTemplate($options);
-
- return strtr($str, $replacements);
- }
-
- protected function _parseUrlTemplate($options = array())
- {
- $str = '';
-
- // If given page is the current active one
- if ($options['page_number'] == $this->getPager()->getPage()) {
- $str = $this->_parseMaskReplacements($this->getSelectedTemplate());
- }
-
- // Possible attempt where Selected == Template
- if ($str == '') {
- $str = $this->_parseMaskReplacements($this->getTemplate());
- }
-
- return $str;
- }
-
- protected function _parseReplacementsTemplate($options = array())
- {
- // Defining "url" options index to allow {%url} mask
- $options['url'] = $this->_parseUrl($options);
-
- $replacements = array();
-
- foreach ($options as $k => $v) {
- $replacements['{%'.$k.'}'] = $v;
- }
-
- return $replacements;
- }
-
- protected function _parseUrl($options = array())
- {
- $str = $this->_parseMaskReplacements($this->getUrlMask());
-
- $replacements = array();
-
- foreach ($options as $k => $v) {
- $replacements['{%'.$k.'}'] = $v;
- }
-
- return strtr($str, $replacements);
- }
-
- protected function _parseMaskReplacements($str)
- {
- $replacements = array();
-
- foreach ($this->_maskReplacements as $k => $v) {
- $replacements['{%'.$k.'}'] = ($v['asValue'] === true) ? $v['newMask'] : '{%'.$v['newMask'].'}';
- }
-
- return strtr($str, $replacements);
- }
-
- public function __debugInfo()
- {
- return array(
- 'pager' => $this->getPager()
- );
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Pager_LayoutArrows extends IPF_ORM_Pager_Layout
-{
- public function display($options = array(), $return = true)
- {
- $pager = $this->getPager();
- $str = '';
-
- $range = $this->getPagerRange()->rangeAroundPage();
-
- if ($pager->getFirstPage() != $pager->getLastPage())
- {
- $this->removeMaskReplacement('page');
-
- if ($range[0] > 1)
- {
- $options['page_number'] = 1;
- $str .= $this->processPage($options);
-
- if ($range[0] > 2)
- {
- $options['page_number'] = 2;
- $str .= $this->processPage($options);
- }
-
- if ($range[0]>3)
- $str .= ' ... ';
- }
-
- // Pages listing
- $str .= parent::display($options, true);
-
- $range = $this->getPagerRange()->rangeAroundPage();
- $last_range = $range[count($range)-1];
-
- if ($last_range < $pager->getLastPage())
- {
- if ($last_range < $pager->getLastPage() - 2)
- {
- $str .= ' ... ';
- }
-
- if ($last_range < $pager->getLastPage() - 1)
- {
- $options['page_number'] = $pager->getLastPage() - 1;
- $str .= $this->processPage($options);
- }
-
- if ($last_range < $pager->getLastPage())
- {
- $options['page_number'] = $pager->getLastPage();
- $str .= $this->processPage($options);
- }
- }
- }
-
- if ($return)
- return $str;
-
- echo $str;
- }
-}
-
+++ /dev/null
-<?php
-
-abstract class IPF_ORM_Pager_Range
-{
- protected $_options;
- private $pager;
-
- final public function __construct($options = array(), $pager = null)
- {
- $this->_setOptions($options);
-
- if ($pager !== null) {
- $this->setPager($pager);
- }
- }
-
- public function getPager()
- {
- return $this->pager;
- }
-
- public function setPager($pager)
- {
- $this->pager = $pager;
-
- // Lazy-load initialization. It only should be called when all
- // needed information data is ready (this can only happens when we have
- // options stored and a IPF_ORM_Pager assocated)
- $this->_initialize();
- }
-
- public function getOptions()
- {
- return $this->_options;
- }
-
- public function getOption($option)
- {
- if (isset($this->_options[$option])) {
- return $this->_options[$option];
- }
-
- throw new IPF_ORM_Exception(
- 'Cannot access unexistent option \'' . $option . '\' in IPF_ORM_Pager_Range class'
- );
- }
-
- protected function _setOptions($options)
- {
- $this->_options = $options;
- }
-
- public function isInRange($page)
- {
- return (array_search($page, $this->rangeAroundPage()) !== false);
- }
-
- abstract protected function _initialize();
-
- abstract public function rangeAroundPage();
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_Pager_Range_Jumping extends IPF_ORM_Pager_Range
-{
- private $_chunkLength;
-
- protected function _initialize()
- {
- if (isset($this->_options['chunk'])) {
- $this->_setChunkLength($this->_options['chunk']);
- } else {
- throw new IPF_ORM_Exception('Missing parameter \'chunk\' that must be define in options.');
- }
- }
-
- public function getChunkLength()
- {
- return $this->_chunkLength;
- }
-
- protected function _setChunkLength($chunkLength)
- {
- $this->_chunkLength = $chunkLength;
- }
-
- public function rangeAroundPage()
- {
- $pager = $this->getPager();
-
- if ($pager->getExecuted()) {
- $page = $pager->getPage();
-
- // Define initial assignments for StartPage and EndPage
- $startPage = $page - ($page - 1) % $this->getChunkLength();
- $endPage = ($startPage + $this->getChunkLength()) - 1;
-
- // Check for EndPage out-range
- if ($endPage > $pager->getLastPage()) {
- $endPage = $pager->getLastPage();
- }
-
- // No need to check for out-range in start, it will never happens
-
- return range($startPage, $endPage);
- }
-
- throw new IPF_ORM_Exception(
- 'Cannot retrieve the range around the page of a not yet executed Pager query'
- );
- }
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_Pager_Range_Sliding extends IPF_ORM_Pager_Range
-{
- private $_chunkLength;
-
- protected function _initialize()
- {
- if (isset($this->_options['chunk'])) {
- $this->_setChunkLength($this->_options['chunk']);
- } else {
- throw new IPF_ORM_Exception('Missing parameter \'chunk\' that must be defined in options.');
- }
- }
-
- public function getChunkLength()
- {
- return $this->_chunkLength;
- }
-
- protected function _setChunkLength($chunkLength)
- {
- $chunkLength = (int) $chunkLength;
- if (!$chunkLength) {
- $chunkLength = 1;
- } else {
- $this->_chunkLength = $chunkLength;
- }
- }
-
- public function rangeAroundPage()
- {
- $pager = $this->getPager();
-
- if ($pager->getExecuted()) {
- $page = $pager->getPage();
- $pages = $pager->getLastPage();
-
- $chunk = $this->getChunkLength();
-
- if ($chunk > $pages) {
- $chunk = $pages;
- }
-
- $chunkStart = $page - (floor($chunk / 2));
- $chunkEnd = $page + (ceil($chunk / 2)-1);
-
- if ($chunkStart < 1) {
- $adjust = 1 - $chunkStart;
- $chunkStart = 1;
- $chunkEnd = $chunkEnd + $adjust;
- }
-
- if ($chunkEnd > $pages) {
- $adjust = $chunkEnd - $pages;
- $chunkStart = $chunkStart - $adjust;
- $chunkEnd = $pages;
- }
- return range($chunkStart, $chunkEnd);
- }
-
- throw new IPF_ORM_Exception(
- 'Cannot retrieve the range around the page of a not yet executed Pager query'
- );
- }
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_Query extends IPF_ORM_Query_Abstract implements Countable, Serializable
-{
- protected static $_keywords = array('ALL',
- 'AND',
- 'ANY',
- 'AS',
- 'ASC',
- 'AVG',
- 'BETWEEN',
- 'BIT_LENGTH',
- 'BY',
- 'CHARACTER_LENGTH',
- 'CHAR_LENGTH',
- 'CURRENT_DATE',
- 'CURRENT_TIME',
- 'CURRENT_TIMESTAMP',
- 'DELETE',
- 'DESC',
- 'DISTINCT',
- 'EMPTY',
- 'EXISTS',
- 'FALSE',
- 'FETCH',
- 'FROM',
- 'GROUP',
- 'HAVING',
- 'IN',
- 'INDEXBY',
- 'INNER',
- 'IS',
- 'JOIN',
- 'LEFT',
- 'LIKE',
- 'LOWER',
- 'MEMBER',
- 'MOD',
- 'NEW',
- 'NOT',
- 'NULL',
- 'OBJECT',
- 'OF',
- 'OR',
- 'ORDER',
- 'OUTER',
- 'POSITION',
- 'SELECT',
- 'SOME',
- 'TRIM',
- 'TRUE',
- 'UNKNOWN',
- 'UPDATE',
- 'WHERE');
-
- protected $_subqueryAliases = array();
- protected $_aggregateAliasMap = array();
- protected $_pendingAggregates = array();
- protected $_isSubquery;
- protected $_neededTables = array();
- protected $_pendingSubqueries = array();
- protected $_pendingFields = array();
- protected $_parsers = array();
- protected $_pendingJoinConditions = array();
- protected $_expressionMap = array();
- protected $_sql;
-
- public static function create($conn = null)
- {
- return new IPF_ORM_Query($conn);
- }
-
- public function reset()
- {
- $this->_pendingJoinConditions = array();
- $this->_pendingSubqueries = array();
- $this->_pendingFields = array();
- $this->_neededTables = array();
- $this->_expressionMap = array();
- $this->_subqueryAliases = array();
- }
-
- public function createSubquery()
- {
- $class = get_class($this);
- $obj = new $class();
-
- // copy the aliases to the subquery
- $obj->copyAliases($this);
-
- // this prevents the 'id' being selected, re ticket #307
- $obj->isSubquery(true);
-
- return $obj;
- }
-
- protected function _addPendingJoinCondition($componentAlias, $joinCondition)
- {
- $this->_pendingJoins[$componentAlias] = $joinCondition;
- }
-
- public function addEnumParam($key, $table = null, $column = null)
- {
- $array = (isset($table) || isset($column)) ? array($table, $column) : array();
-
- if ($key === '?') {
- $this->_enumParams[] = $array;
- } else {
- $this->_enumParams[$key] = $array;
- }
- }
-
- public function getEnumParams()
- {
- return $this->_enumParams;
- }
-
- public function getDql()
- {
- $q = '';
- $q .= ( ! empty($this->_dqlParts['select']))? 'SELECT ' . implode(', ', $this->_dqlParts['select']) : '';
- $q .= ( ! empty($this->_dqlParts['from']))? ' FROM ' . implode(' ', $this->_dqlParts['from']) : '';
- $q .= ( ! empty($this->_dqlParts['where']))? ' WHERE ' . implode(' AND ', $this->_dqlParts['where']) : '';
- $q .= ( ! empty($this->_dqlParts['groupby']))? ' GROUP BY ' . implode(', ', $this->_dqlParts['groupby']) : '';
- $q .= ( ! empty($this->_dqlParts['having']))? ' HAVING ' . implode(' AND ', $this->_dqlParts['having']) : '';
- $q .= ( ! empty($this->_dqlParts['orderby']))? ' ORDER BY ' . implode(', ', $this->_dqlParts['orderby']) : '';
- $q .= ( ! empty($this->_dqlParts['limit']))? ' LIMIT ' . implode(' ', $this->_dqlParts['limit']) : '';
- $q .= ( ! empty($this->_dqlParts['offset']))? ' OFFSET ' . implode(' ', $this->_dqlParts['offset']) : '';
-
- return $q;
- }
-
-
- public function fetchArray($params = array()) {
- return $this->execute($params, IPF_ORM::HYDRATE_ARRAY);
- }
-
- public function fetchOne($params = array(), $hydrationMode = null)
- {
- $collection = $this->execute($params, $hydrationMode);
-
- if (count($collection) === 0) {
- return false;
- }
-
- if ($collection instanceof IPF_ORM_Collection) {
- return $collection->getFirst();
- } else if (is_array($collection)) {
- return array_shift($collection);
- }
-
- return false;
- }
-
- public function isSubquery($bool = null)
- {
- if ($bool === null) {
- return $this->_isSubquery;
- }
-
- $this->_isSubquery = (bool) $bool;
- return $this;
- }
-
- public function getAggregateAlias($dqlAlias)
- {
- return $this->getSqlAggregateAlias($dqlAlias);
- }
-
- public function getSqlAggregateAlias($dqlAlias)
- {
- if (isset($this->_aggregateAliasMap[$dqlAlias])) {
- // mark the expression as used
- $this->_expressionMap[$dqlAlias][1] = true;
-
- return $this->_aggregateAliasMap[$dqlAlias];
- } else if ( ! empty($this->_pendingAggregates)) {
- $this->processPendingAggregates();
-
- return $this->getSqlAggregateAlias($dqlAlias);
- } else {
- throw new IPF_ORM_Exception('Unknown aggregate alias: ' . $dqlAlias);
- }
- }
-
- public function getDqlPart($queryPart)
- {
- if ( ! isset($this->_dqlParts[$queryPart])) {
- throw new IPF_ORM_Exception('Unknown query part ' . $queryPart);
- }
-
- return $this->_dqlParts[$queryPart];
- }
-
- public function contains($dql)
- {
- return stripos($this->getDql(), $dql) === false ? false : true;
- }
-
- public function processPendingFields($componentAlias)
- {
- $tableAlias = $this->getTableAlias($componentAlias);
- $table = $this->_queryComponents[$componentAlias]['table'];
-
- if ( ! isset($this->_pendingFields[$componentAlias])) {
- if ($this->_hydrator->getHydrationMode() != IPF_ORM::HYDRATE_NONE) {
- if ( ! $this->_isSubquery && $componentAlias == $this->getRootAlias()) {
- throw new IPF_ORM_Exception("The root class of the query (alias $componentAlias) "
- . " must have at least one field selected.");
- }
- }
- return;
- }
-
- // At this point we know the component is FETCHED (either it's the base class of
- // the query (FROM xyz) or its a "fetch join").
-
- // Check that the parent join (if there is one), is a "fetch join", too.
- if (isset($this->_queryComponents[$componentAlias]['parent'])) {
- $parentAlias = $this->_queryComponents[$componentAlias]['parent'];
- if (is_string($parentAlias) && ! isset($this->_pendingFields[$parentAlias])
- && $this->_hydrator->getHydrationMode() != IPF_ORM::HYDRATE_NONE) {
- throw new IPF_ORM_Exception("The left side of the join between "
- . "the aliases '$parentAlias' and '$componentAlias' must have at least"
- . " the primary key field(s) selected.");
- }
- }
-
- $fields = $this->_pendingFields[$componentAlias];
-
- // check for wildcards
- if (in_array('*', $fields)) {
- $fields = $table->getFieldNames();
- } else {
- // only auto-add the primary key fields if this query object is not
- // a subquery of another query object
- if ( ! $this->_isSubquery || $this->_hydrator->getHydrationMode() === IPF_ORM::HYDRATE_NONE) {
- $fields = array_unique(array_merge((array) $table->getIdentifier(), $fields));
- }
- }
-
- $sql = array();
- foreach ($fields as $fieldName) {
- $columnName = $table->getColumnName($fieldName);
- $sql[] = $this->_conn->quoteIdentifier($tableAlias . '.' . $columnName)
- . ' AS '
- . $this->_conn->quoteIdentifier($tableAlias . '__' . $columnName);
- }
-
- $this->_neededTables[] = $tableAlias;
-
- return implode(', ', $sql);
- }
-
- public function parseSelectField($field)
- {
- $terms = explode('.', $field);
-
- if (isset($terms[1])) {
- $componentAlias = $terms[0];
- $field = $terms[1];
- } else {
- reset($this->_queryComponents);
- $componentAlias = key($this->_queryComponents);
- $fields = $terms[0];
- }
-
- $tableAlias = $this->getTableAlias($componentAlias);
- $table = $this->_queryComponents[$componentAlias]['table'];
-
-
- // check for wildcards
- if ($field === '*') {
- $sql = array();
-
- foreach ($table->getColumnNames() as $field) {
- $sql[] = $this->parseSelectField($componentAlias . '.' . $field);
- }
-
- return implode(', ', $sql);
- } else {
- $name = $table->getColumnName($field);
-
- $this->_neededTables[] = $tableAlias;
-
- return $this->_conn->quoteIdentifier($tableAlias . '.' . $name)
- . ' AS '
- . $this->_conn->quoteIdentifier($tableAlias . '__' . $name);
- }
- }
-
- public function getExpressionOwner($expr)
- {
- if (strtoupper(substr(trim($expr, '( '), 0, 6)) !== 'SELECT') {
- preg_match_all("/[a-z_][a-z0-9_]*\.[a-z_][a-z0-9_]*[\.[a-z0-9]+]*/i", $expr, $matches);
-
- $match = current($matches);
-
- if (isset($match[0])) {
- $terms = explode('.', $match[0]);
-
- return $terms[0];
- }
- }
- return $this->getRootAlias();
-
- }
-
- public function parseSelect($dql)
- {
- $refs = $this->_tokenizer->sqlExplode($dql, ',');
-
- $pos = strpos(trim($refs[0]), ' ');
- $first = substr($refs[0], 0, $pos);
-
- // check for DISTINCT keyword
- if ($first === 'DISTINCT') {
- $this->_sqlParts['distinct'] = true;
-
- $refs[0] = substr($refs[0], ++$pos);
- }
-
- $parsedComponents = array();
-
- foreach ($refs as $reference) {
- $reference = trim($reference);
-
- if (empty($reference)) {
- continue;
- }
-
- $terms = $this->_tokenizer->sqlExplode($reference, ' ');
-
- $pos = strpos($terms[0], '(');
-
- if (count($terms) > 1 || $pos !== false) {
- $expression = array_shift($terms);
- $alias = array_pop($terms);
-
- if ( ! $alias) {
- $alias = substr($expression, 0, $pos);
- }
-
- $componentAlias = $this->getExpressionOwner($expression);
- $expression = $this->parseClause($expression);
-
- $tableAlias = $this->getTableAlias($componentAlias);
-
- $index = count($this->_aggregateAliasMap);
-
- $sqlAlias = $this->_conn->quoteIdentifier($tableAlias . '__' . $index);
-
- $this->_sqlParts['select'][] = $expression . ' AS ' . $sqlAlias;
-
- $this->_aggregateAliasMap[$alias] = $sqlAlias;
- $this->_expressionMap[$alias][0] = $expression;
-
- $this->_queryComponents[$componentAlias]['agg'][$index] = $alias;
-
- $this->_neededTables[] = $tableAlias;
- } else {
- $e = explode('.', $terms[0]);
-
- if (isset($e[1])) {
- $componentAlias = $e[0];
- $field = $e[1];
- } else {
- reset($this->_queryComponents);
- $componentAlias = key($this->_queryComponents);
- $field = $e[0];
- }
-
- $this->_pendingFields[$componentAlias][] = $field;
- }
- }
- }
-
- public function parseClause($clause)
- {
- $clause = trim($clause);
-
- if (is_numeric($clause)) {
- return $clause;
- }
-
- $terms = $this->_tokenizer->clauseExplode($clause, array(' ', '+', '-', '*', '/', '<', '>', '=', '>=', '<='));
-
- $str = '';
- foreach ($terms as $term) {
- $pos = strpos($term[0], '(');
-
- if ($pos !== false) {
- $name = substr($term[0], 0, $pos);
- $term[0] = $this->parseFunctionExpression($term[0]);
- } else {
- if (substr($term[0], 0, 1) !== "'" && substr($term[0], -1) !== "'") {
-
- if (strpos($term[0], '.') !== false) {
- if ( ! is_numeric($term[0])) {
- $e = explode('.', $term[0]);
-
- $field = array_pop($e);
-
- if ($this->getType() === IPF_ORM_Query::SELECT) {
- $componentAlias = implode('.', $e);
-
- if (empty($componentAlias)) {
- $componentAlias = $this->getRootAlias();
- }
-
- $this->load($componentAlias);
-
- // check the existence of the component alias
- if ( ! isset($this->_queryComponents[$componentAlias])) {
- throw new IPF_ORM_Exception('Unknown component alias ' . $componentAlias);
- }
-
- $table = $this->_queryComponents[$componentAlias]['table'];
-
- $def = $table->getDefinitionOf($field);
-
- // get the actual field name from alias
- $field = $table->getColumnName($field);
-
- // check column existence
- if ( ! $def) {
- throw new IPF_ORM_Exception('Unknown column ' . $field);
- }
-
- if (isset($def['owner'])) {
- $componentAlias = $componentAlias . '.' . $def['owner'];
- }
-
- $tableAlias = $this->getTableAlias($componentAlias);
-
- // build sql expression
- $term[0] = $this->_conn->quoteIdentifier($tableAlias)
- . '.'
- . $this->_conn->quoteIdentifier($field);
- } else {
- // build sql expression
- $field = $this->getRoot()->getColumnName($field);
- $term[0] = $this->_conn->quoteIdentifier($field);
- }
- }
- } else {
- if ( ! empty($term[0]) &&
- ! in_array(strtoupper($term[0]), self::$_keywords) &&
- ! is_numeric($term[0])) {
-
- $componentAlias = $this->getRootAlias();
-
- $found = false;
-
- if ($componentAlias !== false &&
- $componentAlias !== null) {
- $table = $this->_queryComponents[$componentAlias]['table'];
-
- // check column existence
- if ($table->hasField($term[0])) {
- $found = true;
-
- $def = $table->getDefinitionOf($term[0]);
-
- // get the actual column name from field name
- $term[0] = $table->getColumnName($term[0]);
-
-
- if (isset($def['owner'])) {
- $componentAlias = $componentAlias . '.' . $def['owner'];
- }
-
- $tableAlias = $this->getTableAlias($componentAlias);
-
- if ($this->getType() === IPF_ORM_Query::SELECT) {
- // build sql expression
- $term[0] = $this->_conn->quoteIdentifier($tableAlias)
- . '.'
- . $this->_conn->quoteIdentifier($term[0]);
- } else {
- // build sql expression
- $term[0] = $this->_conn->quoteIdentifier($term[0]);
- }
- } else {
- $found = false;
- }
- }
-
- if ( ! $found) {
- $term[0] = $this->getSqlAggregateAlias($term[0]);
- }
- }
- }
- }
- }
-
- $str .= $term[0] . $term[1];
- }
- return $str;
- }
-
- public function parseIdentifierReference($expr)
- {
- }
-
- public function parseFunctionExpression($expr)
- {
- $pos = strpos($expr, '(');
-
- $name = substr($expr, 0, $pos);
-
- if ($name === '') {
- return $this->parseSubquery($expr);
- }
-
- $argStr = substr($expr, ($pos + 1), -1);
-
- $args = array();
- // parse args
-
- foreach ($this->_tokenizer->sqlExplode($argStr, ',') as $arg) {
- $args[] = $this->parseClause($arg);
- }
-
- // convert DQL function to its RDBMS specific equivalent
- try {
- $expr = call_user_func_array(array($this->_conn->expression, $name), $args);
- } catch (IPF_ORM_Exception $e) {
- throw new IPF_ORM_Exception('Unknown function ' . $name . '.');
- }
-
- return $expr;
- }
-
- public function parseSubquery($subquery)
- {
- $trimmed = trim($this->_tokenizer->bracketTrim($subquery));
-
- // check for possible subqueries
- if (substr($trimmed, 0, 4) == 'FROM' || substr($trimmed, 0, 6) == 'SELECT') {
- // parse subquery
- $trimmed = $this->createSubquery()->parseDqlQuery($trimmed)->getQuery();
- } else {
- // parse normal clause
- $trimmed = $this->parseClause($trimmed);
- }
-
- return '(' . $trimmed . ')';
- }
-
- public function processPendingSubqueries()
- {
- foreach ($this->_pendingSubqueries as $value) {
- list($dql, $alias) = $value;
-
- $subquery = $this->createSubquery();
-
- $sql = $subquery->parseDqlQuery($dql, false)->getQuery();
-
- reset($this->_queryComponents);
- $componentAlias = key($this->_queryComponents);
- $tableAlias = $this->getTableAlias($componentAlias);
-
- $sqlAlias = $tableAlias . '__' . count($this->_aggregateAliasMap);
-
- $this->_sqlParts['select'][] = '(' . $sql . ') AS ' . $this->_conn->quoteIdentifier($sqlAlias);
-
- $this->_aggregateAliasMap[$alias] = $sqlAlias;
- $this->_queryComponents[$componentAlias]['agg'][] = $alias;
- }
- $this->_pendingSubqueries = array();
- }
-
- public function processPendingAggregates()
- {
- // iterate trhough all aggregates
- foreach ($this->_pendingAggregates as $aggregate) {
- list ($expression, $components, $alias) = $aggregate;
-
- $tableAliases = array();
-
- // iterate through the component references within the aggregate function
- if ( ! empty ($components)) {
- foreach ($components as $component) {
-
- if (is_numeric($component)) {
- continue;
- }
-
- $e = explode('.', $component);
-
- $field = array_pop($e);
- $componentAlias = implode('.', $e);
-
- // check the existence of the component alias
- if ( ! isset($this->_queryComponents[$componentAlias])) {
- throw new IPF_ORM_Exception('Unknown component alias ' . $componentAlias);
- }
-
- $table = $this->_queryComponents[$componentAlias]['table'];
-
- $field = $table->getColumnName($field);
-
- // check column existence
- if ( ! $table->hasColumn($field)) {
- throw new IPF_ORM_Exception('Unknown column ' . $field);
- }
-
- $sqlTableAlias = $this->getSqlTableAlias($componentAlias);
-
- $tableAliases[$sqlTableAlias] = true;
-
- // build sql expression
-
- $identifier = $this->_conn->quoteIdentifier($sqlTableAlias . '.' . $field);
- $expression = str_replace($component, $identifier, $expression);
- }
- }
-
- if (count($tableAliases) !== 1) {
- $componentAlias = reset($this->_tableAliasMap);
- $tableAlias = key($this->_tableAliasMap);
- }
-
- $index = count($this->_aggregateAliasMap);
- $sqlAlias = $this->_conn->quoteIdentifier($tableAlias . '__' . $index);
-
- $this->_sqlParts['select'][] = $expression . ' AS ' . $sqlAlias;
-
- $this->_aggregateAliasMap[$alias] = $sqlAlias;
- $this->_expressionMap[$alias][0] = $expression;
-
- $this->_queryComponents[$componentAlias]['agg'][$index] = $alias;
-
- $this->_neededTables[] = $tableAlias;
- }
- // reset the state
- $this->_pendingAggregates = array();
- }
-
- protected function _buildSqlQueryBase()
- {
- switch ($this->_type) {
- case self::DELETE:
- $q = 'DELETE FROM ';
- break;
- case self::UPDATE:
- $q = 'UPDATE ';
- break;
- case self::SELECT:
- $distinct = ($this->_sqlParts['distinct']) ? 'DISTINCT ' : '';
- $q = 'SELECT ' . $distinct . implode(', ', $this->_sqlParts['select']) . ' FROM ';
- break;
- }
- return $q;
- }
-
- protected function _buildSqlFromPart()
- {
- $q = '';
- foreach ($this->_sqlParts['from'] as $k => $part) {
- if ($k === 0) {
- $q .= $part;
- continue;
- }
-
- // preserve LEFT JOINs only if needed
- // Check if it's JOIN, if not add a comma separator instead of space
- if (!preg_match('/\bJOIN\b/i', $part) && !isset($this->_pendingJoinConditions[$k])) {
- $q .= ', ' . $part;
- } else {
- $e = explode(' ', $part);
-
- if (substr($part, 0, 9) === 'LEFT JOIN') {
- $aliases = array_merge($this->_subqueryAliases,
- array_keys($this->_neededTables));
-
- if ( ! in_array($e[3], $aliases) &&
- ! in_array($e[2], $aliases) &&
-
- ! empty($this->_pendingFields)) {
- continue;
- }
-
- }
-
- if (isset($this->_pendingJoinConditions[$k])) {
- $parser = new IPF_ORM_JoinCondition($this, $this->_tokenizer);
-
- if (strpos($part, ' ON ') !== false) {
- $part .= ' AND ';
- } else {
- $part .= ' ON ';
- }
- $part .= $parser->parse($this->_pendingJoinConditions[$k]);
-
- unset($this->_pendingJoinConditions[$k]);
- }
-
- $tableAlias = trim($e[3], '"');
- $componentAlias = $this->getComponentAlias($tableAlias);
-
- $string = $this->getInheritanceCondition($componentAlias);
-
- if ($string) {
- $q .= ' ' . $part . ' AND ' . $string;
- } else {
- $q .= ' ' . $part;
- }
- }
-
- $this->_sqlParts['from'][$k] = $part;
- }
- return $q;
- }
-
- public function getSqlQuery($params = array())
- {
- if ($this->_state !== self::STATE_DIRTY) {
- return $this->_sql;
- }
-
- // reset the state
- if ( ! $this->isSubquery()) {
- $this->_queryComponents = array();
- $this->_pendingAggregates = array();
- $this->_aggregateAliasMap = array();
- }
- $this->reset();
-
- // invoke the preQuery hook
- $this->_preQuery();
-
- // process the DQL parts => generate the SQL parts.
- // this will also populate the $_queryComponents.
- foreach ($this->_dqlParts as $queryPartName => $queryParts) {
- $this->_processDqlQueryPart($queryPartName, $queryParts);
- }
- $this->_state = self::STATE_CLEAN;
-
- $params = $this->convertEnums($params);
-
- // Proceed with the generated SQL
-
- if (empty($this->_sqlParts['from'])) {
- return false;
- }
-
- $map = reset($this->_queryComponents);
- $table = $map['table'];
- $rootAlias = key($this->_queryComponents);
-
- $sql = array();
- if ( ! empty($this->_pendingFields)) {
- foreach ($this->_queryComponents as $alias => $map) {
- $fieldSql = $this->processPendingFields($alias);
- if ( ! empty($fieldSql)) {
- $sql[] = $fieldSql;
- }
- }
- }
- if ( ! empty($sql)) {
- array_unshift($this->_sqlParts['select'], implode(', ', $sql));
- }
-
- $this->_pendingFields = array();
-
- // build the basic query
- $q = $this->_buildSqlQueryBase();
- $q .= $this->_buildSqlFromPart();
-
- if ( ! empty($this->_sqlParts['set'])) {
- $q .= ' SET ' . implode(', ', $this->_sqlParts['set']);
- }
-
- $string = $this->getInheritanceCondition($this->getRootAlias());
-
- // apply inheritance to WHERE part
- if ( ! empty($string)) {
- if (substr($string, 0, 1) === '(' && substr($string, -1) === ')') {
- $this->_sqlParts['where'][] = $string;
- } else {
- $this->_sqlParts['where'][] = '(' . $string . ')';
- }
- }
-
- $q .= ( ! empty($this->_sqlParts['where']))? ' WHERE ' . implode(' AND ', $this->_sqlParts['where']) : '';
- $q .= ( ! empty($this->_sqlParts['groupby']))? ' GROUP BY ' . implode(', ', $this->_sqlParts['groupby']) : '';
- $q .= ( ! empty($this->_sqlParts['having']))? ' HAVING ' . implode(' AND ', $this->_sqlParts['having']): '';
- $q .= ( ! empty($this->_sqlParts['orderby']))? ' ORDER BY ' . implode(', ', $this->_sqlParts['orderby']) : '';
-
- $q = $this->_conn->modifyLimitQuery($q, $this->_sqlParts['limit'], $this->_sqlParts['offset']);
-
- // return to the previous state
- if ( ! empty($string)) {
- array_pop($this->_sqlParts['where']);
- }
- $this->_sql = $q;
-
- return $q;
- }
-
- public function parseDqlQuery($query, $clear = true)
- {
- if ($clear) {
- $this->clear();
- }
-
- $query = trim($query);
- $query = str_replace("\r", "\n", str_replace("\r\n", "\n", $query));
- $query = str_replace("\n", ' ', $query);
-
- $parts = $this->_tokenizer->tokenizeQuery($query);
-
- foreach ($parts as $partName => $subParts) {
- $subParts = trim($subParts);
- $partName = strtolower($partName);
- switch ($partName) {
- case 'create':
- $this->_type = self::CREATE;
- break;
- case 'insert':
- $this->_type = self::INSERT;
- break;
- case 'delete':
- $this->_type = self::DELETE;
- break;
- case 'select':
- $this->_type = self::SELECT;
- $this->_addDqlQueryPart($partName, $subParts);
- break;
- case 'update':
- $this->_type = self::UPDATE;
- $partName = 'from';
- case 'from':
- $this->_addDqlQueryPart($partName, $subParts);
- break;
- case 'set':
- $this->_addDqlQueryPart($partName, $subParts, true);
- break;
- case 'group':
- case 'order':
- $partName .= 'by';
- case 'where':
- case 'having':
- case 'limit':
- case 'offset':
- $this->_addDqlQueryPart($partName, $subParts);
- break;
- }
- }
-
- return $this;
- }
-
- public function load($path, $loadFields = true)
- {
- if (isset($this->_queryComponents[$path])) {
- return $this->_queryComponents[$path];
- }
-
- $e = $this->_tokenizer->quoteExplode($path, ' INDEXBY ');
-
- $mapWith = null;
- if (count($e) > 1) {
- $mapWith = trim($e[1]);
-
- $path = $e[0];
- }
-
- // parse custom join conditions
- $e = explode(' ON ', $path);
-
- $joinCondition = '';
-
- if (count($e) > 1) {
- $joinCondition = $e[1];
- $overrideJoin = true;
- $path = $e[0];
- } else {
- $e = explode(' WITH ', $path);
-
- if (count($e) > 1) {
- $joinCondition = $e[1];
- $path = $e[0];
- }
- $overrideJoin = false;
- }
-
- $tmp = explode(' ', $path);
- $componentAlias = $originalAlias = (count($tmp) > 1) ? end($tmp) : null;
-
- $e = preg_split("/[.:]/", $tmp[0], -1);
-
- $fullPath = $tmp[0];
- $prevPath = '';
- $fullLength = strlen($fullPath);
-
- if (isset($this->_queryComponents[$e[0]])) {
- $table = $this->_queryComponents[$e[0]]['table'];
- $componentAlias = $e[0];
-
- $prevPath = $parent = array_shift($e);
- }
-
- foreach ($e as $key => $name) {
- // get length of the previous path
- $length = strlen($prevPath);
-
- // build the current component path
- $prevPath = ($prevPath) ? $prevPath . '.' . $name : $name;
-
- $delimeter = substr($fullPath, $length, 1);
-
- // if an alias is not given use the current path as an alias identifier
- if (strlen($prevPath) === $fullLength && isset($originalAlias)) {
- $componentAlias = $originalAlias;
- } else {
- $componentAlias = $prevPath;
- }
-
- // if the current alias already exists, skip it
- if (isset($this->_queryComponents[$componentAlias])) {
- throw new IPF_ORM_Exception("Duplicate alias '$componentAlias' in query.");
- }
-
- if ( ! isset($table)) {
- // process the root of the path
-
- $table = $this->loadRoot($name, $componentAlias);
- } else {
- $join = ($delimeter == ':') ? 'INNER JOIN ' : 'LEFT JOIN ';
-
- $relation = $table->getRelation($name);
- $localTable = $table;
-
- $table = $relation->getTable();
- $this->_queryComponents[$componentAlias] = array('table' => $table,
- 'parent' => $parent,
- 'relation' => $relation,
- 'map' => null);
-
- $localAlias = $this->getTableAlias($parent, $table->getTableName());
- $foreignAlias = $this->getTableAlias($componentAlias, $relation->getTable()->getTableName());
-
- $foreignSql = $this->_conn->quoteIdentifier($relation->getTable()->getTableName())
- . ' '
- . $this->_conn->quoteIdentifier($foreignAlias);
-
- $map = $relation->getTable()->inheritanceMap;
-
- if ( ! $loadFields || ! empty($map) || $joinCondition) {
- $this->_subqueryAliases[] = $foreignAlias;
- }
-
- if ($relation instanceof IPF_ORM_Relation_Association) {
- $asf = $relation->getAssociationTable();
-
- $assocTableName = $asf->getTableName();
-
- if ( ! $loadFields || ! empty($map) || $joinCondition) {
- $this->_subqueryAliases[] = $assocTableName;
- }
-
- $assocPath = $prevPath . '.' . $asf->getComponentName();
-
- $this->_queryComponents[$assocPath] = array('parent' => $prevPath, 'relation' => $relation, 'table' => $asf);
-
- $assocAlias = $this->getTableAlias($assocPath, $asf->getTableName());
-
- $queryPart = $join . $assocTableName . ' ' . $assocAlias;
-
- $queryPart .= ' ON ' . $localAlias
- . '.'
- . $localTable->getColumnName($localTable->getIdentifier()) // what about composite keys?
- . ' = '
- . $assocAlias . '.' . $relation->getLocal();
-
- if ($relation->isEqual()) {
- // equal nest relation needs additional condition
- $queryPart .= ' OR ' . $localAlias
- . '.'
- . $table->getColumnName($table->getIdentifier())
- . ' = '
- . $assocAlias . '.' . $relation->getForeign();
- }
-
- $this->_sqlParts['from'][] = $queryPart;
-
- $queryPart = $join . $foreignSql;
-
- if ( ! $overrideJoin) {
- $queryPart .= $this->buildAssociativeRelationSql($relation, $assocAlias, $foreignAlias, $localAlias);
- }
- } else {
- $queryPart = $this->buildSimpleRelationSql($relation, $foreignAlias, $localAlias, $overrideJoin, $join);
- }
-
- $queryPart .= $this->buildInheritanceJoinSql($table->getComponentName(), $componentAlias);
-
- $this->_sqlParts['from'][$componentAlias] = $queryPart;
- if ( ! empty($joinCondition)) {
- $this->_pendingJoinConditions[$componentAlias] = $joinCondition;
- }
- }
- if ($loadFields) {
-
- $restoreState = false;
- // load fields if necessary
- if ($loadFields && empty($this->_dqlParts['select'])) {
- $this->_pendingFields[$componentAlias] = array('*');
- }
- }
- $parent = $prevPath;
- }
-
- $table = $this->_queryComponents[$componentAlias]['table'];
-
- return $this->buildIndexBy($componentAlias, $mapWith);
- }
-
- protected function buildSimpleRelationSql(IPF_ORM_Relation $relation, $foreignAlias, $localAlias, $overrideJoin, $join)
- {
- $queryPart = $join . $this->_conn->quoteIdentifier($relation->getTable()->getTableName())
- . ' '
- . $this->_conn->quoteIdentifier($foreignAlias);
-
- if ( ! $overrideJoin) {
- $queryPart .= ' ON '
- . $this->_conn->quoteIdentifier($localAlias . '.' . $relation->getLocal())
- . ' = '
- . $this->_conn->quoteIdentifier($foreignAlias . '.' . $relation->getForeign());
- }
-
- return $queryPart;
- }
-
- protected function buildIndexBy($componentAlias, $mapWith = null)
- {
- $table = $this->_queryComponents[$componentAlias]['table'];
-
- $indexBy = null;
-
- if (isset($mapWith)) {
- $terms = explode('.', $mapWith);
-
- if (isset($terms[1])) {
- $indexBy = $terms[1];
- }
- } elseif ($table->getBoundQueryPart('indexBy') !== null) {
- $indexBy = $table->getBoundQueryPart('indexBy');
- }
-
- if ($indexBy !== null) {
- if ( ! $table->hasColumn($table->getColumnName($indexBy))) {
- throw new IPF_ORM_Exception("Couldn't use key mapping. Column " . $indexBy . " does not exist.");
- }
-
- $this->_queryComponents[$componentAlias]['map'] = $indexBy;
- }
-
- return $this->_queryComponents[$componentAlias];
- }
-
-
- protected function buildAssociativeRelationSql(IPF_ORM_Relation $relation, $assocAlias, $foreignAlias, $localAlias)
- {
- $table = $relation->getTable();
-
- $queryPart = ' ON ';
-
- if ($relation->isEqual()) {
- $queryPart .= '(';
- }
-
- $localIdentifier = $table->getColumnName($table->getIdentifier());
-
- $queryPart .= $this->_conn->quoteIdentifier($foreignAlias . '.' . $localIdentifier)
- . ' = '
- . $this->_conn->quoteIdentifier($assocAlias . '.' . $relation->getForeign());
-
- if ($relation->isEqual()) {
- $queryPart .= ' OR '
- . $this->_conn->quoteIdentifier($foreignAlias . '.' . $localIdentifier)
- . ' = '
- . $this->_conn->quoteIdentifier($assocAlias . '.' . $relation->getLocal())
- . ') AND '
- . $this->_conn->quoteIdentifier($foreignAlias . '.' . $localIdentifier)
- . ' != '
- . $this->_conn->quoteIdentifier($localAlias . '.' . $localIdentifier);
- }
-
- return $queryPart;
- }
-
- public function loadRoot($name, $componentAlias)
- {
- // get the connection for the component
- $manager = IPF_ORM_Manager::getInstance();
-
- $table = $this->_conn->getTable($name);
- $tableName = $table->getTableName();
-
- // get the short alias for this table
- $tableAlias = $this->getTableAlias($componentAlias, $tableName);
- // quote table name
- $queryPart = $this->_conn->quoteIdentifier($tableName);
-
- if ($this->_type === self::SELECT) {
- $queryPart .= ' ' . $this->_conn->quoteIdentifier($tableAlias);
- }
-
- $this->_tableAliasMap[$tableAlias] = $componentAlias;
-
- $queryPart .= $this->buildInheritanceJoinSql($name, $componentAlias);
-
- $this->_sqlParts['from'][] = $queryPart;
-
- $this->_queryComponents[$componentAlias] = array('table' => $table, 'map' => null);
-
- return $table;
- }
-
- public function buildInheritanceJoinSql($name, $componentAlias)
- {
- // get the connection for the component
- $manager = IPF_ORM_Manager::getInstance();
-
- $table = $this->_conn->getTable($name);
- $tableName = $table->getTableName();
-
- // get the short alias for this table
- $tableAlias = $this->getTableAlias($componentAlias, $tableName);
-
- return '';
- }
-
- public function getCountQuery()
- {
- // triggers dql parsing/processing
- $this->getQuery(); // this is ugly
-
- // initialize temporary variables
- $where = $this->_sqlParts['where'];
- $having = $this->_sqlParts['having'];
- $groupby = $this->_sqlParts['groupby'];
- $map = reset($this->_queryComponents);
- $componentAlias = key($this->_queryComponents);
- $tableAlias = $this->_conn->quoteIdentifier($this->getTableAlias($componentAlias));
- $table = $map['table'];
-
- $idColumnNames = array();
- foreach ($table->getIdentifierColumnNames() as $column) {
- $idColumnNames[] = $tableAlias . '.' . $this->_conn->quoteIdentifier($column);
- }
-
- // build the query base
- $q = 'SELECT COUNT(DISTINCT ' . implode(' || ', $idColumnNames) . ') AS num_results';
-
- foreach ($this->_sqlParts['select'] as $field) {
- if (strpos($field, '(') !== false) {
- $q .= ', ' . $field;
- }
- }
-
- $q .= ' FROM ' . $this->_buildSqlFromPart();
-
- // append column aggregation inheritance (if needed)
- $string = $this->getInheritanceCondition($this->getRootAlias());
-
- if ( ! empty($string)) {
- $where[] = $string;
- }
-
- // append conditions
- $q .= ( ! empty($where)) ? ' WHERE ' . implode(' AND ', $where) : '';
-
- if ( ! empty($groupby)) {
- // Maintain existing groupby
- $q .= ' GROUP BY ' . implode(', ', $groupby);
- } else {
- // Default groupby to primary identifier. Database defaults to this internally
- // This is required for situations where the user has aggregate functions in the select part
- // Without the groupby it fails
- $q .= ' GROUP BY ' . implode(', ', $idColumnNames);
- }
-
- $q .= ( ! empty($having)) ? ' HAVING ' . implode(' AND ', $having): '';
-
- return $q;
- }
-
- public function count($params = array())
- {
- $q = $this->getCountQuery();
-
- if ( ! is_array($params)) {
- $params = array($params);
- }
-
- $params = array_merge($this->_params['join'], $this->_params['where'], $this->_params['having'], $params);
-
- $params = $this->convertEnums($params);
-
- $results = $this->getConnection()->fetchAll($q, $params);
-
- if (count($results) > 1) {
- $count = count($results);
- } else {
- if (isset($results[0])) {
- $results[0] = array_change_key_case($results[0], CASE_LOWER);
- $count = $results[0]['num_results'];
- } else {
- $count = 0;
- }
- }
-
- return (int) $count;
- }
-
- public function query($query, $params = array(), $hydrationMode = null)
- {
- $this->parseDqlQuery($query);
- return $this->execute($params, $hydrationMode);
- }
-
- public function copy(IPF_ORM_Query $query = null)
- {
- if ( ! $query) {
- $query = $this;
- }
-
- $new = clone $query;
-
- return $new;
- }
-
- public function __clone()
- {
- $this->_parsers = array();
- }
-
- public function free()
- {
- $this->reset();
- $this->_parsers = array();
- $this->_dqlParts = array();
- $this->_enumParams = array();
- }
-
- public function serialize()
- {
- $vars = get_object_vars($this);
- }
-
- public function unserialize($serialized)
- {
- }
-}
+++ /dev/null
-<?php
-
-abstract class IPF_ORM_Query_Abstract
-{
- const SELECT = 0;
- const DELETE = 1;
- const UPDATE = 2;
- const INSERT = 3;
- const CREATE = 4;
-
- const STATE_CLEAN = 1;
- const STATE_DIRTY = 2;
- const STATE_DIRECT = 3;
- const STATE_LOCKED = 4;
-
- protected $_tableAliasMap = array();
- protected $_view;
- protected $_state = IPF_ORM_Query::STATE_CLEAN;
- protected $_params = array('join' => array(),
- 'where' => array(),
- 'set' => array(),
- 'having' => array());
-
- protected $_resultCache;
- protected $_expireResultCache = false;
- protected $_resultCacheTTL;
-
- protected $_queryCache;
- protected $_expireQueryCache = false;
- protected $_queryCacheTTL;
-
- protected $_conn;
-
- protected $_sqlParts = array(
- 'select' => array(),
- 'distinct' => false,
- 'forUpdate' => false,
- 'from' => array(),
- 'set' => array(),
- 'join' => array(),
- 'where' => array(),
- 'groupby' => array(),
- 'having' => array(),
- 'orderby' => array(),
- 'limit' => false,
- 'offset' => false,
- );
-
- protected $_dqlParts = array(
- 'from' => array(),
- 'select' => array(),
- 'forUpdate' => false,
- 'set' => array(),
- 'join' => array(),
- 'where' => array(),
- 'groupby' => array(),
- 'having' => array(),
- 'orderby' => array(),
- 'limit' => array(),
- 'offset' => array(),
- );
-
- protected $_queryComponents = array();
- protected $_type = self::SELECT;
- protected $_hydrator;
- protected $_tokenizer;
- protected $_parser;
- protected $_tableAliasSeeds = array();
- protected $_options = array(
- 'fetchMode' => IPF_ORM::FETCH_RECORD
- );
- protected $_enumParams = array();
-
- protected $_pendingSetParams = array();
- protected $_components;
- protected $_preQueried = false;
-
- public function __construct(IPF_ORM_Connection $connection = null,
- IPF_ORM_Hydrator_Abstract $hydrator = null)
- {
- if ($connection === null) {
- $connection = IPF_ORM_Manager::connection();
- }
- if ($hydrator === null) {
- $hydrator = new IPF_ORM_Hydrator();
- }
- $this->_conn = $connection;
- $this->_hydrator = $hydrator;
- $this->_tokenizer = new IPF_ORM_Query_Tokenizer();
- $this->_resultCacheTTL = $this->_conn->getAttribute(IPF_ORM::ATTR_RESULT_CACHE_LIFESPAN);
- $this->_queryCacheTTL = $this->_conn->getAttribute(IPF_ORM::ATTR_QUERY_CACHE_LIFESPAN);
- }
-
- public function setOption($name, $value)
- {
- if ( ! isset($this->_options[$name])) {
- throw new IPF_ORM_Exception('Unknown option ' . $name);
- }
- $this->_options[$name] = $value;
- }
-
- public function hasTableAlias($sqlTableAlias)
- {
- return $this->hasSqlTableAlias($sqlTableAlias);
- }
-
- public function hasSqlTableAlias($sqlTableAlias)
- {
- return (isset($this->_tableAliasMap[$sqlTableAlias]));
- }
-
- public function getTableAliases()
- {
- return $this->getTableAliasMap();
- }
-
- public function getTableAliasMap()
- {
- return $this->_tableAliasMap;
- }
-
- public function getQueryPart($part)
- {
- return $this->getSqlQueryPart($part);
- }
-
- public function getSqlQueryPart($part)
- {
- if ( ! isset($this->_sqlParts[$part])) {
- throw new IPF_ORM_Exception('Unknown SQL query part ' . $part);
- }
- return $this->_sqlParts[$part];
- }
-
- public function setQueryPart($name, $part)
- {
- return $this->setSqlQueryPart($name, $part);
- }
-
- public function setSqlQueryPart($name, $part)
- {
- if ( ! isset($this->_sqlParts[$name])) {
- throw new IPF_ORM_Exception('Unknown query part ' . $name);
- }
-
- if ($name !== 'limit' && $name !== 'offset') {
- if (is_array($part)) {
- $this->_sqlParts[$name] = $part;
- } else {
- $this->_sqlParts[$name] = array($part);
- }
- } else {
- $this->_sqlParts[$name] = $part;
- }
-
- return $this;
- }
-
- public function addQueryPart($name, $part)
- {
- return $this->addSqlQueryPart($name, $part);
- }
-
- public function addSqlQueryPart($name, $part)
- {
- if ( ! isset($this->_sqlParts[$name])) {
- throw new IPF_ORM_Exception('Unknown query part ' . $name);
- }
- if (is_array($part)) {
- $this->_sqlParts[$name] = array_merge($this->_sqlParts[$name], $part);
- } else {
- $this->_sqlParts[$name][] = $part;
- }
- return $this;
- }
-
- public function removeQueryPart($name)
- {
- return $this->removeSqlQueryPart($name);
- }
-
- public function removeSqlQueryPart($name)
- {
- if ( ! isset($this->_sqlParts[$name])) {
- throw new IPF_ORM_Exception('Unknown query part ' . $name);
- }
-
- if ($name == 'limit' || $name == 'offset') {
- $this->_sqlParts[$name] = false;
- } else {
- $this->_sqlParts[$name] = array();
- }
-
- return $this;
- }
-
- public function removeDqlQueryPart($name)
- {
- if ( ! isset($this->_dqlParts[$name])) {
- throw new IPF_ORM_Exception('Unknown query part ' . $name);
- }
-
- if ($name == 'limit' || $name == 'offset') {
- $this->_dqlParts[$name] = false;
- } else {
- $this->_dqlParts[$name] = array();
- }
-
- return $this;
- }
-
- public function getParams($params = array())
- {
- return array_merge($this->_params['join'], $this->_params['set'], $this->_params['where'], $this->_params['having'], $params);
- }
-
- public function setParams(array $params = array()) {
- $this->_params = $params;
- }
-
- public function setView(IPF_ORM_View $view)
- {
- $this->_view = $view;
- }
-
- public function getView()
- {
- return $this->_view;
- }
-
- public function convertEnums($params)
- {
- $table = $this->getRoot();
-
- // $position tracks the position of the parameter, to ensure we're converting
- // the right parameter value when simple ? placeholders are used.
- // This only works because SET is only allowed in update statements and it's
- // the first place where parameters can occur.. see issue #935
- $position = 0;
- foreach ($this->_pendingSetParams as $fieldName => $value) {
- $e = explode('.', $fieldName);
- $fieldName = isset($e[1]) ? $e[1]:$e[0];
- if ($table->getTypeOf($fieldName) == 'enum') {
- $value = $value === '?' ? $position : $value;
- $this->addEnumParam($value, $table, $fieldName);
- }
- ++$position;
- }
- $this->_pendingSetParams = array();
-
- foreach ($this->_enumParams as $key => $values) {
- if (isset($params[$key])) {
- if ( ! empty($values)) {
- $params[$key] = $values[0]->enumIndex($values[1], $params[$key]);
- }
- }
- }
-
- return $params;
- }
-
- public function getInheritanceCondition($componentAlias)
- {
- $map = $this->_queryComponents[$componentAlias]['table']->inheritanceMap;
-
- // No inheritance map so lets just return
- if (empty($map)) {
- return;
- }
-
- $tableAlias = $this->getSqlTableAlias($componentAlias);
-
- if ($this->_type !== IPF_ORM_Query::SELECT) {
- $tableAlias = '';
- } else {
- $tableAlias .= '.';
- }
-
- $field = key($map);
- $value = current($map);
- $identifier = $this->_conn->quoteIdentifier($tableAlias . $field);
-
- return $identifier . ' = ' . $this->_conn->quote($value);;
- }
-
- public function getTableAlias($componentAlias, $tableName = null)
- {
- return $this->getSqlTableAlias($componentAlias, $tableName);
- }
-
- public function getSqlTableAlias($componentAlias, $tableName = null)
- {
- $alias = array_search($componentAlias, $this->_tableAliasMap);
-
- if ($alias !== false) {
- return $alias;
- }
-
- if ($tableName === null) {
- throw new IPF_ORM_Exception("Couldn't get short alias for " . $componentAlias);
- }
-
- return $this->generateTableAlias($componentAlias, $tableName);
- }
-
- public function generateNewTableAlias($oldAlias)
- {
- return $this->generateNewSqlTableAlias($oldAlias);
- }
-
- public function generateNewSqlTableAlias($oldAlias)
- {
- if (isset($this->_tableAliasMap[$oldAlias])) {
- // generate a new alias
- $name = substr($oldAlias, 0, 1);
- $i = ((int) substr($oldAlias, 1));
-
- if ($i == 0) {
- $i = 1;
- }
-
- $newIndex = ($this->_tableAliasSeeds[$name] + $i);
-
- return $name . $newIndex;
- }
-
- return $oldAlias;
- }
-
- public function getTableAliasSeed($sqlTableAlias)
- {
- return $this->getSqlTableAliasSeed($sqlTableAlias);
- }
-
- public function getSqlTableAliasSeed($sqlTableAlias)
- {
- if ( ! isset($this->_tableAliasSeeds[$sqlTableAlias])) {
- return 0;
- }
- return $this->_tableAliasSeeds[$sqlTableAlias];
- }
-
- public function hasAliasDeclaration($componentAlias)
- {
- return isset($this->_queryComponents[$componentAlias]);
- }
-
- public function getAliasDeclaration($componentAlias)
- {
- return $this->getQueryComponent($componentAlias);
- }
-
- public function getQueryComponent($componentAlias)
- {
- if ( ! isset($this->_queryComponents[$componentAlias])) {
- throw new IPF_ORM_Exception('Unknown component alias ' . $componentAlias);
- }
-
- return $this->_queryComponents[$componentAlias];
- }
-
- public function copyAliases(IPF_ORM_Query_Abstract $query)
- {
- $this->_tableAliasMap = $query->_tableAliasMap;
- $this->_queryComponents = $query->_queryComponents;
- $this->_tableAliasSeeds = $query->_tableAliasSeeds;
- return $this;
- }
-
- public function getRootAlias()
- {
- if ( ! $this->_queryComponents) {
- $this->getSql();
- }
- reset($this->_queryComponents);
-
- return key($this->_queryComponents);
- }
-
- public function getRootDeclaration()
- {
- $map = reset($this->_queryComponents);
- return $map;
- }
-
- public function getRoot()
- {
- $map = reset($this->_queryComponents);
-
- if ( ! isset($map['table'])) {
- throw new IPF_ORM_Exception('Root component not initialized.');
- }
-
- return $map['table'];
- }
-
- public function generateTableAlias($componentAlias, $tableName)
- {
- return $this->generateSqlTableAlias($componentAlias, $tableName);
- }
-
- public function generateSqlTableAlias($componentAlias, $tableName)
- {
- preg_match('/([^_])/', $tableName, $matches);
- $char = strtolower($matches[0]);
-
- $alias = $char;
-
- if ( ! isset($this->_tableAliasSeeds[$alias])) {
- $this->_tableAliasSeeds[$alias] = 1;
- }
-
- while (isset($this->_tableAliasMap[$alias])) {
- if ( ! isset($this->_tableAliasSeeds[$alias])) {
- $this->_tableAliasSeeds[$alias] = 1;
- }
- $alias = $char . ++$this->_tableAliasSeeds[$alias];
- }
-
- $this->_tableAliasMap[$alias] = $componentAlias;
-
- return $alias;
- }
-
- public function getComponentAlias($sqlTableAlias)
- {
- if ( ! isset($this->_tableAliasMap[$sqlTableAlias])) {
- throw new IPF_ORM_Exception('Unknown table alias ' . $sqlTableAlias);
- }
- return $this->_tableAliasMap[$sqlTableAlias];
- }
-
- protected function _execute($params)
- {
- $params = $this->_conn->convertBooleans($params);
-
- if ( ! $this->_view) {
- if ($this->_queryCache || $this->_conn->getAttribute(IPF_ORM::ATTR_QUERY_CACHE)) {
- $queryCacheDriver = $this->getQueryCacheDriver();
- // calculate hash for dql query
- $dql = $this->getDql();
- $hash = md5($dql . 'IPF_ORM_QUERY_CACHE_SALT');
- $cached = $queryCacheDriver->fetch($hash);
- if ($cached) {
- $query = $this->_constructQueryFromCache($cached);
- } else {
- $query = $this->getSqlQuery($params);
- $serializedQuery = $this->getCachedForm($query);
- $queryCacheDriver->save($hash, $serializedQuery, $this->getQueryCacheLifeSpan());
- }
- } else {
- $query = $this->getSqlQuery($params);
- }
- $params = $this->convertEnums($params);
- } else {
- $query = $this->_view->getSelectSql();
- }
-
- if ($this->_type !== self::SELECT) {
- return $this->_conn->exec($query, $params);
- }
-
- $stmt = $this->_conn->execute($query, $params);
- return $stmt;
- }
-
- public function execute($params = array(), $hydrationMode = null)
- {
- $this->_preQuery();
-
- if ($hydrationMode !== null) {
- $this->_hydrator->setHydrationMode($hydrationMode);
- }
-
- $params = $this->getParams($params);
-
- if ($this->_resultCache && $this->_type == self::SELECT) {
- $cacheDriver = $this->getResultCacheDriver();
-
- $dql = $this->getDql();
- // calculate hash for dql query
- $hash = md5($dql . var_export($params, true));
-
- $cached = ($this->_expireResultCache) ? false : $cacheDriver->fetch($hash);
-
- if ($cached === false) {
- // cache miss
- $stmt = $this->_execute($params);
- $this->_hydrator->setQueryComponents($this->_queryComponents);
- $result = $this->_hydrator->hydrateResultSet($stmt, $this->_tableAliasMap);
-
- $cached = $this->getCachedForm($result);
- $cacheDriver->save($hash, $cached, $this->getResultCacheLifeSpan());
- } else {
- $result = $this->_constructQueryFromCache($cached);
- }
- } else {
- $stmt = $this->_execute($params);
-
- if (is_integer($stmt)) {
- $result = $stmt;
- } else {
- $this->_hydrator->setQueryComponents($this->_queryComponents);
- $result = $this->_hydrator->hydrateResultSet($stmt, $this->_tableAliasMap);
- }
- }
-
- return $result;
- }
-
- protected function _preQuery()
- {
- // Invoke preQuery() hook on IPF_ORM_Query for child classes which implement this hook
- $this->preQuery();
- }
-
- public function preQuery()
- {
- }
-
- protected function _constructQueryFromCache($cached)
- {
- $cached = unserialize($cached);
- $this->_tableAliasMap = $cached[2];
- $customComponent = $cached[0];
-
- $queryComponents = array();
- $cachedComponents = $cached[1];
- foreach ($cachedComponents as $alias => $components) {
- $e = explode('.', $components[0]);
- if (count($e) === 1) {
- $queryComponents[$alias]['table'] = $this->_conn->getTable($e[0]);
- } else {
- $queryComponents[$alias]['parent'] = $e[0];
- $queryComponents[$alias]['relation'] = $queryComponents[$e[0]]['table']->getRelation($e[1]);
- $queryComponents[$alias]['table'] = $queryComponents[$alias]['relation']->getTable();
- }
- if (isset($components[1])) {
- $queryComponents[$alias]['agg'] = $components[1];
- }
- if (isset($components[2])) {
- $queryComponents[$alias]['map'] = $components[2];
- }
- }
- $this->_queryComponents = $queryComponents;
-
- return $customComponent;
- }
-
- public function getCachedForm($customComponent = null)
- {
- $componentInfo = array();
-
- foreach ($this->getQueryComponents() as $alias => $components) {
- if ( ! isset($components['parent'])) {
- $componentInfo[$alias][] = $components['table']->getComponentName();
- } else {
- $componentInfo[$alias][] = $components['parent'] . '.' . $components['relation']->getAlias();
- }
- if (isset($components['agg'])) {
- $componentInfo[$alias][] = $components['agg'];
- }
- if (isset($components['map'])) {
- $componentInfo[$alias][] = $components['map'];
- }
- }
-
- return serialize(array($customComponent, $componentInfo, $this->getTableAliasMap()));
- }
-
- public function addSelect($select)
- {
- return $this->_addDqlQueryPart('select', $select, true);
- }
-
- public function addTableAlias($tableAlias, $componentAlias)
- {
- return $this->addSqlTableAlias($tableAlias, $componentAlias);
- }
-
- public function addSqlTableAlias($sqlTableAlias, $componentAlias)
- {
- $this->_tableAliasMap[$sqlTableAlias] = $componentAlias;
- return $this;
- }
-
- public function addFrom($from)
- {
- return $this->_addDqlQueryPart('from', $from, true);
- }
-
- public function addWhere($where, $params = array())
- {
- if (is_array($params)) {
- $this->_params['where'] = array_merge($this->_params['where'], $params);
- } else {
- $this->_params['where'][] = $params;
- }
- return $this->_addDqlQueryPart('where', $where, true);
- }
-
- public function whereIn($expr, $params = array(), $not = false)
- {
- $params = (array) $params;
-
- // if there's no params, return (else we'll get a WHERE IN (), invalid SQL)
- if (!count($params))
- return $this;
-
- $a = array();
- foreach ($params as $k => $value) {
- if ($value instanceof IPF_ORM_Expression) {
- $value = $value->getSql();
- unset($params[$k]);
- } else {
- $value = '?';
- }
- $a[] = $value;
- }
-
- $this->_params['where'] = array_merge($this->_params['where'], $params);
-
- $where = $expr . ($not === true ? ' NOT ':'') . ' IN (' . implode(', ', $a) . ')';
-
- return $this->_addDqlQueryPart('where', $where, true);
- }
-
- public function whereNotIn($expr, $params = array())
- {
- return $this->whereIn($expr, $params, true);
- }
-
- public function addGroupBy($groupby)
- {
- return $this->_addDqlQueryPart('groupby', $groupby, true);
- }
-
- public function addHaving($having, $params = array())
- {
- if (is_array($params)) {
- $this->_params['having'] = array_merge($this->_params['having'], $params);
- } else {
- $this->_params['having'][] = $params;
- }
- return $this->_addDqlQueryPart('having', $having, true);
- }
-
- public function addOrderBy($orderby)
- {
- return $this->_addDqlQueryPart('orderby', $orderby, true);
- }
-
- public function select($select)
- {
- return $this->_addDqlQueryPart('select', $select);
- }
-
- public function distinct($flag = true)
- {
- $this->_sqlParts['distinct'] = (bool) $flag;
- return $this;
- }
-
- public function forUpdate($flag = true)
- {
- $this->_sqlParts['forUpdate'] = (bool) $flag;
- return $this;
- }
-
- public function delete()
- {
- $this->_type = self::DELETE;
- return $this;
- }
-
- public function update($update)
- {
- $this->_type = self::UPDATE;
- return $this->_addDqlQueryPart('from', $update);
- }
-
- public function set($key, $value, $params = null)
- {
- if (is_array($key)) {
- foreach ($key as $k => $v) {
- $this->set($k, '?', array($v));
- }
- return $this;
- } else {
- if ($params !== null) {
- if (is_array($params)) {
- $this->_params['set'] = array_merge($this->_params['set'], $params);
- } else {
- $this->_params['set'][] = $params;
- }
- }
-
- $this->_pendingSetParams[$key] = $value;
-
- return $this->_addDqlQueryPart('set', $key . ' = ' . $value, true);
- }
- }
-
- public function from($from)
- {
- return $this->_addDqlQueryPart('from', $from);
- }
-
- public function innerJoin($join, $params = array())
- {
- if (is_array($params)) {
- $this->_params['join'] = array_merge($this->_params['join'], $params);
- } else {
- $this->_params['join'][] = $params;
- }
-
- return $this->_addDqlQueryPart('from', 'INNER JOIN ' . $join, true);
- }
-
- public function leftJoin($join, $params = array())
- {
- if (is_array($params)) {
- $this->_params['join'] = array_merge($this->_params['join'], $params);
- } else {
- $this->_params['join'][] = $params;
- }
-
- return $this->_addDqlQueryPart('from', 'LEFT JOIN ' . $join, true);
- }
-
- public function groupBy($groupby)
- {
- return $this->_addDqlQueryPart('groupby', $groupby);
- }
-
- public function where($where, $params = array())
- {
- $this->_params['where'] = array();
- if (is_array($params)) {
- $this->_params['where'] = $params;
- } else {
- $this->_params['where'][] = $params;
- }
-
- return $this->_addDqlQueryPart('where', $where);
- }
-
- public function having($having, $params = array())
- {
- $this->_params['having'] = array();
- if (is_array($params)) {
- $this->_params['having'] = $params;
- } else {
- $this->_params['having'][] = $params;
- }
-
- return $this->_addDqlQueryPart('having', $having);
- }
-
- public function orderBy($orderby)
- {
- return $this->_addDqlQueryPart('orderby', $orderby);
- }
-
- public function limit($limit)
- {
- return $this->_addDqlQueryPart('limit', $limit);
- }
-
- public function offset($offset)
- {
- return $this->_addDqlQueryPart('offset', $offset);
- }
-
- public function getSql($params = array())
- {
- return $this->getSqlQuery($params);
- }
-
- protected function clear()
- {
- $this->_sqlParts = array(
- 'select' => array(),
- 'distinct' => false,
- 'forUpdate' => false,
- 'from' => array(),
- 'set' => array(),
- 'join' => array(),
- 'where' => array(),
- 'groupby' => array(),
- 'having' => array(),
- 'orderby' => array(),
- 'limit' => false,
- 'offset' => false,
- );
- }
-
- public function setHydrationMode($hydrationMode)
- {
- $this->_hydrator->setHydrationMode($hydrationMode);
- return $this;
- }
-
- public function getAliasMap()
- {
- return $this->_queryComponents;
- }
-
- public function getQueryComponents()
- {
- return $this->_queryComponents;
- }
-
- public function getParts()
- {
- return $this->getSqlParts();
- }
-
- public function getSqlParts()
- {
- return $this->_sqlParts;
- }
-
- public function getType()
- {
- return $this->_type;
- }
-
- public function useCache($driver = true, $timeToLive = null)
- {
- return $this->useResultCache($driver, $timeToLive);
- }
-
- public function useResultCache($driver = true, $timeToLive = null)
- {
- if ($driver !== null && $driver !== true && ! ($driver instanceOf IPF_ORM_Cache_Interface)) {
- $msg = 'First argument should be instance of IPF_ORM_Cache_Interface or null.';
- throw new IPF_ORM_Exception($msg);
- }
- $this->_resultCache = $driver;
-
- return $this->setResultCacheLifeSpan($timeToLive);
- }
-
- public function useQueryCache(IPF_ORM_Cache_Interface $driver, $timeToLive = null)
- {
- $this->_queryCache = $driver;
- return $this->setQueryCacheLifeSpan($timeToLive);
- }
-
- public function expireCache($expire = true)
- {
- return $this->expireResultCache($expire);
- }
-
- public function expireResultCache($expire = true)
- {
- $this->_expireResultCache = true;
- return $this;
- }
-
- public function expireQueryCache($expire = true)
- {
- $this->_expireQueryCache = true;
- return $this;
- }
-
- public function setCacheLifeSpan($timeToLive)
- {
- return $this->setResultCacheLifeSpan($timeToLive);
- }
-
- public function setResultCacheLifeSpan($timeToLive)
- {
- if ($timeToLive !== null) {
- $timeToLive = (int) $timeToLive;
- }
- $this->_resultCacheTTL = $timeToLive;
-
- return $this;
- }
-
- public function getResultCacheLifeSpan()
- {
- return $this->_resultCacheTTL;
- }
-
- public function setQueryCacheLifeSpan($timeToLive)
- {
- if ($timeToLive !== null) {
- $timeToLive = (int) $timeToLive;
- }
- $this->_queryCacheTTL = $timeToLive;
-
- return $this;
- }
-
- public function getQueryCacheLifeSpan()
- {
- return $this->_queryCacheTTL;
- }
-
- public function getCacheDriver()
- {
- return $this->getResultCacheDriver();
- }
-
- public function getResultCacheDriver()
- {
- if ($this->_resultCache instanceof IPF_ORM_Cache_Interface) {
- return $this->_resultCache;
- } else {
- return $this->_conn->getResultCacheDriver();
- }
- }
-
- public function getQueryCacheDriver()
- {
- if ($this->_queryCache instanceof IPF_ORM_Cache_Interface) {
- return $this->_queryCache;
- } else {
- return $this->_conn->getQueryCacheDriver();
- }
- }
-
- public function getConnection()
- {
- return $this->_conn;
- }
-
- protected function _addDqlQueryPart($queryPartName, $queryPart, $append = false)
- {
- if ($append) {
- $this->_dqlParts[$queryPartName][] = $queryPart;
- } else {
- $this->_dqlParts[$queryPartName] = array($queryPart);
- }
-
- $this->_state = IPF_ORM_Query::STATE_DIRTY;
- return $this;
- }
-
- protected function _processDqlQueryPart($queryPartName, $queryParts)
- {
- $this->removeSqlQueryPart($queryPartName);
-
- if (is_array($queryParts) && ! empty($queryParts)) {
- foreach ($queryParts as $queryPart) {
- $parser = $this->_getParser($queryPartName);
- $sql = $parser->parse($queryPart);
- if (isset($sql)) {
- if ($queryPartName == 'limit' || $queryPartName == 'offset') {
- $this->setSqlQueryPart($queryPartName, $sql);
- } else {
- $this->addSqlQueryPart($queryPartName, $sql);
- }
- }
- }
- }
- }
-
- protected function _getParser($name)
- {
- if ( ! isset($this->_parsers[$name])) {
- $class = 'IPF_ORM_Query_' . ucwords(strtolower($name));
-
- //IPF_ORM::autoload($class);
-
- if ( ! class_exists($class)) {
- throw new IPF_ORM_Exception('Unknown parser ' . $name);
- }
-
- $this->_parsers[$name] = new $class($this, $this->_tokenizer);
- }
-
- return $this->_parsers[$name];
- }
-
- abstract public function getSqlQuery($params = array());
-
- abstract public function parseDqlQuery($query);
-
- public function parseQuery($query)
- {
- return $this->parseDqlQuery($query);
- }
-
- public function getQuery($params = array())
- {
- return $this->getSqlQuery($params);
- }
-
- public function __debugInfo()
- {
- return array('sql' => $this->getQuery());
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Query_Check
-{
- protected $table;
-
- protected $sql;
-
- protected $_tokenizer;
-
- public function __construct($table)
- {
- if ( ! ($table instanceof IPF_ORM_Table)) {
- $table = IPF_ORM_Manager::connection()->getTable($table);
- }
- $this->table = $table;
- $this->_tokenizer = new IPF_ORM_Query_Tokenizer();
- }
-
- public function getTable()
- {
- return $this->table;
- }
-
- public function parse($dql)
- {
- $this->sql = $this->parseClause($dql);
- }
-
- public function parseClause($dql)
- {
- $parts = $this->_tokenizer->sqlExplode($dql, ' AND ');
-
- if (count($parts) > 1) {
- $ret = array();
- foreach ($parts as $part) {
- $ret[] = $this->parseSingle($part);
- }
-
- $r = implode(' AND ', $ret);
- } else {
- $parts = $this->_tokenizer->quoteExplode($dql, ' OR ');
- if (count($parts) > 1) {
- $ret = array();
- foreach ($parts as $part) {
- $ret[] = $this->parseClause($part);
- }
-
- $r = implode(' OR ', $ret);
- } else {
- $ret = $this->parseSingle($dql);
- return $ret;
- }
- }
- return '(' . $r . ')';
- }
-
- public function parseSingle($part)
- {
- $e = explode(' ', $part);
-
- $e[0] = $this->parseFunction($e[0]);
-
- switch ($e[1]) {
- case '>':
- case '<':
- case '=':
- case '!=':
- case '<>':
-
- break;
- default:
- throw new IPF_ORM_Exception('Unknown operator ' . $e[1]);
- }
-
- return implode(' ', $e);
- }
- public function parseFunction($dql)
- {
- if (($pos = strpos($dql, '(')) !== false) {
- $func = substr($dql, 0, $pos);
- $value = substr($dql, ($pos + 1), -1);
-
- $expr = $this->table->getConnection()->expression;
-
- if ( ! method_exists($expr, $func)) {
- throw new IPF_ORM_Exception('Unknown function ' . $func);
- }
-
- $func = $expr->$func($value);
- }
- return $func;
- }
-
- public function getSql()
- {
- return $this->sql;
- }
-}
+++ /dev/null
-<?php
-
-abstract class IPF_ORM_Query_Condition extends IPF_ORM_Query_Part
-{
- public function parse($str)
- {
- $tmp = trim($str);
-
- $parts = $this->_tokenizer->bracketExplode($str, array(' \&\& ', ' AND '), '(', ')');
-
- if (count($parts) > 1) {
- $ret = array();
- foreach ($parts as $part) {
- $part = $this->_tokenizer->bracketTrim($part, '(', ')');
- $ret[] = $this->parse($part);
- }
- $r = implode(' AND ', $ret);
- } else {
-
- $parts = $this->_tokenizer->bracketExplode($str, array(' \|\| ', ' OR '), '(', ')');
- if (count($parts) > 1) {
- $ret = array();
- foreach ($parts as $part) {
- $part = $this->_tokenizer->bracketTrim($part, '(', ')');
- $ret[] = $this->parse($part);
- }
- $r = implode(' OR ', $ret);
- } else {
- // Fix for #710
- if (substr($parts[0],0,1) == '(' && substr($parts[0], -1) == ')') {
- return $this->parse(substr($parts[0], 1, -1));
- } else {
- // Processing NOT here
- if (strtoupper(substr($parts[0], 0, 4)) === 'NOT ') {
- $r = 'NOT ('.$this->parse(substr($parts[0], 4)).')';
- } else {
- return $this->load($parts[0]);
- }
- }
- }
- }
-
- return '(' . $r . ')';
- }
-
- private function parseBoolean($value)
- {
- // parse booleans
- if ($value == 'true') {
- $value = 1;
- } elseif ($value == 'false') {
- $value = 0;
- }
- return $value;
- }
-
- public function parseLiteralValue($value)
- {
- // check that value isn't a string
- if (strpos($value, '\'') === false) {
- // parse booleans
- $value = $this->parseBoolean($value);
-
- $a = explode('.', $value);
-
- if (count($a) > 1) {
- // either a float or a component..
-
- if ( ! is_numeric($a[0])) {
- // a component found
- $field = array_pop($a);
- $reference = implode('.', $a);
- $value = $this->query->getTableAlias($reference). '.' . $field;
- }
- }
- } else {
- // string literal found
- }
- return $value;
- }
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_Query_Filter implements IPF_ORM_Query_Filter_Interface
-{
- public function preQuery(IPF_ORM_Query $query)
- {
- }
-
- public function postQuery(IPF_ORM_Query $query)
- {
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-class IPF_ORM_Query_Filter_Chain
-{
- protected $_filters = array();
-
- public function add(IPF_ORM_Query_Filter $filter)
- {
- $this->_filters[] = $filter;
- }
-
- public function get($key)
- {
- if ( ! isset($this->_filters[$key])) {
- throw new IPF_ORM_Exception('Unknown filter ' . $key);
- }
- return $this->_filters[$key];
- }
-
- public function set($key, IPF_ORM_Query_Filter $listener)
- {
- $this->_filters[$key] = $listener;
- }
-
- public function preQuery(IPF_ORM_Query $query)
- {
- foreach ($this->_filters as $filter) {
- $filter->preQuery($query);
- }
- }
-
- public function postQuery(IPF_ORM_Query $query)
- {
- foreach ($this->_filters as $filter) {
- $filter->postQuery($query);
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-interface IPF_ORM_Query_Filter_Interface
-{
- public function preQuery(IPF_ORM_Query $query);
- public function postQuery(IPF_ORM_Query $query);
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-class IPF_ORM_Query_From extends IPF_ORM_Query_Part
-{
- public function parse($str, $return = false)
- {
- $str = trim($str);
- $parts = $this->_tokenizer->bracketExplode($str, 'JOIN');
-
- $from = $return ? array() : null;
-
- $operator = false;
-
- switch (trim($parts[0])) {
- case 'INNER':
- $operator = ':';
- case 'LEFT':
- array_shift($parts);
- break;
- }
-
- $last = '';
-
- foreach ($parts as $k => $part) {
- $part = trim($part);
-
- if (empty($part)) {
- continue;
- }
-
- $e = explode(' ', $part);
-
- if (end($e) == 'INNER' || end($e) == 'LEFT') {
- $last = array_pop($e);
- }
- $part = implode(' ', $e);
-
- foreach ($this->_tokenizer->bracketExplode($part, ',') as $reference) {
- $reference = trim($reference);
- $e = explode(' ', $reference);
- $e2 = explode('.', $e[0]);
-
- if ($operator) {
- $e[0] = array_shift($e2) . $operator . implode('.', $e2);
- }
-
- if ($return) {
- $from[] = $e;
- } else {
- $table = $this->query->load(implode(' ', $e));
- }
- }
-
- $operator = ($last == 'INNER') ? ':' : '.';
- }
- return $from;
- }
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_Query_Groupby extends IPF_ORM_Query_Part
-{
- public function parse($str, $append = false)
- {
- $r = array();
- foreach (explode(',', $str) as $reference) {
- $reference = trim($reference);
-
- $r[] = $this->query->parseClause($reference);
- }
- return implode(', ', $r);
- }
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_Query_Having extends IPF_ORM_Query_Condition
-{
- private function parseAggregateFunction($func)
- {
- $pos = strpos($func, '(');
-
- if ($pos !== false) {
- $funcs = array();
-
- $name = substr($func, 0, $pos);
- $func = substr($func, ($pos + 1), -1);
- $params = $this->_tokenizer->bracketExplode($func, ',', '(', ')');
-
- foreach ($params as $k => $param) {
- $params[$k] = $this->parseAggregateFunction($param);
- }
-
- $funcs = $name . '(' . implode(', ', $params) . ')';
-
- return $funcs;
-
- } else {
- if ( ! is_numeric($func)) {
- $a = explode('.', $func);
-
- if (count($a) > 1) {
- $field = array_pop($a);
- $reference = implode('.', $a);
- $map = $this->query->load($reference, false);
- $field = $map['table']->getColumnName($field);
- $func = $this->query->getTableAlias($reference) . '.' . $field;
- } else {
- $field = end($a);
- $func = $this->query->getAggregateAlias($field);
- }
- return $func;
- } else {
- return $func;
- }
- }
- }
-
- final public function load($having)
- {
- $tokens = $this->_tokenizer->bracketExplode($having, ' ', '(', ')');
- $part = $this->parseAggregateFunction(array_shift($tokens));
- $operator = array_shift($tokens);
- $value = implode(' ', $tokens);
- $part .= ' ' . $operator . ' ' . $value;
- // check the RHS for aggregate functions
- if (strpos($value, '(') !== false) {
- $value = $this->parseAggregateFunction($value);
- }
- return $part;
- }
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_Query_JoinCondition extends IPF_ORM_Query_Condition
-{
- public function load($condition)
- {
- $condition = trim($condition);
-
- $e = $this->_tokenizer->sqlExplode($condition);
-
- if (count($e) > 2) {
- $expr = new IPF_ORM_Expression($e[0], $this->query->getConnection());
- $e[0] = $expr->getSql();
-
- $operator = $e[1];
-
- if (substr(trim($e[2]), 0, 1) != '(') {
- $expr = new IPF_ORM_Expression($e[2], $this->query->getConnection());
- $e[2] = $expr->getSql();
- }
-
- // We need to check for agg functions here
- $hasLeftAggExpression = preg_match('/(.*)\(([^\)]*)\)([\)]*)/', $e[0], $leftMatches);
-
- if ($hasLeftAggExpression) {
- $e[0] = $leftMatches[2];
- }
-
- $hasRightAggExpression = preg_match('/(.*)\(([^\)]*)\)([\)]*)/', $e[2], $rightMatches);
-
- if ($hasRightAggExpression) {
- $e[2] = $rightMatches[2];
- }
-
- $a = explode('.', $e[0]);
- $field = array_pop($a);
- $reference = implode('.', $a);
- $value = $e[2];
-
- $conn = $this->query->getConnection();
- $alias = $this->query->getTableAlias($reference);
- $map = $this->query->getAliasDeclaration($reference);
- $table = $map['table'];
- // check if value is enumerated value
- $enumIndex = $table->enumIndex($field, trim($value, "'"));
-
- if (false !== $enumIndex && $conn->getAttribute(IPF_ORM::ATTR_USE_NATIVE_ENUM)) {
- $enumIndex = $conn->quote($enumIndex, 'text');
- }
-
- // FIX: Issues with "(" XXX ")"
- if ($hasRightAggExpression) {
- $value = '(' . $value . ')';
- }
-
- if (substr($value, 0, 1) == '(') {
- // trim brackets
- $trimmed = $this->_tokenizer->bracketTrim($value);
-
- if (substr($trimmed, 0, 4) == 'FROM' || substr($trimmed, 0, 6) == 'SELECT') {
- // subquery found
- $q = $this->query->createSubquery();
-
- // Change due to bug "(" XXX ")"
- //$value = '(' . $q->parseQuery($trimmed)->getQuery() . ')';
- $value = $q->parseQuery($trimmed)->getQuery();
- } elseif (substr($trimmed, 0, 4) == 'SQL:') {
- // Change due to bug "(" XXX ")"
- //$value = '(' . substr($trimmed, 4) . ')';
- $value = substr($trimmed, 4);
- } else {
- // simple in expression found
- $e = $this->_tokenizer->sqlExplode($trimmed, ',');
-
- $value = array();
- foreach ($e as $part) {
- $index = $table->enumIndex($field, trim($part, "'"));
-
- if (false !== $index && $conn->getAttribute(IPF_ORM::ATTR_USE_NATIVE_ENUM)) {
- $index = $conn->quote($index, 'text');
- }
-
- if ($index !== false) {
- $value[] = $index;
- } else {
- $value[] = $this->parseLiteralValue($part);
- }
- }
-
- // Change due to bug "(" XXX ")"
- //$value = '(' . implode(', ', $value) . ')';
- $value = implode(', ', $value);
- }
- } else {
- if ($enumIndex !== false) {
- $value = $enumIndex;
- } else {
- $value = $this->parseLiteralValue($value);
- }
- }
-
- switch ($operator) {
- case '<':
- case '>':
- case '=':
- case '!=':
- if ($enumIndex !== false) {
- $value = $enumIndex;
- }
- default:
- $leftExpr = (($hasLeftAggExpression) ? $leftMatches[1] . '(' : '')
- . $alias . '.' . $field
- . (($hasLeftAggExpression) ? $leftMatches[3] . ')' : '') ;
-
- $rightExpr = (($hasRightAggExpression) ? $rightMatches[1] . '(' : '')
- . $value
- . (($hasRightAggExpression) ? $rightMatches[3] . ')' : '') ;
-
- $condition = $leftExpr . ' ' . $operator . ' ' . $rightExpr;
- }
-
- }
-
- return $condition;
- }
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_Query_Limit extends IPF_ORM_Query_Part
-{
- public function parse($limit)
- {
- return (int) $limit;
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-class IPF_ORM_Query_Offset extends IPF_ORM_Query_Part
-{
- public function parse($offset)
- {
- return (int) $offset;
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-class IPF_ORM_Query_Orderby extends IPF_ORM_Query_Part
-{
- public function parse($str, $append = false)
- {
- $ret = array();
-
- foreach (explode(',', trim($str)) as $r) {
- $r = $this->query->parseClause($r);
-
- $ret[] = $r;
- }
- return $ret;
- }
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_Query_Parser
-{
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-abstract class IPF_ORM_Query_Part
-{
- protected $query;
-
- protected $_tokenizer;
-
- public function __construct($query, IPF_ORM_Query_Tokenizer $tokenizer = null)
- {
- $this->query = $query;
- if ( ! $tokenizer) {
- $tokenizer = new IPF_ORM_Query_Tokenizer();
- }
- $this->_tokenizer = $tokenizer;
- }
-
- public function getQuery()
- {
- return $this->query;
- }
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_Query_Registry
-{
- protected $_queries = array();
-
- public function add($key, $query)
- {
- if (strpos($key, '/') === false) {
- $this->_queries[$key] = $query;
- } else {
- // namespace found
- $e = explode('/', $key);
-
- $this->_queries[$e[0]][$e[1]] = $query;
- }
- }
-
- public function get($key, $namespace = null)
- {
- if (isset($namespace)) {
- if ( ! isset($this->_queries[$namespace][$key])) {
- throw new IPF_ORM_Exception('A query with the name ' . $namespace . '/' . $key . ' does not exist.');
- }
- $query = $this->_queries[$namespace][$key];
- } else {
- if ( ! isset($this->_queries[$key])) {
- throw new IPF_ORM_Exception('A query with the name ' . $key . ' does not exist.');
- }
- $query = $this->_queries[$key];
- }
-
- if ( ! ($query instanceof IPF_ORM_Query)) {
- $query = IPF_ORM_Query::create()->parseQuery($query);
- }
-
- return $query;
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-class IPF_ORM_Query_Select extends IPF_ORM_Query_Part
-{
- public function parse($dql)
- {
- $this->query->parseSelect($dql);
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-class IPF_ORM_Query_Set extends IPF_ORM_Query_Part
-{
- public function parse($dql)
- {
- $terms = $this->_tokenizer->sqlExplode($dql, ' ');
- foreach ($terms as $term) {
- preg_match_all("/[a-z0-9_]+\.[a-z0-9_]+[\.[a-z0-9]+]*/i", $term, $m);
-
- if (isset($m[0])) {
- foreach ($m[0] as $part) {
- $e = explode('.', trim($part));
- $field = array_pop($e);
-
- $reference = implode('.', $e);
-
- $alias = $this->query->getTableAlias($reference);
- $map = $this->query->getAliasDeclaration($reference);
-
- $dql = str_replace($part, $map['table']->getColumnName($field), $dql);
- }
- }
- }
- return $dql;
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-class IPF_ORM_Query_Tokenizer
-{
- public function tokenizeQuery($query)
- {
- $parts = array();
- $tokens = $this->sqlExplode($query, ' ');
-
- foreach ($tokens as $index => $token) {
- $token = trim($token);
- switch (strtolower($token)) {
- case 'delete':
- case 'update':
- case 'select':
- case 'set':
- case 'from':
- case 'where':
- case 'limit':
- case 'offset':
- case 'having':
- $p = $token;
- //$parts[$token] = array();
- $parts[$token] = '';
- break;
- case 'order':
- case 'group':
- $i = ($index + 1);
- if (isset($tokens[$i]) && strtolower($tokens[$i]) === 'by') {
- $p = $token;
- $parts[$token] = '';
- //$parts[$token] = array();
- } else {
- $parts[$p] .= "$token ";
- //$parts[$p][] = $token;
- }
- break;
- case 'by':
- continue;
- default:
- if ( ! isset($p)) {
- throw new IPF_ORM_Exception(
- "Couldn't tokenize query. Encountered invalid token: '$token'.");
- }
-
- $parts[$p] .= "$token ";
- //$parts[$p][] = $token;
- }
- }
- return $parts;
- }
-
- public function bracketTrim($str, $e1 = '(', $e2 = ')')
- {
- if (substr($str, 0, 1) === $e1 && substr($str, -1) === $e2) {
- return substr($str, 1, -1);
- } else {
- return $str;
- }
- }
-
- public function bracketExplode($str, $d = ' ', $e1 = '(', $e2 = ')')
- {
- if (is_array($d)) {
- $a = preg_split('#('.implode('|', $d).')#i', $str);
- $d = stripslashes($d[0]);
- } else {
- $a = explode($d, $str);
- }
-
- $i = 0;
- $term = array();
- foreach($a as $key=>$val) {
- if (empty($term[$i])) {
- $term[$i] = trim($val);
- $s1 = substr_count($term[$i], $e1);
- $s2 = substr_count($term[$i], $e2);
-
- if ($s1 == $s2) {
- $i++;
- }
- } else {
- $term[$i] .= $d . trim($val);
- $c1 = substr_count($term[$i], $e1);
- $c2 = substr_count($term[$i], $e2);
-
- if ($c1 == $c2) {
- $i++;
- }
- }
- }
- return $term;
- }
-
- public function quoteExplode($str, $d = ' ')
- {
- if (is_array($d)) {
- $a = preg_split('/('.implode('|', $d).')/', $str);
- $d = stripslashes($d[0]);
- } else {
- $a = explode($d, $str);
- }
-
- $i = 0;
- $term = array();
- foreach ($a as $key => $val) {
- if (empty($term[$i])) {
- $term[$i] = trim($val);
-
- if ( ! (substr_count($term[$i], "'") & 1)) {
- $i++;
- }
- } else {
- $term[$i] .= $d . trim($val);
-
- if ( ! (substr_count($term[$i], "'") & 1)) {
- $i++;
- }
- }
- }
- return $term;
- }
-
- public function sqlExplode($str, $d = ' ', $e1 = '(', $e2 = ')')
- {
- if ($d == ' ') {
- $d = array(' ', '\s');
- }
- if (is_array($d)) {
- $d = array_map('preg_quote', $d);
-
- if (in_array(' ', $d)) {
- $d[] = '\s';
- }
-
- $split = '#(' . implode('|', $d) . ')#';
-
- $str = preg_split($split, $str);
- $d = stripslashes($d[0]);
- } else {
- $str = explode($d, $str);
- }
-
- $i = 0;
- $term = array();
-
- foreach ($str as $key => $val) {
- if (empty($term[$i])) {
- $term[$i] = trim($val);
-
- $s1 = substr_count($term[$i], $e1);
- $s2 = substr_count($term[$i], $e2);
-
- if (strpos($term[$i], '(') !== false) {
- if ($s1 == $s2) {
- $i++;
- }
- } else {
- if ( ! (substr_count($term[$i], "'") & 1) &&
- ! (substr_count($term[$i], "\"") & 1)) {
- $i++;
- }
- }
- } else {
- $term[$i] .= $d . trim($val);
- $c1 = substr_count($term[$i], $e1);
- $c2 = substr_count($term[$i], $e2);
-
- if (strpos($term[$i], '(') !== false) {
- if ($c1 == $c2) {
- $i++;
- }
- } else {
- if ( ! (substr_count($term[$i], "'") & 1) &&
- ! (substr_count($term[$i], "\"") & 1)) {
- $i++;
- }
- }
- }
- }
- return $term;
- }
-
- public function clauseExplode($str, array $d, $e1 = '(', $e2 = ')')
- {
- if (is_array($d)) {
- $d = array_map('preg_quote', $d);
-
- if (in_array(' ', $d)) {
- $d[] = '\s';
- }
-
- $split = '#(' . implode('|', $d) . ')#';
-
- $str = preg_split($split, $str, -1, PREG_SPLIT_DELIM_CAPTURE);
- }
-
- $i = 0;
- $term = array();
-
- foreach ($str as $key => $val) {
- if ($key & 1) {
- if (isset($term[($i - 1)]) && ! is_array($term[($i - 1)])) {
- $term[($i - 1)] = array($term[($i - 1)], $val);
- }
- continue;
- }
- if (empty($term[$i])) {
- $term[$i] = $val;
- } else {
- $term[$i] .= $str[($key - 1)] . $val;
- }
-
- $c1 = substr_count($term[$i], $e1);
- $c2 = substr_count($term[$i], $e2);
-
- if (strpos($term[$i], '(') !== false) {
- if ($c1 == $c2) {
- $i++;
- }
- } else {
- if ( ! (substr_count($term[$i], "'") & 1) &&
- ! (substr_count($term[$i], "\"") & 1)) {
- $i++;
- }
- }
- }
-
- if (isset($term[$i - 1])) {
- $term[$i - 1] = array($term[$i - 1], '');
- }
-
- return $term;
- }
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_Query_Where extends IPF_ORM_Query_Condition
-{
- public function load($where)
- {
- $where = $this->_tokenizer->bracketTrim(trim($where));
- $conn = $this->query->getConnection();
- $terms = $this->_tokenizer->sqlExplode($where);
-
- if (count($terms) > 1) {
- if (substr($where, 0, 6) == 'EXISTS') {
- return $this->parseExists($where, true);
- } elseif (substr($where, 0, 10) == 'NOT EXISTS') {
- return $this->parseExists($where, false);
- }
- }
-
- if (count($terms) < 3) {
- $terms = $this->_tokenizer->sqlExplode($where, array('=', '<', '<>', '>', '!='));
- }
-
- if (count($terms) > 1) {
- $first = array_shift($terms);
- $value = array_pop($terms);
- $operator = trim(substr($where, strlen($first), -strlen($value)));
- $table = null;
- $field = null;
-
- if (strpos($first, "'") === false && strpos($first, '(') === false) {
- // normal field reference found
- $a = explode('.', $first);
-
- $field = array_pop($a);
- $reference = implode('.', $a);
-
- if (empty($reference)) {
- $map = $this->query->getRootDeclaration();
-
- $alias = $this->query->getTableAlias($this->query->getRootAlias());
- $table = $map['table'];
- } else {
- $map = $this->query->load($reference, false);
-
- $alias = $this->query->getTableAlias($reference);
- $table = $map['table'];
- }
- }
- $first = $this->query->parseClause($first);
-
- $sql = $first . ' ' . $operator . ' ' . $this->parseValue($value, $table, $field);
-
- return $sql;
- } else {
- return $where;
- }
- }
-
- public function parseValue($value, IPF_ORM_Table $table = null, $field = null)
- {
- $conn = $this->query->getConnection();
-
- if (substr($value, 0, 1) == '(') {
- // trim brackets
- $trimmed = $this->_tokenizer->bracketTrim($value);
-
- if (substr($trimmed, 0, 4) == 'FROM' ||
- substr($trimmed, 0, 6) == 'SELECT') {
-
- // subquery found
- $q = new IPF_ORM_Query();
- $value = '(' . $this->query->createSubquery()->parseQuery($trimmed, false)->getQuery() . ')';
-
- } elseif (substr($trimmed, 0, 4) == 'SQL:') {
- $value = '(' . substr($trimmed, 4) . ')';
- } else {
- // simple in expression found
- $e = $this->_tokenizer->sqlExplode($trimmed, ',');
-
- $value = array();
-
- $index = false;
-
- foreach ($e as $part) {
- if (isset($table) && isset($field)) {
- $index = $table->enumIndex($field, trim($part, "'"));
-
- if (false !== $index && $conn->getAttribute(IPF_ORM::ATTR_USE_NATIVE_ENUM)) {
- $index = $conn->quote($index, 'text');
- }
- }
-
- if ($index !== false) {
- $value[] = $index;
- } else {
- $value[] = $this->parseLiteralValue($part);
- }
- }
-
- $value = '(' . implode(', ', $value) . ')';
- }
- } else if (substr($value, 0, 1) == ':' || $value === '?') {
- // placeholder found
- if (isset($table) && isset($field) && $table->getTypeOf($field) == 'enum') {
- $this->query->addEnumParam($value, $table, $field);
- } else {
- $this->query->addEnumParam($value, null, null);
- }
- } else {
- $enumIndex = false;
- if (isset($table) && isset($field)) {
- // check if value is enumerated value
- $enumIndex = $table->enumIndex($field, trim($value, "'"));
-
- if (false !== $enumIndex && $conn->getAttribute(IPF_ORM::ATTR_USE_NATIVE_ENUM)) {
- $enumIndex = $conn->quote($enumIndex, 'text');
- }
- }
-
- if ($enumIndex !== false) {
- $value = $enumIndex;
- } else {
- $value = $this->parseLiteralValue($value);
- }
- }
- return $value;
- }
-
- public function parseExists($where, $negation)
- {
- $operator = ($negation) ? 'EXISTS' : 'NOT EXISTS';
-
- $pos = strpos($where, '(');
-
- if ($pos == false) {
- throw new IPF_ORM_Exception('Unknown expression, expected a subquery with () -marks');
- }
-
- $sub = $this->_tokenizer->bracketTrim(substr($where, $pos));
-
- return $operator . ' (' . $this->query->createSubquery()->parseQuery($sub, false)->getQuery() . ')';
- }
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_RawSql extends IPF_ORM_Query_Abstract
-{
- private $fields = array();
-
- public function parseDqlQueryPart($queryPartName, $queryPart, $append=false)
- {
- if ($queryPartName == 'select') {
- $this->_parseSelectFields($queryPart, $append);
- return $this;
- }
-
- if (!isset($this->parts[$queryPartName])) {
- $this->_sqlParts[$queryPartName] = array();
- }
-
- if (!$append) {
- $this->_sqlParts[$queryPartName] = array($queryPart);
- } else {
- $this->_sqlParts[$queryPartName][] = $queryPart;
- }
- return $this;
- }
-
- protected function _addDqlQueryPart($queryPartName, $queryPart, $append=false)
- {
- return $this->parseDqlQueryPart($queryPartName, $queryPart, $append);
- }
-
- private function _parseSelectFields($queryPart, $append=false)
- {
- if ($append)
- $this->fields[] = $queryPart;
- else
- $this->fields = array($queryPart);
-
- $this->_sqlParts['select'] = array();
- }
-
- public function parseDqlQuery($query)
- {
- $this->_parseSelectFields($query);
- $this->clear();
-
- $tokens = $this->_tokenizer->sqlExplode($query, ' ');
-
- $parts = array();
- foreach ($tokens as $key => $part) {
- $partLowerCase = strtolower($part);
- switch ($partLowerCase) {
- case 'select':
- case 'from':
- case 'where':
- case 'limit':
- case 'offset':
- case 'having':
- $type = $partLowerCase;
- if ( ! isset($parts[$partLowerCase])) {
- $parts[$partLowerCase] = array();
- }
- break;
- case 'order':
- case 'group':
- $i = $key + 1;
- if (isset($tokens[$i]) && strtolower($tokens[$i]) === 'by') {
- $type = $partLowerCase . 'by';
- $parts[$type] = array();
- } else {
- //not a keyword so we add it to the previous type
- $parts[$type][] = $part;
- }
- break;
- case 'by':
- continue;
- default:
- //not a keyword so we add it to the previous type.
- if ( ! isset($parts[$type][0])) {
- $parts[$type][0] = $part;
- } else {
- // why does this add to index 0 and not append to the
- // array. If it had done that one could have used
- // parseQueryPart.
- $parts[$type][0] .= ' '.$part;
- }
- }
- }
-
- $this->_sqlParts = $parts;
- $this->_sqlParts['select'] = array();
-
- return $this;
- }
-
- public function getSqlQuery($params=array())
- {
- $select = array();
-
- foreach ($this->fields as $field) {
- if (preg_match('/^{([^}{]+)\.([^}{]+)}$/U', $field, $e)) {
- // try to auto-add component
- if (!$this->hasSqlTableAlias($e[1])) {
- try {
- $this->addComponent($e[1], ucwords($e[1]));
- } catch (IPF_ORM_Exception $exception) {
- throw new IPF_ORM_Exception('The associated component for table alias ' . $e[1] . ' couldn\'t be found.');
- }
- }
-
- $componentAlias = $this->getComponentAlias($e[1]);
-
- if ($e[2] == '*') {
- foreach ($this->_queryComponents[$componentAlias]['table']->getColumnNames() as $name) {
- $field = $e[1] . '.' . $name;
-
- $select[$componentAlias][$field] = $field . ' AS ' . $e[1] . '__' . $name;
- }
- } else {
- $field = $e[1] . '.' . $e[2];
- $select[$componentAlias][$field] = $field . ' AS ' . $e[1] . '__' . $e[2];
- }
- } else {
- $select['__raw__'][] = $field;
- }
- }
-
- // force-add all primary key fields
-
- foreach ($this->getTableAliasMap() as $tableAlias => $componentAlias) {
- $map = $this->_queryComponents[$componentAlias];
-
- foreach ((array) $map['table']->getIdentifierColumnNames() as $key) {
- $field = $tableAlias . '.' . $key;
-
- if ( ! isset($this->_sqlParts['select'][$field])) {
- $select[$componentAlias][$field] = $field . ' AS ' . $tableAlias . '__' . $key;
- }
- }
- }
-
- // first add the fields of the root component
- reset($this->_queryComponents);
- $componentAlias = key($this->_queryComponents);
-
- $q = 'SELECT ' . implode(', ', $select[$componentAlias]);
- unset($select[$componentAlias]);
-
- foreach ($select as $component => $fields) {
- if ( ! empty($fields)) {
- $q .= ', ' . implode(', ', $fields);
- }
- }
-
- $string = $this->getInheritanceCondition($this->getRootAlias());
- if ( ! empty($string)) {
- $this->_sqlParts['where'][] = $string;
- }
- $copy = $this->_sqlParts;
- unset($copy['select']);
-
- $q .= ( ! empty($this->_sqlParts['from']))? ' FROM ' . implode(' ', $this->_sqlParts['from']) : '';
- $q .= ( ! empty($this->_sqlParts['where']))? ' WHERE ' . implode(' AND ', $this->_sqlParts['where']) : '';
- $q .= ( ! empty($this->_sqlParts['groupby']))? ' GROUP BY ' . implode(', ', $this->_sqlParts['groupby']) : '';
- $q .= ( ! empty($this->_sqlParts['having']))? ' HAVING ' . implode(' AND ', $this->_sqlParts['having']) : '';
- $q .= ( ! empty($this->_sqlParts['orderby']))? ' ORDER BY ' . implode(', ', $this->_sqlParts['orderby']) : '';
- $q .= ( ! empty($this->_sqlParts['limit']))? ' LIMIT ' . implode(' ', $this->_sqlParts['limit']) : '';
- $q .= ( ! empty($this->_sqlParts['offset']))? ' OFFSET ' . implode(' ', $this->_sqlParts['offset']) : '';
-
- if ( ! empty($string)) {
- array_pop($this->_sqlParts['where']);
- }
- return $q;
- }
-
- public function getFields()
- {
- return $this->fields;
- }
-
- public function addComponent($tableAlias, $path)
- {
- $tmp = explode(' ', $path);
- $originalAlias = (count($tmp) > 1) ? end($tmp) : null;
-
- $e = explode('.', $tmp[0]);
-
- $fullPath = $tmp[0];
- $fullLength = strlen($fullPath);
-
- $table = null;
-
- $currPath = '';
-
- if (isset($this->_queryComponents[$e[0]])) {
- $table = $this->_queryComponents[$e[0]]['table'];
- $currPath = $parent = array_shift($e);
- }
-
- foreach ($e as $k => $component) {
- // get length of the previous path
- $length = strlen($currPath);
-
- // build the current component path
- $currPath = ($currPath) ? $currPath . '.' . $component : $component;
-
- $delimeter = substr($fullPath, $length, 1);
-
- // if an alias is not given use the current path as an alias identifier
- if (strlen($currPath) === $fullLength && isset($originalAlias)) {
- $componentAlias = $originalAlias;
- } else {
- $componentAlias = $currPath;
- }
-
- if (!isset($table)) {
- $table = IPF_ORM_Manager::connection()->getTable($component);
- $this->_queryComponents[$componentAlias] = array('table' => $table);
- } else {
- $relation = $table->getRelation($component);
-
- $this->_queryComponents[$componentAlias] = array('table' => $relation->getTable(),
- 'parent' => $parent,
- 'relation' => $relation);
- }
- $this->addSqlTableAlias($tableAlias, $componentAlias);
-
- $parent = $currPath;
- }
-
- return $this;
- }
-}
-
+++ /dev/null
-<?php
-
-abstract class IPF_ORM_Record extends IPF_ORM_Record_Abstract implements Countable, IteratorAggregate, Serializable
-{
- const STATE_DIRTY = 1;
- const STATE_TDIRTY = 2;
- const STATE_CLEAN = 3;
- const STATE_PROXY = 4;
- const STATE_TCLEAN = 5;
- const STATE_LOCKED = 6;
-
- protected $_id = array();
- protected $_data = array();
- protected $_values = array();
- protected $_state;
- protected $_modified = array();
- protected $_errors = array();
- protected $_references = array();
- protected $_pendingDeletes = array();
- protected $_custom = array();
- private static $_index = 1;
- private $_oid;
-
- public function __construct($table = null, $isNewEntry = false)
- {
- if (isset($table) && $table instanceof IPF_ORM_Table) {
- $this->_table = $table;
- $exists = ( ! $isNewEntry);
- } else {
- // get the table of this class
- $class = get_class($this);
- $this->_table = IPF_ORM::getTable($class);
- $exists = false;
- }
-
- // Check if the current connection has the records table in its registry
- // If not this record is only used for creating table definition and setting up
- // relations.
- if ( ! $this->_table->getConnection()->hasTable($this->_table->getComponentName())) {
- return;
- }
-
- $this->_oid = self::$_index;
-
- self::$_index++;
-
- // get the data array
- $this->_data = $this->_table->getData();
-
- // get the column count
- $count = count($this->_data);
-
- $this->_values = $this->cleanData($this->_data);
-
- $this->prepareIdentifiers($exists);
-
- if ( ! $exists) {
- if ($count > count($this->_values)) {
- $this->_state = IPF_ORM_Record::STATE_TDIRTY;
- } else {
- $this->_state = IPF_ORM_Record::STATE_TCLEAN;
- }
-
- // set the default values for this record
- $this->assignDefaultValues();
- } else {
- $this->_state = IPF_ORM_Record::STATE_CLEAN;
-
- if ($count < $this->_table->getColumnCount()) {
- $this->_state = IPF_ORM_Record::STATE_PROXY;
- }
- }
-
- $repository = $this->_table->getRepository();
- $repository->add($this);
-
- $this->construct();
- }
-
- public static function _index()
- {
- return self::$_index;
- }
-
- public function construct(){}
-
- public function getOid()
- {
- return $this->_oid;
- }
-
- public function oid()
- {
- return $this->_oid;
- }
-
- public function isValid()
- {
- // Clear the stack from any previous errors.
- $this->_errors = array();
-
- // Run validation process
- $validator = new IPF_ORM_Validator();
- $validator->validateRecord($this);
- $this->validate();
- if ($this->_state == self::STATE_TDIRTY || $this->_state == self::STATE_TCLEAN) {
- $this->validateOnInsert();
- } else {
- $this->validateOnUpdate();
- }
-
- return count($this->_errors) === 0;
- }
-
- public function addError($invalidFieldName, $errorCode = 'general')
- {
- $this->_errors[$invalidFieldName][] = $errorCode;
- }
-
- public function getErrors()
- {
- return $this->_errors;
- }
-
- protected function validate(){}
-
- protected function validateOnUpdate(){}
-
- protected function validateOnInsert(){}
-
- public function preSerialize($event){}
-
- public function postSerialize($event){}
-
- public function preUnserialize($event){}
-
- public function postUnserialize($event){}
-
- public function preSave($event){}
-
- public function postSave($event){}
-
- public function preDelete($event){}
-
- public function postDelete($event){}
-
- public function preUpdate($event){}
-
- public function postUpdate($event){}
-
- public function preInsert($event){}
-
- public function postInsert($event){}
-
- public function assignDefaultValues($overwrite = false)
- {
- if ( ! $this->_table->hasDefaultValues()) {
- return false;
- }
- foreach ($this->_data as $column => $value) {
- $default = $this->_table->getDefaultValueOf($column);
-
- if ($default === null) {
- continue;
- }
-
- if (IPF_ORM_Null::isNull($value) || $overwrite) {
- $this->_data[$column] = $default;
- $this->_modified[] = $column;
- $this->_state = IPF_ORM_Record::STATE_TDIRTY;
- }
- }
- }
-
- public function cleanData(&$data)
- {
- $tmp = $data;
- $data = array();
-
- foreach ($this->getTable()->getFieldNames() as $fieldName) {
- if (isset($tmp[$fieldName])) {
- $data[$fieldName] = $tmp[$fieldName];
- } else if (array_key_exists($fieldName, $tmp)) {
- $data[$fieldName] = IPF_ORM_Null::getInstance();
- } else if (!isset($this->_data[$fieldName])) {
- $data[$fieldName] = IPF_ORM_Null::getInstance();
- }
- unset($tmp[$fieldName]);
- }
-
- return $tmp;
- }
-
- public function hydrate(array $data)
- {
- $this->_values = array_merge($this->_values, $this->cleanData($data));
- $this->_data = array_merge($this->_data, $data);
- $this->prepareIdentifiers(true);
- }
-
- private function prepareIdentifiers($exists = true)
- {
- switch ($this->_table->getIdentifierType()) {
- case IPF_ORM::IDENTIFIER_AUTOINC:
- case IPF_ORM::IDENTIFIER_NATURAL:
- $name = $this->_table->getIdentifier();
- if (is_array($name)) {
- $name = $name[0];
- }
- if ($exists) {
- if (isset($this->_data[$name]) && !IPF_ORM_Null::isNull($this->_data[$name])) {
- $this->_id[$name] = $this->_data[$name];
- }
- }
- break;
- case IPF_ORM::IDENTIFIER_COMPOSITE:
- $names = $this->_table->getIdentifier();
-
- foreach ($names as $name) {
- if (IPF_ORM_Null::isNull($this->_data[$name])) {
- $this->_id[$name] = null;
- } else {
- $this->_id[$name] = $this->_data[$name];
- }
- }
- break;
- }
- }
-
- public function serialize()
- {
- $event = new IPF_ORM_Event($this, IPF_ORM_Event::RECORD_SERIALIZE);
-
- $this->preSerialize($event);
-
- $vars = get_object_vars($this);
-
- unset($vars['_references']);
- unset($vars['_table']);
- unset($vars['_errors']);
- unset($vars['_filter']);
-
- $name = $this->_table->getIdentifier();
- $this->_data = array_merge($this->_data, $this->_id);
-
- foreach ($this->_data as $k => $v) {
- if ($v instanceof IPF_ORM_Record && $this->_table->getTypeOf($k) != 'object') {
- unset($vars['_data'][$k]);
- } elseif (IPF_ORM_Null::isNull($v)) {
- unset($vars['_data'][$k]);
- } else {
- switch ($this->_table->getTypeOf($k)) {
- case 'array':
- case 'object':
- $vars['_data'][$k] = serialize($vars['_data'][$k]);
- break;
- case 'gzip':
- $vars['_data'][$k] = gzcompress($vars['_data'][$k]);
- break;
- case 'enum':
- $vars['_data'][$k] = $this->_table->enumIndex($k, $vars['_data'][$k]);
- break;
- }
- }
- }
-
- $str = serialize($vars);
-
- $this->postSerialize($event);
-
- return $str;
- }
-
- public function unserialize($serialized)
- {
- $event = new IPF_ORM_Event($this, IPF_ORM_Event::RECORD_UNSERIALIZE);
-
- $this->preUnserialize($event);
-
- $connection = IPF_ORM_Manager::connection();
-
- $this->_oid = self::$_index;
- self::$_index++;
-
- $this->_table = $connection->getTable(get_class($this));
-
- $array = unserialize($serialized);
-
- foreach($array as $k => $v) {
- $this->$k = $v;
- }
-
- foreach ($this->_data as $k => $v) {
- switch ($this->_table->getTypeOf($k)) {
- case 'array':
- case 'object':
- $this->_data[$k] = unserialize($this->_data[$k]);
- break;
- case 'gzip':
- $this->_data[$k] = gzuncompress($this->_data[$k]);
- break;
- case 'enum':
- $this->_data[$k] = $this->_table->enumValue($k, $this->_data[$k]);
- break;
-
- }
- }
-
- $this->_table->getRepository()->add($this);
-
- $this->cleanData($this->_data);
-
- $this->prepareIdentifiers($this->exists());
-
- $this->postUnserialize($event);
- }
-
- public function state($state = null)
- {
- if ($state == null) {
- return $this->_state;
- }
-
- $err = false;
- if (is_integer($state)) {
- if ($state >= 1 && $state <= 6) {
- $this->_state = $state;
- } else {
- $err = true;
- }
- } else if (is_string($state)) {
- $upper = strtoupper($state);
-
- $const = 'IPF_ORM_Record::STATE_' . $upper;
- if (defined($const)) {
- $this->_state = constant($const);
- } else {
- $err = true;
- }
- }
-
- if ($this->_state === IPF_ORM_Record::STATE_TCLEAN ||
- $this->_state === IPF_ORM_Record::STATE_CLEAN) {
- $this->_modified = array();
- }
-
- if ($err) {
- throw new IPF_ORM_Exception('Unknown record state ' . $state);
- }
- }
-
- public function refresh($deep = false)
- {
- $id = $this->identifier();
- if ( ! is_array($id)) {
- $id = array($id);
- }
- if (empty($id)) {
- return false;
- }
- $id = array_values($id);
-
- if ($deep) {
- $query = $this->getTable()->createQuery();
- foreach (array_keys($this->_references) as $name) {
- $query->leftJoin(get_class($this) . '.' . $name);
- }
- $query->where(implode(' = ? AND ', $this->getTable()->getIdentifierColumnNames()) . ' = ?');
- $this->clearRelated();
- $record = $query->fetchOne($id);
- } else {
- // Use FETCH_ARRAY to avoid clearing object relations
- $record = $this->getTable()->find($id, IPF_ORM::HYDRATE_ARRAY);
- if ($record) {
- $this->hydrate($record);
- }
- }
-
- if ($record === false) {
- throw new IPF_ORM_Exception('Failed to refresh. Record does not exist.');
- }
-
- $this->_modified = array();
-
- $this->prepareIdentifiers();
-
- $this->_state = IPF_ORM_Record::STATE_CLEAN;
-
- return $this;
- }
-
- public function refreshRelated($name = null)
- {
- if (is_null($name)) {
- foreach ($this->_table->getRelations() as $rel) {
- $this->_references[$rel->getAlias()] = $rel->fetchRelatedFor($this);
- }
- } else {
- $rel = $this->_table->getRelation($name);
- $this->_references[$name] = $rel->fetchRelatedFor($this);
- }
- }
-
- public function clearRelated()
- {
- $this->_references = array();
- }
-
- public function getTable()
- {
- return $this->_table;
- }
-
- public function getData()
- {
- return $this->_data;
- }
-
- public function rawGet($fieldName)
- {
- if ( ! isset($this->_data[$fieldName])) {
- throw new IPF_ORM_Exception('Unknown property '. $fieldName);
- }
- if (IPF_ORM_Null::isNull($this->_data[$fieldName])) {
- return null;
- }
-
- return $this->_data[$fieldName];
- }
-
- public function load()
- {
- // only load the data from database if the IPF_ORM_Record is in proxy state
- if ($this->_state == IPF_ORM_Record::STATE_PROXY) {
- $this->refresh();
- $this->_state = IPF_ORM_Record::STATE_CLEAN;
- return true;
- }
- return false;
- }
-
- public function get($fieldName, $load = true)
- {
- $value = IPF_ORM_Null::getInstance();
-
- if (isset($this->_data[$fieldName])) {
- if (IPF_ORM_Null::isNull($this->_data[$fieldName]) && $load) {
- $this->load();
- }
- if (IPF_ORM_Null::isNull($this->_data[$fieldName])) {
- $value = null;
- } else {
- $value = $this->_data[$fieldName];
- }
- return $value;
- }
-
- if (isset($this->_values[$fieldName])) {
- return $this->_values[$fieldName];
- }
-
- if (!isset($this->_references[$fieldName]) && $load) {
- $rel = $this->_table->getRelation($fieldName);
- $this->_references[$fieldName] = $rel->fetchRelatedFor($this);
- }
- return $this->_references[$fieldName];
- }
-
- public function mapValue($name, $value)
- {
- $this->_values[$name] = $value;
- }
-
- public function set($fieldName, $value, $load = true)
- {
- if (isset($this->_data[$fieldName])) {
- $type = $this->_table->getTypeOf($fieldName);
- if ($value instanceof IPF_ORM_Record) {
- $id = $value->getIncremented();
-
- if ($id !== null && $type !== 'object') {
- $value = $id;
- }
- }
-
- if ($load) {
- $old = $this->get($fieldName, $load);
- } else {
- $old = $this->_data[$fieldName];
- }
-
- if ($this->_isValueModified($type, $old, $value)) {
- if ($value === null) {
- $value = IPF_ORM_Null::getInstance();
- }
-
- $this->_data[$fieldName] = $value;
- $this->_modified[] = $fieldName;
- switch ($this->_state) {
- case IPF_ORM_Record::STATE_CLEAN:
- $this->_state = IPF_ORM_Record::STATE_DIRTY;
- break;
- case IPF_ORM_Record::STATE_TCLEAN:
- $this->_state = IPF_ORM_Record::STATE_TDIRTY;
- break;
- }
- }
- } else {
- $this->coreSetRelated($fieldName, $value);
- }
-
- return $this;
- }
-
- protected function _isValueModified($type, $old, $new)
- {
- if ($type == 'boolean' && (is_bool($old) || is_numeric($old)) && (is_bool($new) || is_numeric($new)) && $old == $new) {
- return false;
- } else {
- return $old !== $new;
- }
- }
-
- public function coreSetRelated($name, $value)
- {
- $rel = $this->_table->getRelation($name);
-
- if ($value === null) {
- $value = IPF_ORM_Null::getInstance();
- }
-
- // one-to-many or one-to-one relation
- if ($rel instanceof IPF_ORM_Relation_ForeignKey || $rel instanceof IPF_ORM_Relation_LocalKey) {
- if ( ! $rel->isOneToOne()) {
- // one-to-many relation found
- if ( ! ($value instanceof IPF_ORM_Collection)) {
- throw new IPF_ORM_Exception("Couldn't call IPF_ORM::set(), second argument should be an instance of IPF_ORM_Collection when setting one-to-many references.");
- }
- if (isset($this->_references[$name])) {
- $this->_references[$name]->setData($value->getData());
- return $this;
- }
- } else {
- if (!IPF_ORM_Null::isNull($value)) {
- $relatedTable = $value->getTable();
- $foreignFieldName = $relatedTable->getFieldName($rel->getForeign());
- $localFieldName = $this->_table->getFieldName($rel->getLocal());
-
- // one-to-one relation found
- if ( ! ($value instanceof IPF_ORM_Record)) {
- throw new IPF_ORM_Exception("Couldn't call IPF_ORM::set(), second argument should be an instance of IPF_ORM_Record or IPF_ORM_Null when setting one-to-one references.");
- }
- if ($rel instanceof IPF_ORM_Relation_LocalKey) {
- if ( ! empty($foreignFieldName) && $foreignFieldName != $value->getTable()->getIdentifier()) {
- $this->set($localFieldName, $value->rawGet($foreignFieldName), false);
- } else {
- $this->set($localFieldName, $value, false);
- }
- } else {
- $value->set($foreignFieldName, $this, false);
- }
- }
- }
-
- } else if ($rel instanceof IPF_ORM_Relation_Association) {
- // join table relation found
- if ( ! ($value instanceof IPF_ORM_Collection)) {
- throw new IPF_ORM_Exception("Couldn't call IPF_ORM::set(), second argument should be an instance of IPF_ORM_Collection when setting many-to-many references.");
- }
- }
-
- $this->_references[$name] = $value;
- }
-
- public function contains($fieldName)
- {
- if (isset($this->_data[$fieldName])) {
- // this also returns true if the field is a IPF_ORM_Null.
- // imho this is not correct behavior.
- return true;
- }
- if (isset($this->_id[$fieldName])) {
- return true;
- }
- if (isset($this->_values[$fieldName])) {
- return true;
- }
- if (isset($this->_references[$fieldName]) && !IPF_ORM_Null::isNull($this->_references[$fieldName])) {
- return true;
- }
- return false;
- }
-
- public function __unset($name)
- {
- if (isset($this->_data[$name])) {
- $this->_data[$name] = array();
- } else if (isset($this->_references[$name])) {
- if ($this->_references[$name] instanceof IPF_ORM_Record) {
- $this->_pendingDeletes[] = $this->$name;
- $this->_references[$name] = IPF_ORM_Null::getInstance();
- } elseif ($this->_references[$name] instanceof IPF_ORM_Collection) {
- $this->_pendingDeletes[] = $this->$name;
- $this->_references[$name]->setData(array());
- }
- }
- }
-
- public function getPendingDeletes()
- {
- return $this->_pendingDeletes;
- }
-
- public function save(IPF_ORM_Connection $conn = null)
- {
- if ($conn === null) {
- $conn = $this->_table->getConnection();
- }
- $conn->unitOfWork->saveGraph($this);
- }
-
- public function trySave(IPF_ORM_Connection $conn = null) {
- try {
- $this->save($conn);
- return true;
- } catch (IPF_ORM_Exception_Validator $ignored) {
- return false;
- }
- }
-
- public function replace(IPF_ORM_Connection $conn = null)
- {
- if ($conn === null) {
- $conn = $this->_table->getConnection();
- }
-
- if ($this->exists()) {
- return $this->save();
- } else {
- $identifier = (array) $this->getTable()->getIdentifier();
- return $conn->replace($this->_table, $this->toArray(), $identifier);
- }
- }
-
- public function getModified()
- {
- $a = array();
-
- foreach ($this->_modified as $k => $v) {
- $a[$v] = $this->_data[$v];
- }
- return $a;
- }
-
- public function modifiedFields()
- {
- $a = array();
-
- foreach ($this->_modified as $k => $v) {
- $a[$v] = $this->_data[$v];
- }
- return $a;
- }
-
- public function getPrepared(array $array = array())
- {
- $a = array();
-
- if (empty($array)) {
- $modifiedFields = $this->_modified;
- }
-
- foreach ($modifiedFields as $field) {
- $type = $this->_table->getTypeOf($field);
-
- if (IPF_ORM_Null::isNull($this->_data[$field])) {
- $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 'enum':
- $a[$field] = $this->_table->enumIndex($field, $this->_data[$field]);
- break;
- default:
- if ($this->_data[$field] instanceof IPF_ORM_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 IPF_ORM_Record_Exception('Unexpected null value.');
- }
- */
- }
- }
- $map = $this->_table->inheritanceMap;
- foreach ($map as $k => $v) {
- $k = $this->_table->getFieldName($k);
- $old = $this->get($k, false);
-
- if ((string) $old !== (string) $v || $old === null) {
- $a[$k] = $v;
- $this->_data[$k] = $v;
- }
- }
-
- return $a;
- }
-
- public function count()
- {
- return count($this->_data);
- }
-
- public function columnCount()
- {
- return $this->count();
- }
-
- public function toArray($deep = true, $prefixKey = false)
- {
- if ($this->_state == self::STATE_LOCKED) {
- return false;
- }
-
- $stateBeforeLock = $this->_state;
- $this->_state = self::STATE_LOCKED;
-
- $a = array();
-
- foreach ($this as $column => $value) {
- if (IPF_ORM_Null::isNull($value) || is_object($value)) {
- $value = null;
- }
-
- $a[$column] = $value;
- }
-
- if ($this->_table->getIdentifierType() == IPF_ORM::IDENTIFIER_AUTOINC) {
- $i = $this->_table->getIdentifier();
- $a[$i] = $this->getIncremented();
- }
-
- if ($deep) {
- foreach ($this->_references as $key => $relation) {
- if (!IPF_ORM_Null::isNull($relation)) {
- $a[$key] = $relation->toArray($deep, $prefixKey);
- }
- }
- }
-
- // [FIX] Prevent mapped IPF_ORM_Records from being displayed fully
- foreach ($this->_values as $key => $value) {
- if ($value instanceof IPF_ORM_Record) {
- $a[$key] = $value->toArray($deep, $prefixKey);
- } else {
- $a[$key] = $value;
- }
- }
-
- $this->_state = $stateBeforeLock;
-
- return $a;
- }
-
- public function merge($data, $deep = true)
- {
- if ($data instanceof $this) {
- $array = $data->toArray($deep);
- } else if (is_array($data)) {
- $array = $data;
- }
-
- return $this->fromArray($array, $deep);
- }
-
- public function fromArray(array $array, $deep = true)
- {
- $refresh = false;
- foreach ($array as $key => $value) {
- if ($key == '_identifier') {
- $refresh = true;
- $this->assignIdentifier((array) $value);
- continue;
- }
-
- if ($deep && $this->getTable()->hasRelation($key)) {
- $this->$key->fromArray($value, $deep);
- } else if ($this->getTable()->hasField($key)) {
- $this->set($key, $value);
- }
- }
-
- if ($refresh) {
- $this->refresh();
- }
- }
-
- protected function _synchronizeWithArrayForRelation($key, $value)
- {
- $this->get($key)->synchronizeWithArray($value);
- }
-
- public function synchronizeWithArray(array $array, $deep = true)
- {
- $refresh = false;
- foreach ($array as $key => $value) {
- if ($key == '_identifier') {
- $refresh = true;
- $this->assignIdentifier((array) $value);
- continue;
- }
- if ($deep && $this->getTable()->hasRelation($key)) {
- $this->_synchronizeWithArrayForRelation($key, $value);
- } else if ($this->getTable()->hasField($key)) {
- $this->set($key, $value);
- }
- }
- // eliminate relationships missing in the $array
- foreach ($this->_references as $name => $obj) {
- if ( ! isset($array[$name])) {
- unset($this->$name);
- }
- }
- if ($refresh) {
- $this->refresh();
- }
- }
-
- public function exists()
- {
- return ($this->_state !== IPF_ORM_Record::STATE_TCLEAN &&
- $this->_state !== IPF_ORM_Record::STATE_TDIRTY);
- }
-
- public function isModified()
- {
- return ($this->_state === IPF_ORM_Record::STATE_DIRTY ||
- $this->_state === IPF_ORM_Record::STATE_TDIRTY);
- }
-
- public function hasRelation($fieldName)
- {
- if (isset($this->_data[$fieldName]) || isset($this->_id[$fieldName])) {
- return true;
- }
- return $this->_table->hasRelation($fieldName);
- }
-
- public function getIterator()
- {
- return new IPF_ORM_Record_Iterator($this);
- }
-
- public function delete(IPF_ORM_Connection $conn = null)
- {
- if ($conn == null) {
- $conn = $this->_table->getConnection();
- }
- return $conn->unitOfWork->delete($this);
- }
-
- public function copy($deep = false)
- {
- $data = $this->_data;
-
- if ($this->_table->getIdentifierType() === IPF_ORM::IDENTIFIER_AUTOINC) {
- $id = $this->_table->getIdentifier();
-
- unset($data[$id]);
- }
-
- $ret = $this->_table->create($data);
- $modified = array();
-
- foreach ($data as $key => $val) {
- if (!IPF_ORM_Null::isNull($val)) {
- $ret->_modified[] = $key;
- }
- }
-
- if ($deep) {
- foreach ($this->_references as $key => $value) {
- if ($value instanceof IPF_ORM_Collection) {
- foreach ($value as $record) {
- $ret->{$key}[] = $record->copy($deep);
- }
- } else if($value instanceof IPF_ORM_Record) {
- $ret->set($key, $value->copy($deep));
- }
- }
- }
-
- return $ret;
- }
-
- public function assignIdentifier($id = false)
- {
- if ($id === false) {
- $this->_id = array();
- $this->_data = $this->cleanData($this->_data);
- $this->_state = IPF_ORM_Record::STATE_TCLEAN;
- $this->_modified = array();
- } elseif ($id === true) {
- $this->prepareIdentifiers(true);
- $this->_state = IPF_ORM_Record::STATE_CLEAN;
- $this->_modified = array();
- } else {
- if (is_array($id)) {
- foreach ($id as $fieldName => $value) {
- $this->_id[$fieldName] = $value;
- $this->_data[$fieldName] = $value;
- }
- } else {
- $name = $this->_table->getIdentifier();
- $this->_id[$name] = $id;
- $this->_data[$name] = $id;
- }
- $this->_state = IPF_ORM_Record::STATE_CLEAN;
- $this->_modified = array();
- }
- }
-
- public function identifier()
- {
- return $this->_id;
- }
-
- final public function getIncremented()
- {
- $id = current($this->_id);
- if ($id === false) {
- return null;
- }
-
- return $id;
- }
-
- public function pk($sep='_')
- {
- $pk = '';
- foreach($this->_id as $val) {
- if ($pk!='')
- $pk .= $sep;
- $pk .= $val;
- }
- return $pk;
- }
-
- public function getLast()
- {
- return $this;
- }
-
- public function hasReference($name)
- {
- return isset($this->_references[$name]);
- }
-
- public function reference($name)
- {
- if (isset($this->_references[$name])) {
- return $this->_references[$name];
- }
- }
-
- public function obtainReference($name)
- {
- if (isset($this->_references[$name])) {
- return $this->_references[$name];
- }
- throw new IPF_ORM_Exception("Unknown reference $name");
- }
-
- public function getReferences()
- {
- return $this->_references;
- }
-
- final public function setRelated($alias, IPF_ORM_Access $coll)
- {
- $this->_references[$alias] = $coll;
- }
-
- public function loadReference($name)
- {
- $rel = $this->_table->getRelation($name);
- $this->_references[$name] = $rel->fetchRelatedFor($this);
- }
-
- public function call($callback, $column)
- {
- $args = func_get_args();
- array_shift($args);
-
- if (isset($args[0])) {
- $fieldName = $args[0];
- $args[0] = $this->get($fieldName);
-
- $newvalue = call_user_func_array($callback, $args);
-
- $this->_data[$fieldName] = $newvalue;
- }
- return $this;
- }
-
- public function unlink($alias, $ids = array())
- {
- $ids = (array) $ids;
-
- $q = new IPF_ORM_Query();
-
- $rel = $this->getTable()->getRelation($alias);
-
- if ($rel instanceof IPF_ORM_Relation_Association) {
- $q->delete()
- ->from($rel->getAssociationTable()->getComponentName())
- ->where($rel->getLocal() . ' = ?', array_values($this->identifier()));
-
- if (count($ids) > 0) {
- $q->whereIn($rel->getForeign(), $ids);
- }
-
- $q->execute();
-
- } else if ($rel instanceof IPF_ORM_Relation_ForeignKey) {
- $q->update($rel->getTable()->getComponentName())
- ->set($rel->getForeign(), '?', array(null))
- ->addWhere($rel->getForeign() . ' = ?', array_values($this->identifier()));
-
- if (count($ids) > 0) {
- $q->whereIn($rel->getTable()->getIdentifier(), $ids);
- }
-
- $q->execute();
- }
- if (isset($this->_references[$alias])) {
- foreach ($this->_references[$alias] as $k => $record) {
- if (in_array(current($record->identifier()), $ids)) {
- $this->_references[$alias]->remove($k);
- }
- }
- $this->_references[$alias]->takeSnapshot();
- }
- return $this;
- }
-
- public function link($alias, $ids)
- {
- $ids = (array) $ids;
-
- if ( ! count($ids)) {
- return $this;
- }
-
- $identifier = array_values($this->identifier());
- $identifier = array_shift($identifier);
-
- $rel = $this->getTable()->getRelation($alias);
-
- if ($rel instanceof IPF_ORM_Relation_Association) {
-
- $modelClassName = $rel->getAssociationTable()->getComponentName();
- $localFieldName = $rel->getLocalFieldName();
- $localFieldDef = $rel->getAssociationTable()->getColumnDefinition($localFieldName);
- if ($localFieldDef['type'] == 'integer') {
- $identifier = (integer) $identifier;
- }
- $foreignFieldName = $rel->getForeignFieldName();
- $foreignFieldDef = $rel->getAssociationTable()->getColumnDefinition($foreignFieldName);
- if ($foreignFieldDef['type'] == 'integer') {
- for ($i = 0; $i < count($ids); $i++) {
- $ids[$i] = (integer) $ids[$i];
- }
- }
-
- foreach ($ids as $id) {
- $record = new $modelClassName;
- $record[$localFieldName] = $identifier;
- $record[$foreignFieldName] = $id;
- $record->save();
- }
-
- } else if ($rel instanceof IPF_ORM_Relation_ForeignKey) {
-
- $q = new IPF_ORM_Query();
-
- $q->update($rel->getTable()->getComponentName())
- ->set($rel->getForeign(), '?', array_values($this->identifier()));
-
- if (count($ids) > 0) {
- $q->whereIn($rel->getTable()->getIdentifier(), $ids);
- }
-
- $q->execute();
-
- } else if ($rel instanceof IPF_ORM_Relation_LocalKey) {
-
- $q = new IPF_ORM_Query();
-
- $q->update($this->getTable()->getComponentName())
- ->set($rel->getLocalFieldName(), '?', $ids);
-
- if (count($ids) > 0) {
- $q->whereIn($rel->getTable()->getIdentifier(), array_values($this->identifier()));
- }
-
- $q->execute();
-
- }
-
- return $this;
- }
-
- public function __call($method, $args)
- {
- if (($template = $this->_table->getMethodOwner($method)) !== false) {
- $template->setInvoker($this);
- return call_user_func_array(array($template, $method), $args);
- }
-
- foreach ($this->_table->getTemplates() as $template) {
- if (method_exists($template, $method)) {
- $template->setInvoker($this);
- $this->_table->setMethodOwner($method, $template);
-
- return call_user_func_array(array($template, $method), $args);
- }
- }
-
- throw new IPF_ORM_Exception(sprintf('Unknown method %s::%s', get_class($this), $method));
- }
-
- public function free($deep = false)
- {
- if ($this->_state != self::STATE_LOCKED) {
- $this->_state = self::STATE_LOCKED;
-
- $this->_table->getRepository()->evict($this->_oid);
- $this->_table->removeRecord($this);
- $this->_data = array();
- $this->_id = array();
-
- if ($deep) {
- foreach ($this->_references as $name => $reference) {
- if (!IPF_ORM_Null::isNull($reference)) {
- $reference->free($deep);
- }
- }
- }
-
- $this->_references = array();
- }
- }
-
- public function toString()
- {
- return IPF_ORM::dump(get_object_vars($this));
- }
-
- public function __toString()
- {
- return sprintf('<%s #%d>', get_class($this), $this->_oid);
- }
-
- public function SetFromFormData($cleaned_values)
- {
- $names = $this->_table->getFieldNames();
- foreach ($cleaned_values as $key=>$val) {
- $validators = $this->getTable()->getFieldValidators($key);
- if (
- array_key_exists('image',$validators) ||
- array_key_exists('file',$validators) ||
- array_key_exists('email',$validators)
- ){
- if (($val!==null) && ($val==''))
- continue;
- }
- if (array_search($key,$names)){
- $this->$key = $val;
- }
- }
- }
-
- public function SetCustom($name, $val)
- {
- $this->_custom[$name] = $val;
- }
-
- public function GetCustom($name)
- {
- if (isset($this->_custom[$name]))
- return $this->_custom[$name];
- return null;
- }
-}
-
+++ /dev/null
-<?php
-
-abstract class IPF_ORM_Record_Abstract extends IPF_ORM_Access
-{
- protected $_table;
-
- public static function setTableDefinition(IPF_ORM_Table $table)
- {
- }
-
- public static function setUp(IPF_ORM_Table $table)
- {
- }
-
- public function getTable()
- {
- return $this->_table;
- }
-
- public function setInheritanceMap($map)
- {
- $this->_table->setOption('inheritanceMap', $map);
- }
-
- public function attribute($attr, $value)
- {
- if ($value == null) {
- if (is_array($attr)) {
- foreach ($attr as $k => $v) {
- $this->_table->setAttribute($k, $v);
- }
- } else {
- return $this->_table->getAttribute($attr);
- }
- } else {
- $this->_table->setAttribute($attr, $value);
- }
- }
-
- public function check($constraint, $name = null)
- {
- if (is_array($constraint)) {
- foreach ($constraint as $name => $def) {
- $this->_table->addCheckConstraint($def, $name);
- }
- } else {
- $this->_table->addCheckConstraint($constraint, $name);
- }
- return $this;
- }
-
- public function __debugInfo()
- {
- $r = array();
- foreach ($this->getTable()->getColumnNames() as $column)
- $r[$column] = $this->get($column);
- return $r;
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Record_Iterator extends ArrayIterator
-{
- private $record;
-
- public function __construct(IPF_ORM_Record $record)
- {
- $this->record = $record;
- parent::__construct($record->getData());
- }
-
- public function current()
- {
- $value = parent::current();
-
- if (IPF_ORM_Null::isNull($value)) {
- return null;
- } else {
- return $value;
- }
- }
-}
-
+++ /dev/null
-<?php
-
-abstract class IPF_ORM_Relation implements ArrayAccess
-{
- const ONE_AGGREGATE = 0;
- const ONE_COMPOSITE = 1;
- const MANY_AGGREGATE = 2;
- const MANY_COMPOSITE = 3;
-
- const ONE = 0;
- const MANY = 2;
-
- protected $definition = array('alias' => true,
- 'foreign' => true,
- 'local' => true,
- 'class' => true,
- 'type' => true,
- 'table' => true,
- 'localTable' => true,
- 'name' => null,
- 'refTable' => null,
- 'onDelete' => null,
- 'onUpdate' => null,
- 'deferred' => null,
- 'deferrable' => null,
- 'constraint' => null,
- 'equal' => false,
- 'cascade' => array(), // application-level cascades
- 'owningSide' => false, // whether this is the owning side
- 'exclude' => false,
- );
-
- public function __construct(array $definition)
- {
- $def = array();
- foreach ($this->definition as $key => $val) {
- if ( ! isset($definition[$key]) && $val) {
- throw new IPF_ORM_Exception($key . ' is required!');
- }
- if (isset($definition[$key])) {
- $def[$key] = $definition[$key];
- } else {
- $def[$key] = $this->definition[$key];
- }
- }
- $this->definition = $def;
- }
-
- public function hasConstraint()
- {
- return $this->definition['constraint'] ||
- $this->definition['onUpdate'] ||
- $this->definition['onDelete'];
- }
-
- public function isDeferred()
- {
- return $this->definition['deferred'];
- }
-
- public function isDeferrable()
- {
- return $this->definition['deferrable'];
- }
- public function isEqual()
- {
- return $this->definition['equal'];
- }
-
- public function offsetExists($offset)
- {
- return isset($this->definition[$offset]);
- }
-
- public function offsetGet($offset)
- {
- if (isset($this->definition[$offset])) {
- return $this->definition[$offset];
- }
-
- return null;
- }
-
- public function offsetSet($offset, $value)
- {
- if (isset($this->definition[$offset])) {
- $this->definition[$offset] = $value;
- }
- }
-
- public function offsetUnset($offset)
- {
- $this->definition[$offset] = false;
- }
-
- public function toArray()
- {
- return $this->definition;
- }
-
- final public function getAlias()
- {
- return $this->definition['alias'];
- }
-
- final public function getType()
- {
- return $this->definition['type'];
- }
-
- public function isCascadeDelete()
- {
- return in_array('delete', $this->definition['cascade']);
- }
-
- final public function getTable()
- {
- return IPF_ORM_Manager::connection()->getTable($this->definition['class']);
- }
-
- final public function getClass()
- {
- return $this->definition['class'];
- }
-
- final public function getLocal()
- {
- return $this->definition['local'];
- }
-
- final public function getLocalFieldName()
- {
- return $this->definition['localTable']->getFieldName($this->definition['local']);
- }
-
- final public function getForeign()
- {
- return $this->definition['foreign'];
- }
-
- final public function getForeignFieldName()
- {
- return $this->definition['table']->getFieldName($this->definition['foreign']);
- }
-
- final public function isComposite()
- {
- return ($this->definition['type'] == IPF_ORM_Relation::ONE_COMPOSITE ||
- $this->definition['type'] == IPF_ORM_Relation::MANY_COMPOSITE);
- }
-
- final public function isOneToOne()
- {
- return ($this->definition['type'] == IPF_ORM_Relation::ONE_AGGREGATE ||
- $this->definition['type'] == IPF_ORM_Relation::ONE_COMPOSITE);
- }
-
- abstract public function getRelationDql($count, $context);
-
- abstract public function fetchRelatedFor(IPF_ORM_Record $record);
-
- public function __toString()
- {
- $r[] = "<pre>";
- foreach ($this->definition as $k => $v) {
- if (is_object($v)) {
- $v = 'Object(' . get_class($v) . ')';
- }
- $r[] = $k . ' : ' . $v;
- }
- $r[] = "</pre>";
- return implode("\n", $r);
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Relation_Association extends IPF_ORM_Relation
-{
- public function getAssociationFactory()
- {
- return $this->definition['refTable'];
- }
- public function getAssociationTable()
- {
- return $this->definition['refTable'];
- }
-
- public function getRelationDql($count, $context)
- {
- $component = $this->definition['refTable']->getComponentName();
- $thisTable = $this->getTable()->getComponentName();
-
- $sub = substr(str_repeat('?, ', $count), 0, -2);
- switch ($context) {
- case "record":
- $dql = 'FROM ' . $thisTable . '.' . $component .
- ' WHERE ' . $thisTable . '.' . $component . '.' . $this->definition['local'] . ' IN (' . $sub . ')';
- break;
- case "collection":
- $dql = 'FROM ' . $component . '.' . $thisTable .
- ' WHERE ' . $component . '.' . $this->definition['local'] . ' IN (' . $sub . ')';
- break;
- }
- return $dql;
- }
-
- public function fetchRelatedFor(IPF_ORM_Record $record)
- {
- $id = $record->pk();
- if (empty($id) || ! $this->definition['table']->getAttribute(IPF_ORM::ATTR_LOAD_REFERENCES)) {
- $coll = new IPF_ORM_Collection($this->getTable());
- } else {
- $coll = $this->getTable()->getConnection()->query($this->getRelationDql(1, 'record'), array($id));
- }
- $coll->setReference($record, $this);
- return $coll;
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Relation_ForeignKey extends IPF_ORM_Relation
-{
- public function fetchRelatedFor(IPF_ORM_Record $record)
- {
- $id = array();
- $localTable = $record->getTable();
- foreach ((array) $this->definition['local'] as $local) {
- $value = $record->get($localTable->getFieldName($local));
- if (isset($value)) {
- $id[] = $value;
- }
- }
- if ($this->isOneToOne()) {
- if ( ! $record->exists() || empty($id) ||
- ! $this->definition['table']->getAttribute(IPF_ORM::ATTR_LOAD_REFERENCES)) {
-
- $related = $this->getTable()->create();
- } else {
- $dql = 'FROM ' . $this->getTable()->getComponentName()
- . ' WHERE ' . $this->getCondition();
-
- $coll = $this->getTable()->getConnection()->query($dql, $id);
- $related = $coll[0];
- }
-
- $related->set($related->getTable()->getFieldName($this->definition['foreign']),
- $record, false);
- } else {
-
- if ( ! $record->exists() || empty($id) ||
- ! $this->definition['table']->getAttribute(IPF_ORM::ATTR_LOAD_REFERENCES)) {
-
- $related = new IPF_ORM_Collection($this->getTable());
- } else {
- $query = $this->getRelationDql(1, '');
- $related = $this->getTable()->getConnection()->query($query, $id);
- }
- $related->setReference($record, $this);
- }
- return $related;
- }
-
- public function getRelationDql($count, $context)
- {
- $table = $this->getTable();
- $component = $table->getComponentName();
-
- $dql = 'FROM ' . $component
- . ' WHERE ' . $component . '.' . $this->definition['foreign']
- . ' IN (' . substr(str_repeat('?, ', $count), 0, -2) . ')';
-
- $ordering = $table->getOrdering();
- if ($ordering)
- $dql .= ' ORDER BY ' . implode(', ', $ordering);
-
- return $dql;
- }
-
- public function getCondition($alias = null)
- {
- if ( ! $alias) {
- $alias = $this->getTable()->getComponentName();
- }
- $conditions = array();
- foreach ((array) $this->definition['foreign'] as $foreign) {
- $conditions[] = $alias . '.' . $foreign . ' = ?';
- }
- return implode(' AND ', $conditions);
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-class IPF_ORM_Relation_LocalKey extends IPF_ORM_Relation
-{
- public function fetchRelatedFor(IPF_ORM_Record $record)
- {
- $localFieldName = $record->getTable()->getFieldName($this->definition['local']);
- $id = $record->get($localFieldName);
-
- if (is_null($id) || ! $this->definition['table']->getAttribute(IPF_ORM::ATTR_LOAD_REFERENCES)) {
- $related = $this->getTable()->create();
- } else {
- $dql = 'FROM ' . $this->getTable()->getComponentName()
- . ' WHERE ' . $this->getCondition();
-
- $related = $this->getTable()
- ->getConnection()
- ->query($dql, array($id))
- ->getFirst();
-
- if ( ! $related || empty($related)) {
- $related = $this->getTable()->create();
- }
- }
-
- $record->set($localFieldName, $related, false);
-
- return $related;
- }
-
- public function getRelationDql($count, $context)
- {
- $component = $this->getTable()->getComponentName();
- return 'FROM ' . $component .
- ' WHERE ' . $component . '.' . $this->definition['foreign'] .
- ' IN (' . substr(str_repeat('?, ', $count), 0, -2) . ')';
- }
-
- public function getCondition($alias = null)
- {
- if ( ! $alias) {
- $alias = $this->getTable()->getComponentName();
- }
- return $alias . '.' . $this->definition['foreign'] . ' = ?';
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Relation_Nest extends IPF_ORM_Relation_Association
-{
- public function getRelationDql($count, $context = 'record')
- {
- switch ($context) {
- case 'record':
- $identifierColumnNames = $this->definition['table']->getIdentifierColumnNames();
- $identifier = array_pop($identifierColumnNames);
- $sub = 'SELECT '.$this->definition['foreign']
- . ' FROM '.$this->definition['refTable']->getTableName()
- . ' WHERE '.$this->definition['local']
- . ' = ?';
-
- $sub2 = 'SELECT '.$this->definition['local']
- . ' FROM '.$this->definition['refTable']->getTableName()
- . ' WHERE '.$this->definition['foreign']
- . ' = ?';
-
- $dql = 'FROM ' . $this->definition['table']->getComponentName()
- . '.' . $this->definition['refTable']->getComponentName()
- . ' WHERE ' . $this->definition['table']->getComponentName()
- . '.' . $identifier
- . ' IN (' . $sub . ')'
- . ' || ' . $this->definition['table']->getComponentName()
- . '.' . $identifier
- . ' IN (' . $sub2 . ')';
- break;
- case 'collection':
- $sub = substr(str_repeat('?, ', $count),0,-2);
- $dql = 'FROM '.$this->definition['refTable']->getComponentName()
- . '.' . $this->definition['table']->getComponentName()
- . ' WHERE '.$this->definition['refTable']->getComponentName()
- . '.' . $this->definition['local'] . ' IN (' . $sub . ')';
- };
-
- return $dql;
- }
-
- public function fetchRelatedFor(IPF_ORM_Record $record)
- {
- $id = $record->getIncremented();
-
-
- if (empty($id) || ! $this->definition['table']->getAttribute(IPF_ORM::ATTR_LOAD_REFERENCES)) {
- return new IPF_ORM_Collection($this->getTable());
- } else {
- $q = new IPF_ORM_RawSql($this->getTable()->getConnection());
-
- $assocTable = $this->getAssociationFactory()->getTableName();
- $tableName = $record->getTable()->getTableName();
- $identifierColumnNames = $record->getTable()->getIdentifierColumnNames();
- $identifier = array_pop($identifierColumnNames);
-
- $sub = 'SELECT ' . $this->getForeign()
- . ' FROM ' . $assocTable
- . ' WHERE ' . $this->getLocal()
- . ' = ?';
-
- $condition[] = $tableName . '.' . $identifier . ' IN (' . $sub . ')';
- $joinCondition[] = $tableName . '.' . $identifier . ' = ' . $assocTable . '.' . $this->getForeign();
-
- if ($this->definition['equal']) {
- $sub2 = 'SELECT ' . $this->getLocal()
- . ' FROM ' . $assocTable
- . ' WHERE ' . $this->getForeign()
- . ' = ?';
-
- $condition[] = $tableName . '.' . $identifier . ' IN (' . $sub2 . ')';
- $joinCondition[] = $tableName . '.' . $identifier . ' = ' . $assocTable . '.' . $this->getLocal();
- }
- $q->select('{'.$tableName.'.*}')
- ->addSelect('{'.$assocTable.'.*}')
- ->from($tableName . ' INNER JOIN ' . $assocTable . ' ON ' . implode(' OR ', $joinCondition))
- ->where(implode(' OR ', $condition));
- $q->addComponent($tableName, $record->getTable()->getComponentName());
- $q->addComponent($assocTable, $record->getTable()->getComponentName(). '.' . $this->getAssociationFactory()->getComponentName());
-
- $params = ($this->definition['equal']) ? array($id, $id) : array($id);
-
- return $q->execute($params);
- }
- }
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_Relation_Parser
-{
- protected $_table;
- protected $_relations = array();
- protected $_pending = array();
-
- public function __construct(IPF_ORM_Table $table)
- {
- $this->_table = $table;
- }
-
- public function getTable()
- {
- return $this->_table;
- }
-
- public function getPendingRelation($name)
- {
- if ( ! isset($this->_pending[$name])) {
- throw new IPF_ORM_Exception('Unknown pending relation ' . $name);
- }
-
- return $this->_pending[$name];
- }
-
- public function getPendingRelations()
- {
- return $this->_pending;
- }
-
- public function unsetPendingRelations($name)
- {
- unset($this->_pending[$name]);
- }
-
- public function hasRelation($name)
- {
- return isset($this->_pending[$name]) || isset($this->_relations[$name]);
- }
-
- public function bind($name, $alias, $type, $options=array())
- {
- if (!$alias)
- $alias = $name;
-
- unset($this->relations[$alias]);
-
- $options['class'] = $name;
- $options['alias'] = $alias;
- $options['type'] = $type;
-
- $this->_pending[$alias] = $options;
- }
-
- private function createRelation($alias)
- {
- if (isset($this->_relations[$alias]))
- return;
-
- if (!isset($this->_pending[$alias]))
- throw new IPF_ORM_Exception('Unknown relation alias "' . $alias . '".');
-
- $def = $this->_pending[$alias];
- $identifierColumnNames = $this->_table->getIdentifierColumnNames();
- $idColumnName = array_pop($identifierColumnNames);
-
- // check if reference class name exists
- // if it does we are dealing with association relation
- if (isset($def['refClass'])) {
- $def = $this->completeAssocDefinition($def);
- $localClasses = array_merge($this->_table->getOption('parents'), array($this->_table->getComponentName()));
-
- if ( ! isset($this->_pending[$def['refClass']]) &&
- ! isset($this->_relations[$def['refClass']])) {
-
- $parser = $def['refTable']->getRelationParser();
- if ( ! $parser->hasRelation($this->_table->getComponentName())) {
- $parser->bind($this->_table->getComponentName(), null, IPF_ORM_Relation::ONE, array(
- 'local' => $def['local'],
- 'foreign' => $idColumnName,
- 'localKey' => true,
- ));
- }
-
- if (!$this->hasRelation($def['refClass'])) {
- $this->bind($def['refClass'], null, IPF_ORM_Relation::MANY, array(
- 'foreign' => $def['local'],
- 'local' => $idColumnName,
- ));
- }
- }
- if (in_array($def['class'], $localClasses)) {
- $rel = new IPF_ORM_Relation_Nest($def);
- } else {
- $rel = new IPF_ORM_Relation_Association($def);
- }
- } else {
- // simple foreign key relation
- $def = $this->completeDefinition($def);
-
- if (isset($def['localKey'])) {
- $rel = new IPF_ORM_Relation_LocalKey($def);
-
- // Automatically index foreign keys which are not primary
- $foreign = (array) $def['foreign'];
- foreach ($foreign as $fk) {
- if ( ! $rel->getTable()->isIdentifier($fk)) {
- $rel->getTable()->addIndex($fk, array('fields' => array($fk)));
- }
- }
- } else {
- $rel = new IPF_ORM_Relation_ForeignKey($def);
- }
- }
-
- if (isset($rel)) {
- // unset pending relation
- unset($this->_pending[$alias]);
-
- $this->_relations[$alias] = $rel;
- }
- }
-
- public function getRelation($alias)
- {
- $this->getRelations();
- return $this->_relations[$alias];
- }
-
- public function getRelations()
- {
- foreach ($this->_pending as $k => $v) {
- $this->createRelation($k);
- }
-
- return $this->_relations;
- }
-
- public function completeAssocDefinition($def)
- {
- $conn = $this->_table->getConnection();
-
- $def['table'] = IPF_ORM::getTable($def['class']);
- $def['localTable'] = $this->_table;
- $def['class'] = $def['table']->getComponentName();
- $def['refTable'] = IPF_ORM::getTable($def['refClass']);
-
- $id = $def['refTable']->getIdentifierColumnNames();
-
- if (count($id) > 1) {
- if ( ! isset($def['foreign'])) {
- // foreign key not set
- // try to guess the foreign key
-
- $def['foreign'] = ($def['local'] === $id[0]) ? $id[1] : $id[0];
- }
- if ( ! isset($def['local'])) {
- // foreign key not set
- // try to guess the foreign key
-
- $def['local'] = ($def['foreign'] === $id[0]) ? $id[1] : $id[0];
- }
- } else {
-
- if ( ! isset($def['foreign'])) {
- // foreign key not set
- // try to guess the foreign key
-
- $columns = $this->getIdentifiers($def['table']);
-
- $def['foreign'] = $columns;
- }
- if ( ! isset($def['local'])) {
- // local key not set
- // try to guess the local key
- $columns = $this->getIdentifiers($this->_table);
-
- $def['local'] = $columns;
- }
- }
- return $def;
- }
-
- public function getIdentifiers(IPF_ORM_Table $table)
- {
- $componentNameToLower = strtolower($table->getComponentName());
- if (is_array($table->getIdentifier())) {
- $columns = array();
- foreach ((array) $table->getIdentifierColumnNames() as $identColName) {
- $columns[] = $componentNameToLower . '_' . $identColName;
- }
- } else {
- $columns = $componentNameToLower . '_' . $table->getColumnName(
- $table->getIdentifier());
- }
-
- return $columns;
- }
-
- public function guessColumns(array $classes, IPF_ORM_Table $foreignTable)
- {
- $conn = $this->_table->getConnection();
-
- foreach ($classes as $class) {
- try {
- $table = $conn->getTable($class);
- } catch (IPF_ORM_Exception $e) {
- continue;
- }
- $columns = $this->getIdentifiers($table);
- $found = true;
-
- foreach ((array) $columns as $column) {
- if ( ! $foreignTable->hasColumn($column)) {
- $found = false;
- break;
- }
- }
- if ($found) {
- break;
- }
- }
-
- if ( ! $found) {
- throw new IPF_ORM_Exception("Couldn't find columns.");
- }
-
- return $columns;
- }
-
- public function completeDefinition($def)
- {
- $conn = $this->_table->getConnection();
- $def['table'] = IPF_ORM::getTable($def['class']);
- $def['localTable'] = $this->_table;
- $def['class'] = $def['table']->getComponentName();
-
- $foreignClasses = array_merge($def['table']->getOption('parents'), array($def['class']));
- $localClasses = array_merge($this->_table->getOption('parents'), array($this->_table->getComponentName()));
-
- $localIdentifierColumnNames = $this->_table->getIdentifierColumnNames();
- $localIdentifierCount = count($localIdentifierColumnNames);
- $localIdColumnName = array_pop($localIdentifierColumnNames);
- $foreignIdentifierColumnNames = $def['table']->getIdentifierColumnNames();
- $foreignIdColumnName = array_pop($foreignIdentifierColumnNames);
-
- if (isset($def['local'])) {
- if ( ! isset($def['foreign'])) {
- // local key is set, but foreign key is not
- // try to guess the foreign key
-
- if ($def['local'] === $localIdColumnName) {
- $def['foreign'] = $this->guessColumns($localClasses, $def['table']);
- } else {
- // the foreign field is likely to be the
- // identifier of the foreign class
- $def['foreign'] = $foreignIdColumnName;
- $def['localKey'] = true;
- }
- } else {
- if ($localIdentifierCount == 1) {
- if ($def['local'] == $localIdColumnName && isset($def['owningSide'])
- && $def['owningSide'] === true) {
- $def['localKey'] = true;
- } else if (($def['local'] !== $localIdColumnName && $def['type'] == IPF_ORM_Relation::ONE)) {
- $def['localKey'] = true;
- }
- } else if ($localIdentifierCount > 1) {
- // It's a composite key and since 'foreign' can not point to a composite
- // key currently, we know that 'local' must be the foreign key.
- $def['localKey'] = true;
- }
- }
- } else {
- if (isset($def['foreign'])) {
- // local key not set, but foreign key is set
- // try to guess the local key
- if ($def['foreign'] === $foreignIdColumnName) {
- $def['localKey'] = true;
- try {
- $def['local'] = $this->guessColumns($foreignClasses, $this->_table);
- } catch (IPF_ORM_Exception $e) {
- $def['local'] = $localIdColumnName;
- }
- } else {
- $def['local'] = $localIdColumnName;
- }
- } else {
- // neither local or foreign key is being set
- // try to guess both keys
-
- $conn = $this->_table->getConnection();
-
- // the following loops are needed for covering inheritance
- foreach ($localClasses as $class) {
- $table = $conn->getTable($class);
- $identifierColumnNames = $table->getIdentifierColumnNames();
- $idColumnName = array_pop($identifierColumnNames);
- $column = strtolower($table->getComponentName())
- . '_' . $idColumnName;
-
- foreach ($foreignClasses as $class2) {
- $table2 = $conn->getTable($class2);
- if ($table2->hasColumn($column)) {
- $def['foreign'] = $column;
- $def['local'] = $idColumnName;
- return $def;
- }
- }
- }
-
- foreach ($foreignClasses as $class) {
- $table = $conn->getTable($class);
- $identifierColumnNames = $table->getIdentifierColumnNames();
- $idColumnName = array_pop($identifierColumnNames);
- $column = strtolower($table->getComponentName())
- . '_' . $idColumnName;
-
- foreach ($localClasses as $class2) {
- $table2 = $conn->getTable($class2);
- if ($table2->hasColumn($column)) {
- $def['foreign'] = $idColumnName;
- $def['local'] = $column;
- $def['localKey'] = true;
- return $def;
- }
- }
- }
-
- // auto-add columns and auto-build relation
- $columns = array();
- foreach ((array) $this->_table->getIdentifierColumnNames() as $id) {
- // ?? should this not be $this->_table->getComponentName() ??
- $column = strtolower($table->getComponentName())
- . '_' . $id;
-
- $col = $this->_table->getColumnDefinition($id);
- $type = $col['type'];
- $length = $col['length'];
-
- unset($col['type']);
- unset($col['length']);
- unset($col['autoincrement']);
- unset($col['primary']);
-
- $def['table']->setColumn($column, $type, $length, $col);
-
- $columns[] = $column;
- }
- if (count($columns) > 1) {
- $def['foreign'] = $columns;
- } else {
- $def['foreign'] = $columns[0];
- }
- $def['local'] = $localIdColumnName;
- }
- }
- return $def;
- }
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_Table extends IPF_ORM_Configurable implements Countable
-{
- protected $_data = array();
- protected $_identifier = array();
- protected $_identifierType;
- protected $_conn;
- protected $_identityMap = array();
-
- protected $_repository;
- protected $_columns = array();
- protected $_fieldNames = array();
-
- protected $_columnNames = array();
-
- protected $hasDefaultValues;
-
- protected $_options = array('name' => null,
- 'tableName' => null,
- 'inheritanceMap' => array(),
- 'enumMap' => array(),
- 'type' => null,
- 'charset' => null,
- 'collation' => null,
- 'indexes' => array(),
- 'parents' => array(),
- 'queryParts' => array(),
- 'versioning' => null,
- 'subclasses' => array(),
- );
-
- protected $_ordering = null;
-
- protected $_parser;
-
- protected $_templates = array();
- protected $_invokedMethods = array();
-
- public $listeners = array();
-
- public function __construct($name, IPF_ORM_Connection $conn)
- {
- if (empty($name) || !class_exists($name))
- throw new IPF_ORM_Exception("Couldn't find class " . $name);
-
- $this->_conn = $conn;
- $this->setParent($this->_conn);
-
- $this->_options['name'] = $name;
- $this->_parser = new IPF_ORM_Relation_Parser($this);
-
- $this->initParents($name);
-
- // create database table
- $name::setTableDefinition($this);
-
- if (!isset($this->_options['tableName'])) {
- $this->setTableName(IPF_ORM_Inflector::tableize($class->getName()));
- }
-
- $this->initIdentifier();
-
- $name::setUp($this);
-
- $this->_repository = new IPF_ORM_Table_Repository($this);
- }
-
- private function initParents($name)
- {
- $names = array();
-
- $class = $name;
- do {
- if ($class === 'IPF_ORM_Record')
- break;
-
- $name = $class;
- $names[] = $name;
- } while ($class = get_parent_class($class));
-
- if ($class === false)
- throw new IPF_ORM_Exception('Class "' . $name . '" must be a child class of IPF_ORM_Record');
-
- // reverse names
- $names = array_reverse($names);
- // save parents
- array_pop($names);
- $this->_options['parents'] = $names;
- }
-
- public function initIdentifier()
- {
- switch (count($this->_identifier)) {
- case 0:
- $definition = array('type' => 'integer',
- 'length' => 20,
- 'autoincrement' => true,
- 'primary' => true);
- $this->setColumn('id', $definition['type'], $definition['length'], $definition, true);
- $this->_identifier = 'id';
- $this->_identifierType = IPF_ORM::IDENTIFIER_AUTOINC;
- break;
- case 1:
- foreach ($this->_identifier as $pk) {
- $e = $this->getDefinitionOf($pk);
-
- $found = false;
-
- foreach ($e as $option => $value) {
- if ($found) {
- break;
- }
-
- $e2 = explode(':', $option);
-
- switch (strtolower($e2[0])) {
- case 'autoincrement':
- case 'autoinc':
- if ($value !== false) {
- $this->_identifierType = IPF_ORM::IDENTIFIER_AUTOINC;
- $found = true;
- }
- break;
- }
- }
- if ( ! isset($this->_identifierType)) {
- $this->_identifierType = IPF_ORM::IDENTIFIER_NATURAL;
- }
- }
-
- $this->_identifier = $pk;
-
- break;
- default:
- $this->_identifierType = IPF_ORM::IDENTIFIER_COMPOSITE;
- }
- }
-
- public function isIdentifier($fieldName)
- {
- return ($fieldName === $this->getIdentifier() ||
- in_array($fieldName, (array) $this->getIdentifier()));
- }
-
- public function isIdentifierAutoincrement()
- {
- return $this->getIdentifierType() === IPF_ORM::IDENTIFIER_AUTOINC;
- }
-
- public function isIdentifierComposite()
- {
- return $this->getIdentifierType() === IPF_ORM::IDENTIFIER_COMPOSITE;
- }
-
- public function getMethodOwner($method)
- {
- return (isset($this->_invokedMethods[$method])) ?
- $this->_invokedMethods[$method] : false;
- }
-
- public function setMethodOwner($method, $class)
- {
- $this->_invokedMethods[$method] = $class;
- }
-
- public function getTemplates()
- {
- return $this->_templates;
- }
-
- public function getRelationParser()
- {
- return $this->_parser;
- }
-
- public function __get($option)
- {
- if (isset($this->_options[$option])) {
- return $this->_options[$option];
- }
- return null;
- }
-
- public function __isset($option)
- {
- return isset($this->_options[$option]);
- }
-
- public function getOptions()
- {
- return $this->_options;
- }
-
- public function setOptions($options)
- {
- foreach ($options as $key => $value) {
- $this->setOption($key, $value);
- }
- }
-
- public function addCheckConstraint($definition, $name)
- {
- if (is_string($name)) {
- $this->_options['checks'][$name] = $definition;
- } else {
- $this->_options['checks'][] = $definition;
- }
-
- return $this;
- }
-
- public function addIndex($index, array $definition)
- {
- $this->_options['indexes'][$index] = $definition;
- }
-
- public function getIndex($index)
- {
- if (isset($this->_options['indexes'][$index])) {
- return $this->_options['indexes'][$index];
- }
-
- return false;
- }
-
- public function bind($class, $alias, $type, array $options)
- {
- $this->_parser->bind($class, $alias, $type, $options);
- }
-
- public function ownsOne($class, $alias, $options=array())
- {
- $this->bind($class, $alias, IPF_ORM_Relation::ONE_COMPOSITE, $options);
- }
-
- public function ownsMany($class, $alias, $options=array())
- {
- $this->bind($class, $alias, IPF_ORM_Relation::MANY_COMPOSITE, $options);
- }
-
- public function hasOne($class, $alias, $options=array())
- {
- $this->bind($class, $alias, IPF_ORM_Relation::ONE_AGGREGATE, $options);
- }
-
- public function hasMany($class, $alias, $options=array())
- {
- $this->bind($class, $alias, IPF_ORM_Relation::MANY_AGGREGATE, $options);
- }
-
- public function hasRelation($alias)
- {
- return $this->_parser->hasRelation($alias);
- }
-
- public function getRelation($alias)
- {
- return $this->_parser->getRelation($alias);
- }
-
- public function getRelations()
- {
- return $this->_parser->getRelations();
- }
-
- public function createQuery($alias = '')
- {
- if ( ! empty($alias)) {
- $alias = ' ' . trim($alias);
- }
- return IPF_ORM_Query::create($this->_conn)->from($this->getComponentName() . $alias);
- }
-
- public function getRepository()
- {
- return $this->_repository;
- }
-
- public function setOption($name, $value)
- {
- switch ($name) {
- case 'name':
- case 'tableName':
- break;
- case 'enumMap':
- case 'inheritanceMap':
- case 'index':
- if (!is_array($value))
- throw new IPF_ORM_Exception($name . ' should be an array.');
- break;
- }
- $this->_options[$name] = $value;
- }
-
- public function getOption($name, $default=null)
- {
- if (isset($this->_options[$name])) {
- return $this->_options[$name];
- }
- return $default;
- }
-
- public function getColumnName($fieldName)
- {
- // FIX ME: This is being used in places where an array is passed, but it should not be an array
- // For example in places where IPF_ORM should support composite foreign/primary keys
- $fieldName = is_array($fieldName) ? $fieldName[0]:$fieldName;
-
- if (isset($this->_columnNames[$fieldName])) {
- return $this->_columnNames[$fieldName];
- }
-
- return strtolower($fieldName);
- }
-
- public function getColumnDefinition($columnName, $default=false)
- {
- if (isset($this->_columns[$columnName])) {
- return $this->_columns[$columnName];
- }
- return false;
- }
-
- public function getFieldName($columnName)
- {
- if (isset($this->_fieldNames[$columnName])) {
- return $this->_fieldNames[$columnName];
- }
- return $columnName;
- }
- public function setColumns(array $definitions)
- {
- foreach ($definitions as $name => $options) {
- $this->setColumn($name, $options['type'], $options['length'], $options);
- }
- }
-
- 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]);
- } 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 'string':
- case 'clob':
- case 'float':
- case 'double':
- case 'integer':
- case 'array':
- case 'object':
- case 'blob':
- case 'gzip':
- // use php int max
- $length = 2147483647;
- 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;
- }
- }
-
- public function hasDefaultValues()
- {
- return $this->hasDefaultValues;
- }
-
- public function getDefaultValueOf($fieldName)
- {
- $columnName = $this->getColumnName($fieldName);
- if ( ! isset($this->_columns[$columnName])) {
- throw new IPF_ORM_Exception("Couldn't get default value. Column ".$columnName." doesn't exist.");
- }
- if (isset($this->_columns[$columnName]['default'])) {
- return $this->_columns[$columnName]['default'];
- } else {
- return null;
- }
- }
-
- public function getIdentifier()
- {
- return $this->_identifier;
- }
-
- public function getIdentifierType()
- {
- return $this->_identifierType;
- }
-
- public function hasColumn($columnName)
- {
- return isset($this->_columns[strtolower($columnName)]);
- }
-
- public function hasField($fieldName)
- {
- return isset($this->_columnNames[$fieldName]);
- }
-
- public function setSubClasses(array $map)
- {
- $class = $this->getComponentName();
- if (isset($map[$class])) {
- $this->setOption('inheritanceMap', $map[$class]);
- } else {
- $this->setOption('subclasses', array_keys($map));
- }
- }
-
- public function setConnection(IPF_ORM_Connection $conn)
- {
- $this->_conn = $conn;
-
- $this->setParent($this->_conn);
-
- return $this;
- }
-
- public function getConnection()
- {
- return $this->_conn;
- }
-
- public function create(array $array = array())
- {
- $record = new $this->_options['name']($this, true);
- $record->fromArray($array);
-
- return $record;
- }
-
- public function find($id, $hydrationMode = null)
- {
- if (is_null($id)) {
- return false;
- }
-
- $id = is_array($id) ? array_values($id) : array($id);
-
- $q = $this->createQuery();
- $q->where(implode(' = ? AND ', (array) $this->getIdentifier()) . ' = ?', $id)
- ->limit(1);
- $res = $q->fetchOne(array(), $hydrationMode);
- $q->free();
-
- return $res;
- }
-
- public function findAll($hydrationMode = null)
- {
- $q = $this->createQuery();
- if ($this->_ordering) {
- $q->orderBy(implode(', ', $this->_ordering));
- } elseif ($this->hasTemplate('IPF_ORM_Template_Orderable')) {
- $q->orderBy($this->getTemplate('IPF_ORM_Template_Orderable')->getColumnName());
- }
- return $q->execute(array(), $hydrationMode);
- }
-
- public function findBySql($dql, $params = array(), $hydrationMode = null)
- {
- return $this->createQuery()->where($dql)->execute($params, $hydrationMode);
- }
-
- public function findByDql($dql, $params = array(), $hydrationMode = null)
- {
- $parser = new IPF_ORM_Query($this->_conn);
- $component = $this->getComponentName();
- $query = 'FROM ' . $component . ' WHERE ' . $dql;
-
- return $parser->query($query, $params, $hydrationMode);
- }
-
- public function findOneByDql($dql, $params = array(), $hydrationMode = null)
- {
- $results = $this->findByDql($dql, $params, $hydrationMode);
- if (is_array($results) && isset($results[0])) {
- return $results[0];
- } else if ($results instanceof IPF_ORM_Collection && $results->count() > 0) {
- return $results->getFirst();
- } else {
- return false;
- }
- }
-
-
- public function execute($queryKey, $params = array(), $hydrationMode = IPF_ORM::HYDRATE_RECORD)
- {
- return IPF_ORM_Manager::getInstance()
- ->getQueryRegistry()
- ->get($queryKey, $this->getComponentName())
- ->execute($params, $hydrationMode);
- }
-
- public function executeOne($queryKey, $params = array(), $hydrationMode = IPF_ORM::HYDRATE_RECORD)
- {
- return IPF_ORM_Manager::getInstance()
- ->getQueryRegistry()
- ->get($queryKey, $this->getComponentName())
- ->fetchOne($params, $hydrationMode);
- }
-
- public function clear()
- {
- $this->_identityMap = array();
- }
-
- public function addRecord(IPF_ORM_Record $record)
- {
- $id = implode(' ', $record->identifier());
-
- if (isset($this->_identityMap[$id])) {
- return false;
- }
-
- $this->_identityMap[$id] = $record;
-
- return true;
- }
-
- public function removeRecord(IPF_ORM_Record $record)
- {
- $id = implode(' ', $record->identifier());
-
- if (isset($this->_identityMap[$id])) {
- unset($this->_identityMap[$id]);
- return true;
- }
-
- return false;
- }
-
- public function getRecord()
- {
- if ( ! empty($this->_data)) {
- $identifierFieldNames = $this->getIdentifier();
-
- if ( ! is_array($identifierFieldNames)) {
- $identifierFieldNames = array($identifierFieldNames);
- }
-
- $found = false;
- foreach ($identifierFieldNames as $fieldName) {
- if ( ! isset($this->_data[$fieldName])) {
- // primary key column not found return new record
- $found = true;
- break;
- }
- $id[] = $this->_data[$fieldName];
- }
-
- if ($found) {
- $recordName = $this->getComponentName();
- $record = new $recordName($this, true);
- $this->_data = array();
- return $record;
- }
-
-
- $id = implode(' ', $id);
-
- if (isset($this->_identityMap[$id])) {
- $record = $this->_identityMap[$id];
- $record->hydrate($this->_data);
- } else {
- $recordName = $this->getComponentName();
- $record = new $recordName($this);
- $this->_identityMap[$id] = $record;
- }
- $this->_data = array();
- } else {
- $recordName = $this->getComponentName();
- $record = new $recordName($this, true);
- }
-
- return $record;
- }
-
- final public function applyInheritance($where)
- {
- if ( ! empty($this->_options['inheritanceMap'])) {
- $a = array();
- foreach ($this->_options['inheritanceMap'] as $field => $value) {
- $a[] = $this->getColumnName($field) . ' = ?';
- }
- $i = implode(' AND ', $a);
- $where .= ' AND ' . $i;
- }
- return $where;
- }
-
- public function count()
- {
- $a = $this->_conn->execute('SELECT COUNT(1) FROM ' . $this->_options['tableName'])->fetch(IPF_ORM::FETCH_NUM);
- return current($a);
- }
-
- public function getQueryObject()
- {
- $graph = new IPF_ORM_Query($this->getConnection());
- $graph->load($this->getComponentName());
- return $graph;
- }
-
- public function getEnumValues($fieldName)
- {
- $columnName = $this->getColumnName($fieldName);
- if (isset($this->_columns[$columnName]['values'])) {
- return $this->_columns[$columnName]['values'];
- } else {
- return array();
- }
- }
-
- public function enumValue($fieldName, $index)
- {
- if (IPF_ORM_Null::isNull($index)) {
- return $index;
- }
-
- $columnName = $this->getColumnName($fieldName);
- if ( ! $this->_conn->getAttribute(IPF_ORM::ATTR_USE_NATIVE_ENUM)
- && isset($this->_columns[$columnName]['values'][$index])
- ) {
- return $this->_columns[$columnName]['values'][$index];
- }
-
- return $index;
- }
-
- public function enumIndex($fieldName, $value)
- {
- $values = $this->getEnumValues($fieldName);
-
- $index = array_search($value, $values);
- if ($index === false || !$this->_conn->getAttribute(IPF_ORM::ATTR_USE_NATIVE_ENUM)) {
- return $index;
- }
- return $value;
- }
-
- public function getColumnCount()
- {
- return count($this->_columns);
- }
-
- public function getColumns()
- {
- return $this->_columns;
- }
-
- public function removeColumn($fieldName)
- {
- if ($this->hasField($fieldName)) {
- $columnName = $this->getColumnName($fieldName);
- unset($this->_columnNames[$fieldName], $this->_fieldNames[$columnName], $this->_columns[$columnName]);
- return true;
- } else {
- return false;
- }
- }
-
- public function getColumnNames(array $fieldNames = null)
- {
- if ($fieldNames === null) {
- return array_keys($this->_columns);
- } else {
- $columnNames = array();
- foreach ($fieldNames as $fieldName) {
- $columnNames[] = $this->getColumnName($fieldName);
- }
- return $columnNames;
- }
- }
-
- public function getIdentifierColumnNames()
- {
- return $this->getColumnNames((array) $this->getIdentifier());
- }
-
- public function getFieldNames()
- {
- return array_values($this->_fieldNames);
- }
-
- public function getDefinitionOf($fieldName)
- {
- $columnName = $this->getColumnName($fieldName);
- return $this->getColumnDefinition($columnName);
- }
-
- public function getTypeOf($fieldName)
- {
- return $this->getTypeOfColumn($this->getColumnName($fieldName));
- }
-
- public function getTypeOfColumn($columnName)
- {
- return isset($this->_columns[$columnName]) ? $this->_columns[$columnName]['type'] : false;
- }
-
- public function setData(array $data)
- {
- $this->_data = $data;
- }
-
- public function getData()
- {
- return $this->_data;
- }
-
- public function prepareValue($fieldName, $value, $typeHint = null)
- {
- if (IPF_ORM_Null::isNull($value)) {
- return $value;
- } else if ($value === null) {
- return null;
- } else {
- $type = is_null($typeHint) ? $this->getTypeOf($fieldName) : $typeHint;
-
- switch ($type) {
- case 'integer':
- case 'string';
- // don't do any casting here PHP INT_MAX is smaller than what the databases support
- break;
- case 'enum':
- return $this->enumValue($fieldName, $value);
- break;
- case 'boolean':
- return (boolean) $value;
- break;
- case 'array':
- case 'object':
- if (is_string($value)) {
- $value = empty($value) ? null:unserialize($value);
-
- if ($value === false) {
- throw new IPF_ORM_Exception('Unserialization of ' . $fieldName . ' failed.');
- }
- return $value;
- }
- break;
- case 'gzip':
- $value = gzuncompress($value);
-
- if ($value === false) {
- throw new IPF_ORM_Exception('Uncompressing of ' . $fieldName . ' failed.');
- }
- return $value;
- break;
- }
- }
- return $value;
- }
-
- public function getComponentName()
- {
- return $this->_options['name'];
- }
-
- public function getTableName()
- {
- return $this->_options['tableName'];
- }
-
- public function setTableName($tableName)
- {
- $this->setOption('tableName', $tableName);
- }
-
- public function getTemplate($template)
- {
- if (!isset($this->_templates[$template]))
- throw new IPF_ORM_Exception('Template ' . $template . ' not loaded');
- return $this->_templates[$template];
- }
-
- public function hasTemplate($template)
- {
- return isset($this->_templates[$template]);
- }
-
- public function addTemplate($tpl, array $options=array())
- {
- if (!is_object($tpl)) {
- $className = 'IPF_ORM_Template_' . $tpl;
-
- if (class_exists($className, true)) {
- $tpl = new $className($options);
- } else if (class_exists($tpl, true)) {
- $tpl = new $tpl($options);
- } else {
- throw new IPF_ORM_Record_Exception('Could not load behavior named: "' . $tpl . '"');
- }
- }
-
- if (!($tpl instanceof IPF_ORM_Template)) {
- throw new IPF_ORM_Record_Exception('Loaded behavior class is not an istance of IPF_ORM_Template.');
- }
-
- $className = get_class($tpl);
- $this->_templates[$className] = $tpl;
-
- $tpl->setTableDefinition($this);
- }
-
- public function setOrdering($ordering)
- {
- $this->_ordering = $ordering;
- }
-
- public function getOrdering()
- {
- return $this->_ordering;
- }
-
- public function bindQueryParts(array $queryParts)
- {
- $this->_options['queryParts'] = $queryParts;
- return $this;
- }
-
- public function bindQueryPart($queryPart, $value)
- {
- $this->_options['queryParts'][$queryPart] = $value;
- return $this;
- }
-
- public function getFieldValidators($fieldName)
- {
- $validators = array();
- $columnName = $this->getColumnName($fieldName);
- // this loop is a dirty workaround to get the validators filtered out of
- // the options, since everything is squeezed together currently
-
- if (!isset($this->_columns[$columnName]))
- return array();
-
- foreach ($this->_columns[$columnName] as $name => $args) {
- if (empty($name)
- || $name == 'primary'
- || $name == 'protected'
- || $name == 'autoincrement'
- || $name == 'default'
- || $name == 'values'
- || $name == 'zerofill'
- || $name == 'owner'
- || $name == 'scale'
- || $name == 'type'
- || $name == 'length'
- || $name == 'fixed') {
- continue;
- }
- if ($name == 'notnull' && isset($this->_columns[$columnName]['autoincrement'])) {
- continue;
- }
- // skip it if it's explicitly set to FALSE (i.e. notnull => false)
- if ($args === false) {
- continue;
- }
- $validators[$name] = $args;
- }
-
- return $validators;
- }
-
- public function getBoundQueryPart($queryPart)
- {
- if (isset($this->_options['queryParts'][$queryPart]))
- return $this->_options['queryParts'][$queryPart];
- else
- return null;
- }
-
- public function __toString()
- {
- return IPF_ORM_Utils::getTableAsString($this);
- }
-
- protected function findBy($fieldName, $value, $hydrationMode = null)
- {
- $q = $this->createQuery()->where($fieldName . ' = ?', array($value));
- if ($this->_ordering)
- $q->orderBy(implode(', ', $this->_ordering));
- return $q->execute(array(), $hydrationMode);
- }
-
- protected function findOneBy($fieldName, $value, $hydrationMode = null)
- {
- $results = $this->createQuery()
- ->where($fieldName . ' = ?',array($value))
- ->limit(1)
- ->execute(array(), $hydrationMode);
-
- if (is_array($results) && isset($results[0])) {
- return $results[0];
- } else if ($results instanceof IPF_ORM_Collection && $results->count() > 0) {
- return $results->getFirst();
- } else {
- return false;
- }
- }
-
- protected function _resolveFindByFieldName($name)
- {
- $fieldName = IPF_ORM_Inflector::tableize($name);
- if ($this->hasColumn($name) || $this->hasField($name)) {
- return $this->getFieldName($this->getColumnName($name));
- } else if ($this->hasColumn($fieldName) || $this->hasField($fieldName)) {
- return $this->getFieldName($this->getColumnName($fieldName));
- } else {
- return false;
- }
- }
-
- public function __call($method, $arguments)
- {
- if (substr($method, 0, 6) == 'findBy') {
- $by = substr($method, 6, strlen($method));
- $method = 'findBy';
- } else if (substr($method, 0, 9) == 'findOneBy') {
- $by = substr($method, 9, strlen($method));
- $method = 'findOneBy';
- }
-
- if (isset($by)) {
- if ( ! isset($arguments[0])) {
- throw new IPF_ORM_Exception('You must specify the value to findBy');
- }
-
- $fieldName = $this->_resolveFindByFieldName($by);
- $hydrationMode = isset($arguments[1]) ? $arguments[1]:null;
- if ($this->hasField($fieldName)) {
- return $this->$method($fieldName, $arguments[0], $hydrationMode);
- } else if ($this->hasRelation($by)) {
- $relation = $this->getRelation($by);
-
- if ($relation['type'] === IPF_ORM_Relation::MANY) {
- throw new IPF_ORM_Exception('Cannot findBy many relationship.');
- }
-
- return $this->$method($relation['local'], $arguments[0], $hydrationMode);
- } else {
- throw new IPF_ORM_Exception('Cannot find by: ' . $by . '. Invalid column or relationship alias.');
- }
- }
-
- throw new IPF_ORM_Exception(sprintf('Unknown method %s::%s', get_class($this), $method));
- }
-
- public function notifyRecordListeners($method, $event)
- {
- foreach ($this->listeners as $listener)
- if (is_callable(array($listener, $method)))
- $listener->$method($event);
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Table_Repository implements Countable, IteratorAggregate
-{
- private $table;
- private $registry = array();
-
- public function __construct(IPF_ORM_Table $table)
- {
- $this->table = $table;
- }
-
- public function getTable()
- {
- return $this->table;
- }
-
- public function add(IPF_ORM_Record $record)
- {
- $oid = $record->getOID();
-
- if (isset($this->registry[$oid])) {
- return false;
- }
- $this->registry[$oid] = $record;
-
- return true;
- }
-
- public function get($oid)
- {
- if ( ! isset($this->registry[$oid])) {
- throw new IPF_ORM_Exception("Unknown object identifier");
- }
- return $this->registry[$oid];
- }
-
- public function count()
- {
- return count($this->registry);
- }
-
- public function evict($oid)
- {
- if ( ! isset($this->registry[$oid])) {
- return false;
- }
- unset($this->registry[$oid]);
- return true;
- }
-
- public function evictAll()
- {
- $evicted = 0;
- foreach ($this->registry as $oid=>$record) {
- if ($this->evict($oid)) {
- $evicted++;
- }
- }
- return $evicted;
- }
-
- public function getIterator()
- {
- return new ArrayIterator($this->registry);
- }
-
- public function contains($oid)
- {
- return isset($this->registry[$oid]);
- }
-
- public function loadAll()
- {
- $this->table->findAll();
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-abstract class IPF_ORM_Template
-{
- protected $_invoker;
-
- public function setInvoker(IPF_ORM_Record $invoker)
- {
- $this->_invoker = $invoker;
- }
-
- public function getInvoker()
- {
- return $this->_invoker;
- }
-
- abstract public function setTableDefinition(IPF_ORM_Table $table);
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Template_Listener_Orderable
-{
- private $columnName, $prepend;
-
- public function __construct($columnName, $prepend)
- {
- $this->columnName = $columnName;
- $this->prepend = $prepend;
- }
-
- public function preInsert(IPF_ORM_Event $event)
- {
- $this->setOrderValue($event->getInvoker());
- }
-
- public function preUpdate(IPF_ORM_Event $event)
- {
- $this->setOrderValue($event->getInvoker());
- }
-
- private function setOrderValue($obj)
- {
- $columnName = $this->columnName;
- if ($obj->$columnName !== null)
- return;
-
- if ($this->prepend) {
- $f = 'min';
- $d = '-';
- } else {
- $f = 'max';
- $d = '+';
- }
-
- $res = IPF_ORM_Query::create()
- ->select('coalesce('.$f.'('.$this->columnName.') '.$d.' 1, 1) as x_ord')
- ->from(get_class($obj))
- ->execute();
-
- $obj->$columnName = (int)$res[0]->x_ord;
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Template_Listener_Owned
-{
- private $columnName;
-
- public function __construct($columnName)
- {
- $this->columnName = $columnName;
- }
-
- public function preInsert(IPF_ORM_Event $event)
- {
- $this->setOwner($event->getInvoker());
- }
-
- public function preUpdate(IPF_ORM_Event $event)
- {
- $this->setOwner($event->getInvoker());
- }
-
- private function setOwner($obj)
- {
- $columnName = $this->columnName;
- if ($obj->$columnName)
- return;
-
- $request = IPF_Project::getInstance()->request;
- if ($request && !$request->user->isAnonymous()) {
- $obj->$columnName = $request->user->id;
- }
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Template_Listener_Sluggable
-{
- protected $_options = array();
-
- public function __construct(array $options)
- {
- $this->_options = $options;
- }
-
- public function preInsert(IPF_ORM_Event $event)
- {
- $name = $this->_options['name'];
-
- $record = $event->getInvoker();
-
- if ( ! $record->$name) {
- $record->$name = $this->buildSlug($record);
- }
- }
-
- public function preUpdate(IPF_ORM_Event $event)
- {
- if (false !== $this->_options['unique']) {
- $name = $this->_options['name'];
-
- $record = $event->getInvoker();
-
- if ( ! $record->$name ||
- (false !== $this->_options['canUpdate'] &&
- array_key_exists($name, $record->getModified()))) {
- $record->$name = $this->buildSlug($record);
- }
- }
- }
-
- protected function buildSlug($record)
- {
- if (empty($this->_options['fields'])) {
- if (method_exists($record, 'getUniqueSlug')) {
- $value = $record->getUniqueSlug($record);
- } else {
- $value = (string) $record;
- }
- } else {
- if ($this->_options['unique'] === true) {
- $value = $this->getUniqueSlug($record);
- } else {
- $value = '';
- foreach ($this->_options['fields'] as $field) {
- $value .= $record->$field . ' ';
- }
- }
- }
-
- $value = call_user_func_array($this->_options['builder'], array($value, $record));
-
- return $value;
- }
-
- public function getUniqueSlug($record)
- {
- $name = $this->_options['name'];
- $slugFromFields = '';
- foreach ($this->_options['fields'] as $field) {
- $slugFromFields .= $record->$field . ' ';
- }
-
- $proposal = $record->$name ? $record->$name : $slugFromFields;
- $proposal = call_user_func_array($this->_options['builder'], array($proposal, $record));
- $slug = $proposal;
-
- $whereString = 'r.' . $name . ' LIKE ?';
- $whereParams = array($proposal.'%');
-
- if ($record->exists()) {
- $identifier = $record->identifier();
- $whereString .= ' AND r.' . implode(' != ? AND r.', $record->getTable()->getIdentifierColumnNames()) . ' != ?';
- $whereParams = array_merge($whereParams, array_values($identifier));
- }
-
- foreach ($this->_options['uniqueBy'] as $uniqueBy) {
- if (is_null($record->$uniqueBy)) {
- $whereString .= ' AND r.'.$uniqueBy.' IS NULL';
- } else {
- $whereString .= ' AND r.'.$uniqueBy.' = ?';
- $whereParams[] = $record->$uniqueBy;
- }
- }
-
- $query = IPF_ORM_Query::create()
- ->select('r.'.$name)
- ->from(get_class($record).' r')
- ->where($whereString , $whereParams)
- ->setHydrationMode(IPF_ORM::HYDRATE_ARRAY);
-
- $similarSlugResult = $query->execute();
-
- $similarSlugs = array();
- foreach ($similarSlugResult as $key => $value) {
- $similarSlugs[$key] = $value[$name];
- }
-
- $i = 1;
- while (in_array($slug, $similarSlugs)) {
- $slug = $proposal.'-'.$i;
- $i++;
- }
-
- return $slug;
- }
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_Template_Listener_Timestampable
-{
- protected $_options = array();
- public function __construct(array $options)
- {
- $this->_options = $options;
- }
-
- public function preInsert(IPF_ORM_Event $event)
- {
- if( ! $this->_options['created']['disabled']) {
- $createdName = $this->_options['created']['name'];
- $event->getInvoker()->$createdName = $this->getTimestamp('created');
- }
-
- if( ! $this->_options['updated']['disabled'] && $this->_options['updated']['onInsert']) {
- $updatedName = $this->_options['updated']['name'];
- $event->getInvoker()->$updatedName = $this->getTimestamp('updated');
- }
- }
-
- public function preUpdate(IPF_ORM_Event $event)
- {
- if( ! $this->_options['updated']['disabled']) {
- $updatedName = $this->_options['updated']['name'];
- $event->getInvoker()->$updatedName = $this->getTimestamp('updated');
- }
- }
-
- public function getTimestamp($type)
- {
- $options = $this->_options[$type];
-
- if ($options['expression'] !== false && is_string($options['expression'])) {
- return new IPF_ORM_Expression($options['expression']);
- } else {
- if ($options['type'] == 'date') {
- return gmdate($options['format']);
- } else if ($options['type'] == 'timestamp') {
- return gmdate($options['format']);
- } else {
- return gmmktime();
- }
- }
- }
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_Template_Orderable extends IPF_ORM_Template
-{
- private $columnName = 'ord';
- private $exclude = true;
- private $prepend = false;
-
- public function __construct(array $options=array())
- {
- if ($options) {
- if (array_key_exists('name', $options))
- $this->columnName = $options['name'];
- if (array_key_exists('exclude', $options))
- $this->exclude = $options['exclude'];
- if (array_key_exists('prepend', $options))
- $this->prepend = $options['prepend'];
- }
- }
-
- public function getColumnName()
- {
- return $this->columnName;
- }
-
- public function setTableDefinition(IPF_ORM_Table $table)
- {
- $table->setColumn($this->columnName, 'integer', null, array('exclude' => $this->exclude));
- $table->addIndex($table->getOption('tableName') . '_orderable_' . $this->columnName, array('fields' => array($this->columnName)));
- $table->listeners['Orderable_'.$this->columnName] = new IPF_ORM_Template_Listener_Orderable($this->columnName, $this->prepend);
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Template_Owned extends IPF_ORM_Template
-{
- private $name = 'owner';
- private $columnName = 'owner_id';
- private $exclude = true;
- private $verbose = 'owner';
-
- public function __construct(array $options=array())
- {
- if ($options) {
- if (array_key_exists('column', $options))
- $this->columnName = $options['column'];
- if (array_key_exists('name', $options))
- $this->name = $options['name'];
- if (array_key_exists('exclude', $options))
- $this->exclude = $options['exclude'];
- if (array_key_exists('verbose', $options))
- $this->verbose = $options['verbose'];
- }
- }
-
- public function getColumnName()
- {
- return $this->columnName;
- }
-
- public function setTableDefinition(IPF_ORM_Table $table)
- {
- $table->setColumn($this->columnName, 'integer', null, array(
- 'exclude' => $this->exclude,
- 'verbose' => $this->verbose,
- ));
-
- $fks = $table->getOption('foreignKeys', array());
- $fks[] = array(
- 'local' => $this->columnName,
- 'foreign' => 'id',
- 'foreignTable' => 'auth_users',
- 'onUpdate' => null,
- 'onDelete' => 'CASCADE',
- );
- $table->setOption('foreignKeys', $fks);
-
- $table->listeners['Owned_'.$this->columnName] = new IPF_ORM_Template_Listener_Owned($this->columnName);
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Template_Sluggable extends IPF_ORM_Template
-{
- protected $_options = array('name' => 'slug',
- 'type' => 'string',
- 'length' => 255,
- 'unique' => true,
- 'options' => array(),
- 'fields' => array(),
- 'uniqueBy' => array(),
- 'uniqueIndex' => true,
- 'canUpdate' => false,
- 'builder' => array('IPF_ORM_Inflector', 'urlize'),
- 'indexName' => 'sluggable'
- );
-
- public function __construct(array $options = array())
- {
- $this->_options = IPF_ORM_Utils::arrayDeepMerge($this->_options, $options);
- }
-
- public function setTableDefinition(IPF_ORM_Table $table)
- {
- $table->setColumn($this->_options['name'], $this->_options['type'], $this->_options['length'], $this->_options['options']);
-
- if ($this->_options['unique'] == true && $this->_options['uniqueIndex'] == true && !empty($this->_options['fields'])) {
- $indexFields = array($this->_options['name']);
- $indexFields = array_merge($indexFields, $this->_options['uniqueBy']);
- $table->addIndex($this->_options['indexName'], array('fields' => $indexFields, 'type' => 'unique'));
- }
- $table->listeners['Sluggable_'.print_r($this->_options, true)] = new IPF_ORM_Template_Listener_Sluggable($this->_options);
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Template_Timestampable extends IPF_ORM_Template
-{
- protected $_options = array(
- 'created' => array(
- 'name' => 'created_at',
- 'type' => 'timestamp',
- 'format' => 'Y-m-d H:i:s',
- 'disabled' => false,
- 'expression' => false,
- 'options' => array('exclude' => true),
- ),
- 'updated' => array(
- 'name' => 'updated_at',
- 'type' => 'timestamp',
- 'format' => 'Y-m-d H:i:s',
- 'disabled' => false,
- 'expression' => false,
- 'onInsert' => true,
- 'exclude' => true,
- 'options' => array('exclude' => true),
- ),
- );
-
- public function __construct(array $options = array())
- {
- $this->_options = IPF_ORM_Utils::arrayDeepMerge($this->_options, $options);
- }
-
- public function setTableDefinition(IPF_ORM_Table $table)
- {
- if (!$this->_options['created']['disabled']) {
- $table->setColumn($this->_options['created']['name'], $this->_options['created']['type'], null, $this->_options['created']['options']);
- }
- if (!$this->_options['updated']['disabled']) {
- $table->setColumn($this->_options['updated']['name'], $this->_options['updated']['type'], null, $this->_options['updated']['options']);
- }
- $table->listeners['Timestampable_'.print_r($this->_options, true)] = new IPF_ORM_Template_Listener_Timestampable($this->_options);
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Transaction extends IPF_ORM_Connection_Module
-{
- const STATE_SLEEP = 0;
- const STATE_ACTIVE = 1;
- const STATE_BUSY = 2;
-
- protected $_nestingLevel = 0;
- protected $_internalNestingLevel = 0;
- protected $invalid = array();
- protected $savePoints = array();
- protected $_collections = array();
-
- public function addCollection(IPF_ORM_Collection $coll)
- {
- $this->_collections[] = $coll;
-
- return $this;
- }
-
- public function getState()
- {
- switch ($this->_nestingLevel) {
- case 0:
- return IPF_ORM_Transaction::STATE_SLEEP;
- break;
- case 1:
- return IPF_ORM_Transaction::STATE_ACTIVE;
- break;
- default:
- return IPF_ORM_Transaction::STATE_BUSY;
- }
- }
-
- public function addInvalid(IPF_ORM_Record $record)
- {
- if (in_array($record, $this->invalid, true)) {
- return false;
- }
- $this->invalid[] = $record;
- return true;
- }
-
- public function getInvalid()
- {
- return $this->invalid;
- }
-
- public function getTransactionLevel()
- {
- return $this->_nestingLevel;
- }
-
- public function getInternalTransactionLevel()
- {
- return $this->_internalNestingLevel;
- }
-
- public function beginTransaction($savepoint = null)
- {
- if ( ! is_null($savepoint)) {
- $this->savePoints[] = $savepoint;
-
- $event = new IPF_ORM_Event($this, IPF_ORM_Event::SAVEPOINT_CREATE);
-
- $this->conn->notifyDBListeners('preSavepointCreate', $event);
-
- if ( ! $event->skipOperation) {
- $this->createSavePoint($savepoint);
- }
-
- $this->conn->notifyDBListeners('postSavepointCreate', $event);
- } else {
- if ($this->_nestingLevel == 0) {
- $event = new IPF_ORM_Event($this, IPF_ORM_Event::TX_BEGIN);
-
- $this->conn->notifyDBListeners('preTransactionBegin', $event);
-
- if ( ! $event->skipOperation) {
- try {
- $this->_doBeginTransaction();
- } catch (Exception $e) {
- throw new IPF_ORM_Exception($e->getMessage());
- }
- }
- $this->conn->notifyDBListeners('postTransactionBegin', $event);
- }
- }
-
- $level = ++$this->_nestingLevel;
-
- return $level;
- }
-
- public function commit($savepoint = null)
- {
- if ($this->_nestingLevel == 0) {
- throw new IPF_ORM_Exception("Commit failed. There is no active transaction.");
- }
-
- if ( ! is_null($savepoint)) {
- $this->_nestingLevel -= $this->removeSavePoints($savepoint);
-
- $event = new IPF_ORM_Event($this, IPF_ORM_Event::SAVEPOINT_COMMIT);
-
- $this->conn->notifyDBListeners('preSavepointCommit', $event);
-
- if ( ! $event->skipOperation) {
- $this->releaseSavePoint($savepoint);
- }
-
- $this->conn->notifyDBListeners('postSavepointCommit', $event);
- } else {
-
- if ($this->_nestingLevel == 1 || $this->_internalNestingLevel == 1) {
- if ( ! empty($this->invalid)) {
- if ($this->_internalNestingLevel == 1) {
- // transaction was started by IPF_ORM, so we are responsible
- // for a rollback
- $this->rollback();
- $tmp = $this->invalid;
- $this->invalid = array();
- throw new IPF_ORM_Exception_Validator($tmp);
- }
- }
- if ($this->_nestingLevel == 1) {
- // take snapshots of all collections used within this transaction
- foreach ($this->_collections as $coll) {
- $coll->takeSnapshot();
- }
- $this->_collections = array();
-
- $event = new IPF_ORM_Event($this, IPF_ORM_Event::TX_COMMIT);
-
- $this->conn->notifyDBListeners('preTransactionCommit', $event);
- if ( ! $event->skipOperation) {
- $this->_doCommit();
- }
- $this->conn->notifyDBListeners('postTransactionCommit', $event);
- }
- }
-
- if ($this->_nestingLevel > 0) {
- $this->_nestingLevel--;
- }
- if ($this->_internalNestingLevel > 0) {
- $this->_internalNestingLevel--;
- }
- }
-
- return true;
- }
-
- public function rollback($savepoint = null)
- {
- if ($this->_nestingLevel == 0) {
- throw new IPF_ORM_Exception("Rollback failed. There is no active transaction.");
- }
-
- if ($this->_internalNestingLevel > 1 || $this->_nestingLevel > 1) {
- $this->_internalNestingLevel--;
- $this->_nestingLevel--;
- return false;
- }
-
- if ( ! is_null($savepoint)) {
- $this->_nestingLevel -= $this->removeSavePoints($savepoint);
-
- $event = new IPF_ORM_Event($this, IPF_ORM_Event::SAVEPOINT_ROLLBACK);
-
- $this->conn->notifyDBListeners('preSavepointRollback', $event);
-
- if ( ! $event->skipOperation) {
- $this->rollbackSavePoint($savepoint);
- }
-
- $this->conn->notifyDBListeners('postSavepointRollback', $event);
- } else {
- $event = new IPF_ORM_Event($this, IPF_ORM_Event::TX_ROLLBACK);
-
- $this->conn->notifyDBListeners('preTransactionRollback', $event);
-
- if ( ! $event->skipOperation) {
- $this->_nestingLevel = 0;
- $this->_internalNestingLevel = 0;
- try {
- $this->_doRollback();
- } catch (Exception $e) {
- throw new IPF_ORM_Exception($e->getMessage());
- }
- }
-
- $this->conn->notifyDBListeners('postTransactionRollback', $event);
- }
-
- return true;
- }
-
- protected function createSavePoint($savepoint)
- {
- throw new IPF_ORM_Exception('Savepoints not supported by this driver.');
- }
-
- protected function releaseSavePoint($savepoint)
- {
- throw new IPF_ORM_Exception('Savepoints not supported by this driver.');
- }
-
- protected function rollbackSavePoint($savepoint)
- {
- throw new IPF_ORM_Exception('Savepoints not supported by this driver.');
- }
-
- protected function _doRollback()
- {
- $this->conn->getDbh()->rollback();
- }
-
- protected function _doCommit()
- {
- $this->conn->getDbh()->commit();
- }
-
- protected function _doBeginTransaction()
- {
- $this->conn->getDbh()->beginTransaction();
- }
-
- private function removeSavePoints($savepoint)
- {
- $this->savePoints = array_values($this->savePoints);
-
- $found = false;
- $i = 0;
-
- foreach ($this->savePoints as $key => $sp) {
- if ( ! $found) {
- if ($sp === $savepoint) {
- $found = true;
- }
- }
- if ($found) {
- $i++;
- unset($this->savePoints[$key]);
- }
- }
-
- return $i;
- }
-
- public function setIsolation($isolation)
- {
- throw new IPF_ORM_Exception('Transaction isolation levels not supported by this driver.');
- }
-
- public function getIsolation()
- {
- throw new IPF_ORM_Exception('Fetching transaction isolation level not supported by this driver.');
- }
-
- public function beginInternalTransaction($savepoint = null)
- {
- $this->_internalNestingLevel++;
- return $this->beginTransaction($savepoint);
- }
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_Transaction_Mysql extends IPF_ORM_Transaction
-{
- protected function createSavePoint($savepoint)
- {
- $query = 'SAVEPOINT ' . $savepoint;
-
- return $this->conn->execute($query);
- }
-
- protected function releaseSavePoint($savepoint)
- {
- $query = 'RELEASE SAVEPOINT ' . $savepoint;
-
- return $this->conn->execute($query);
- }
-
- protected function rollbackSavePoint($savepoint)
- {
- $query = 'ROLLBACK TO SAVEPOINT ' . $savepoint;
-
- return $this->conn->execute($query);
- }
-
- public function setIsolation($isolation)
- {
- switch ($isolation) {
- case 'READ UNCOMMITTED':
- case 'READ COMMITTED':
- case 'REPEATABLE READ':
- case 'SERIALIZABLE':
- break;
- default:
- throw new IPF_ORM_Exception('Isolation level ' . $isolation . ' is not supported.');
- }
-
- $query = 'SET SESSION TRANSACTION ISOLATION LEVEL ' . $isolation;
-
- return $this->conn->execute($query);
- }
-
- public function getIsolation()
- {
- return $this->conn->fetchOne('SELECT @@tx_isolation');
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-class IPF_ORM_Utils {
-
- public static function getConnectionStateAsString($state)
- {
- switch ($state) {
- case IPF_ORM_Transaction::STATE_SLEEP:
- return "open";
- break;
- case IPF_ORM_Transaction::STATE_BUSY:
- return "busy";
- break;
- case IPF_ORM_Transaction::STATE_ACTIVE:
- return "active";
- break;
- }
- }
-
- public static function getConnectionAsString(IPF_ORM_Connection $connection)
- {
- $r[] = '<pre>';
- $r[] = 'IPF_ORM_Connection object';
- $r[] = 'State : ' . IPF_ORM_Utils::getConnectionStateAsString($connection->transaction->getState());
- $r[] = 'Open Transactions : ' . $connection->transaction->getTransactionLevel();
- $r[] = 'Table in memory : ' . $connection->count();
- $r[] = 'Driver name : ' . $connection->getDriverName();
- $r[] = "</pre>";
- return implode("\n",$r)."<br>";
- }
-
- public static function getValidators()
- {
- return array(
- 'country',
- 'creditcard',
- 'date',
- 'driver',
- 'email',
- 'exception',
- 'future',
- 'htmlcolor',
- 'ip',
- 'minlength',
- 'nospace',
- 'notblank',
- 'notnull',
- 'past',
- 'range',
- 'readonly',
- 'regexp',
- 'time',
- 'timestamp',
- 'unique',
- 'unsigned',
- 'usstate',
- 'file',
- 'image',
- 'html',
- 'uploadTo',
- 'verbose',
- );
- }
-
-
- public static function arrayDeepMerge()
- {
- switch (func_num_args()) {
- case 0:
- return false;
- case 1:
- return func_get_arg(0);
- case 2:
- $args = func_get_args();
- $args[2] = array();
-
- if (is_array($args[0]) && is_array($args[1]))
- {
- foreach (array_unique(array_merge(array_keys($args[0]),array_keys($args[1]))) as $key)
- {
- $isKey0 = array_key_exists($key, $args[0]);
- $isKey1 = array_key_exists($key, $args[1]);
-
- if ($isKey0 && $isKey1 && is_array($args[0][$key]) && is_array($args[1][$key]))
- {
- $args[2][$key] = self::arrayDeepMerge($args[0][$key], $args[1][$key]);
- } else if ($isKey0 && $isKey1) {
- $args[2][$key] = $args[1][$key];
- } else if ( ! $isKey1) {
- $args[2][$key] = $args[0][$key];
- } else if ( ! $isKey0) {
- $args[2][$key] = $args[1][$key];
- }
- }
-
- return $args[2];
- } else {
- return $args[1];
- }
- default:
- $args = func_get_args();
- $args[1] = self::arrayDeepMerge($args[0], $args[1]);
- array_shift($args);
- return call_user_func_array(array('IPF', 'arrayDeepMerge'), $args);
- break;
- }
- }
-
- public static function getTableAsString(IPF_ORM_Table $table)
- {
- $r[] = "<pre>";
- $r[] = "Component : ".$table->getComponentName();
- $r[] = "Table : ".$table->getTableName();
- $r[] = "</pre>";
-
- return implode("\n",$r)."<br>";
- }
-
- public static function getCollectionAsString(IPF_ORM_Collection $collection)
- {
- $r[] = "<pre>";
- $r[] = get_class($collection);
- $r[] = 'data : ' . IPF_ORM::dump($collection->getData(), false);
- //$r[] = 'snapshot : ' . IPF_ORM::dump($collection->getSnapshot());
- $r[] = "</pre>";
- return implode("\n",$r);
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Validator
-{
- private static $validators = array();
- public static function getValidator($name)
- {
- if ( ! isset(self::$validators[$name])) {
- $class = 'IPF_ORM_Validator_' . ucfirst($name);
- if (class_exists($class)) {
- self::$validators[$name] = new $class;
- } else if (class_exists($name)) {
- self::$validators[$name] = new $name;
- } else {
- throw new IPF_ORM_Exception("Validator named '$name' not available.");
- }
-
- }
- return self::$validators[$name];
- }
-
- public function validateRecord(IPF_ORM_Record $record)
- {
- $table = $record->getTable();
-
- // if record is transient all fields will be validated
- // if record is persistent only the modified fields will be validated
- $fields = $record->exists() ? $record->getModified():$record->getData();
- foreach ($fields as $fieldName => $value) {
- $this->validateField($table, $fieldName, $value, $record);
- }
- }
-
- private function validateField(IPF_ORM_Table $table, $fieldName, $value, IPF_ORM_Record $record)
- {
- if (IPF_ORM_Null::isNull($value)) {
- $value = null;
- } else if ($value instanceof IPF_ORM_Record) {
- $value = $value->getIncremented();
- }
-
- $dataType = $table->getTypeOf($fieldName);
-
- // Validate field type
- if (!IPF_ORM_Validator::isValidType($value, $dataType)) {
- $record->addError($fieldName, 'type');
- }
-
- if ($dataType == 'enum') {
- $enumIndex = $table->enumIndex($fieldName, $value);
- if ($enumIndex === false) {
- $record->addError($fieldName, 'enum');
- }
- }
-
- // Validate field length
- $definition = $table->getDefinitionOf($fieldName);
- if (!$this->validateLength($value, $dataType, $definition['length'])) {
- $record->addError($fieldName, 'length');
- }
-
- // Run all custom validators
- foreach ($table->getFieldValidators($fieldName) as $validatorName => $args) {
- if (!is_string($validatorName)) {
- $validatorName = $args;
- $args = array();
- }
-
- $validator = IPF_ORM_Validator::getValidator($validatorName);
- $validator->invoker = $record;
- $validator->field = $fieldName;
- $validator->args = $args;
- if (!$validator->validate($value)) {
- $record->addError($fieldName, $validatorName);
- }
- }
- }
-
- public function validateLength($value, $type, $maximumLength)
- {
- if ($type == 'timestamp' || $type == 'integer' || $type == 'enum') {
- return true;
- } else if ($type == 'array' || $type == 'object') {
- $length = strlen(serialize($value));
- } else if ($type == 'decimal') {
- if (!$maximumLength)
- $maximumLength = 18;
- $length = strlen($value);
- } else {
- $length = strlen($value);
- }
-
- if ($length > $maximumLength) {
- return false;
- }
- return true;
- }
-
- public function hasErrors()
- {
- return (count($this->stack) > 0);
- }
-
- public static function isValidType($var, $type)
- {
- if ($var instanceof IPF_ORM_Expression) {
- return true;
- } else if ($var === null) {
- return true;
- } else if (is_object($var)) {
- return $type == 'object';
- }
-
- switch ($type) {
- case 'float':
- case 'double':
- case 'decimal':
- return (string)$var == strval(floatval($var));
- case 'integer':
- return (string)$var == strval(intval($var));
- case 'string':
- return is_string($var) || is_numeric($var);
- case 'blob':
- case 'clob':
- case 'gzip':
- return is_string($var);
- case 'array':
- return is_array($var);
- case 'object':
- return is_object($var);
- case 'boolean':
- return is_bool($var) || (is_numeric($var) && ($var == 0 || $var == 1));
- case 'timestamp':
- $validator = self::getValidator('timestamp');
- return $validator->validate($var);
- case 'time':
- $validator = self::getValidator('time');
- return $validator->validate($var);
- case 'date':
- $validator = self::getValidator('date');
- return $validator->validate($var);
- case 'enum':
- return is_string($var) || is_int($var);
- default:
- return false;
- }
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Validator_Country
-{
- private static $countries = array(
- 'ad' => 'Andorra',
- 'ae' => 'United Arab Emirates',
- 'af' => 'Afghanistan',
- 'ag' => 'Antigua and Barbuda',
- 'ai' => 'Anguilla',
- 'al' => 'Albania',
- 'am' => 'Armenia',
- 'an' => 'Netherlands Antilles',
- 'ao' => 'Angola',
- 'aq' => 'Antarctica',
- 'ar' => 'Argentina',
- 'as' => 'American Samoa',
- 'at' => 'Austria',
- 'au' => 'Australia',
- 'aw' => 'Aruba',
- 'az' => 'Azerbaijan',
- 'ba' => 'Bosnia Hercegovina',
- 'bb' => 'Barbados',
- 'bd' => 'Bangladesh',
- 'be' => 'Belgium',
- 'bf' => 'Burkina Faso',
- 'bg' => 'Bulgaria',
- 'bh' => 'Bahrain',
- 'bi' => 'Burundi',
- 'bj' => 'Benin',
- 'bm' => 'Bermuda',
- 'bn' => 'Brunei Darussalam',
- 'bo' => 'Bolivia',
- 'br' => 'Brazil',
- 'bs' => 'Bahamas',
- 'bt' => 'Bhutan',
- 'bv' => 'Bouvet Island',
- 'bw' => 'Botswana',
- 'by' => 'Belarus (Byelorussia)',
- 'bz' => 'Belize',
- 'ca' => 'Canada',
- 'cc' => 'Cocos Islands',
- 'cd' => 'Congo, The Democratic Republic of the',
- 'cf' => 'Central African Republic',
- 'cg' => 'Congo',
- 'ch' => 'Switzerland',
- 'ci' => 'Ivory Coast',
- 'ck' => 'Cook Islands',
- 'cl' => 'Chile',
- 'cm' => 'Cameroon',
- 'cn' => 'China',
- 'co' => 'Colombia',
- 'cr' => 'Costa Rica',
- 'cs' => 'Czechoslovakia',
- 'cu' => 'Cuba',
- 'cv' => 'Cape Verde',
- 'cx' => 'Christmas Island',
- 'cy' => 'Cyprus',
- 'cz' => 'Czech Republic',
- 'de' => 'Germany',
- 'dj' => 'Djibouti',
- 'dk' => 'Denmark',
- 'dm' => 'Dominica',
- 'do' => 'Dominican Republic',
- 'dz' => 'Algeria',
- 'ec' => 'Ecuador',
- 'ee' => 'Estonia',
- 'eg' => 'Egypt',
- 'eh' => 'Western Sahara',
- 'er' => 'Eritrea',
- 'es' => 'Spain',
- 'et' => 'Ethiopia',
- 'fi' => 'Finland',
- 'fj' => 'Fiji',
- 'fk' => 'Falkland Islands',
- 'fm' => 'Micronesia',
- 'fo' => 'Faroe Islands',
- 'fr' => 'France',
- 'fx' => 'France, Metropolitan FX',
- 'ga' => 'Gabon',
- 'gb' => 'United Kingdom (Great Britain)',
- 'gd' => 'Grenada',
- 'ge' => 'Georgia',
- 'gf' => 'French Guiana',
- 'gh' => 'Ghana',
- 'gi' => 'Gibraltar',
- 'gl' => 'Greenland',
- 'gm' => 'Gambia',
- 'gn' => 'Guinea',
- 'gp' => 'Guadeloupe',
- 'gq' => 'Equatorial Guinea',
- 'gr' => 'Greece',
- 'gs' => 'South Georgia and the South Sandwich Islands',
- 'gt' => 'Guatemala',
- 'gu' => 'Guam',
- 'gw' => 'Guinea-bissau',
- 'gy' => 'Guyana',
- 'hk' => 'Hong Kong',
- 'hm' => 'Heard and McDonald Islands',
- 'hn' => 'Honduras',
- 'hr' => 'Croatia',
- 'ht' => 'Haiti',
- 'hu' => 'Hungary',
- 'id' => 'Indonesia',
- 'ie' => 'Ireland',
- 'il' => 'Israel',
- 'in' => 'India',
- 'io' => 'British Indian Ocean Territory',
- 'iq' => 'Iraq',
- 'ir' => 'Iran',
- 'is' => 'Iceland',
- 'it' => 'Italy',
- 'jm' => 'Jamaica',
- 'jo' => 'Jordan',
- 'jp' => 'Japan',
- 'ke' => 'Kenya',
- 'kg' => 'Kyrgyzstan',
- 'kh' => 'Cambodia',
- 'ki' => 'Kiribati',
- 'km' => 'Comoros',
- 'kn' => 'Saint Kitts and Nevis',
- 'kp' => 'North Korea',
- 'kr' => 'South Korea',
- 'kw' => 'Kuwait',
- 'ky' => 'Cayman Islands',
- 'kz' => 'Kazakhstan',
- 'la' => 'Laos',
- 'lb' => 'Lebanon',
- 'lc' => 'Saint Lucia',
- 'li' => 'Lichtenstein',
- 'lk' => 'Sri Lanka',
- 'lr' => 'Liberia',
- 'ls' => 'Lesotho',
- 'lt' => 'Lithuania',
- 'lu' => 'Luxembourg',
- 'lv' => 'Latvia',
- 'ly' => 'Libya',
- 'ma' => 'Morocco',
- 'mc' => 'Monaco',
- 'md' => 'Moldova Republic',
- 'mg' => 'Madagascar',
- 'mh' => 'Marshall Islands',
- 'mk' => 'Macedonia, The Former Yugoslav Republic of',
- 'ml' => 'Mali',
- 'mm' => 'Myanmar',
- 'mn' => 'Mongolia',
- 'mo' => 'Macau',
- 'mp' => 'Northern Mariana Islands',
- 'mq' => 'Martinique',
- 'mr' => 'Mauritania',
- 'ms' => 'Montserrat',
- 'mt' => 'Malta',
- 'mu' => 'Mauritius',
- 'mv' => 'Maldives',
- 'mw' => 'Malawi',
- 'mx' => 'Mexico',
- 'my' => 'Malaysia',
- 'mz' => 'Mozambique',
- 'na' => 'Namibia',
- 'nc' => 'New Caledonia',
- 'ne' => 'Niger',
- 'nf' => 'Norfolk Island',
- 'ng' => 'Nigeria',
- 'ni' => 'Nicaragua',
- 'nl' => 'Netherlands',
- 'no' => 'Norway',
- 'np' => 'Nepal',
- 'nr' => 'Nauru',
- 'nt' => 'Neutral Zone',
- 'nu' => 'Niue',
- 'nz' => 'New Zealand',
- 'om' => 'Oman',
- 'pa' => 'Panama',
- 'pe' => 'Peru',
- 'pf' => 'French Polynesia',
- 'pg' => 'Papua New Guinea',
- 'ph' => 'Philippines',
- 'pk' => 'Pakistan',
- 'pl' => 'Poland',
- 'pm' => 'St. Pierre and Miquelon',
- 'pn' => 'Pitcairn',
- 'pr' => 'Puerto Rico',
- 'pt' => 'Portugal',
- 'pw' => 'Palau',
- 'py' => 'Paraguay',
- 'qa' => 'Qatar',
- 're' => 'Reunion',
- 'ro' => 'Romania',
- 'ru' => 'Russia',
- 'rw' => 'Rwanda',
- 'sa' => 'Saudi Arabia',
- 'sb' => 'Solomon Islands',
- 'sc' => 'Seychelles',
- 'sd' => 'Sudan',
- 'se' => 'Sweden',
- 'sg' => 'Singapore',
- 'sh' => 'St. Helena',
- 'si' => 'Slovenia',
- 'sj' => 'Svalbard and Jan Mayen Islands',
- 'sk' => 'Slovakia (Slovak Republic)',
- 'sl' => 'Sierra Leone',
- 'sm' => 'San Marino',
- 'sn' => 'Senegal',
- 'so' => 'Somalia',
- 'sr' => 'Suriname',
- 'st' => 'Sao Tome and Principe',
- 'sv' => 'El Salvador',
- 'sy' => 'Syria',
- 'sz' => 'Swaziland',
- 'tc' => 'Turks and Caicos Islands',
- 'td' => 'Chad',
- 'tf' => 'French Southern Territories',
- 'tg' => 'Togo',
- 'th' => 'Thailand',
- 'tj' => 'Tajikistan',
- 'tk' => 'Tokelau',
- 'tm' => 'Turkmenistan',
- 'tn' => 'Tunisia',
- 'to' => 'Tonga',
- 'tp' => 'East Timor',
- 'tr' => 'Turkey',
- 'tt' => 'Trinidad, Tobago',
- 'tv' => 'Tuvalu',
- 'tw' => 'Taiwan',
- 'tz' => 'Tanzania',
- 'ua' => 'Ukraine',
- 'ug' => 'Uganda',
- 'uk' => 'United Kingdom',
- 'um' => 'United States Minor Islands',
- 'us' => 'United States of America',
- 'uy' => 'Uruguay',
- 'uz' => 'Uzbekistan',
- 'va' => 'Vatican City',
- 'vc' => 'Saint Vincent, Grenadines',
- 've' => 'Venezuela',
- 'vg' => 'Virgin Islands (British)',
- 'vi' => 'Virgin Islands (USA)',
- 'vn' => 'Viet Nam',
- 'vu' => 'Vanuatu',
- 'wf' => 'Wallis and Futuna Islands',
- 'ws' => 'Samoa',
- 'ye' => 'Yemen',
- 'yt' => 'Mayotte',
- 'yu' => 'Yugoslavia',
- 'za' => 'South Africa',
- 'zm' => 'Zambia',
- 'zr' => 'Zaire',
- 'zw' => 'Zimbabwe');
-
- public static function getCountries()
- {
- return self::$countries;
- }
-
- public function validate($value)
- {
- $value = strtolower($value);
- return isset(self::$countries[$value]);
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Validator_Creditcard
-{
- public function validate($value)
- {
- $card_regexes = array(
- "/^4\d{12}(\d\d\d){0,1}$/" => 'visa',
- "/^5[12345]\d{14}$/" => 'mastercard',
- "/^3[47]\d{13}$/" => 'amex',
- "/^6011\d{12}$/" => 'discover',
- "/^30[012345]\d{11}$/" => 'diners',
- "/^3[68]\d{12}$/" => 'diners',
- );
-
- $cardType = '';
- foreach ($card_regexes as $regex => $type) {
- if (preg_match($regex, $value)) {
- $cardType = $type;
- break;
- }
- }
- if (!$cardType)
- return false;
-
- /* mod 10 checksum algorithm */
- $revcode = strrev($value);
- $checksum = 0;
- for ($i = 0; $i < strlen($revcode); $i++) {
- $currentNum = intval($revcode[$i]);
- if ($i & 1) { /* Odd position */
- $currentNum *= 2;
- }
- /* Split digits and add. */
- $checksum += $currentNum % 10;
- if ($currentNum > 9) {
- $checksum += 1;
- }
- }
- return $checksum % 10 == 0;
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Validator_Date
-{
- public function validate($value)
- {
- if ($value === null) {
- return true;
- }
- $e = explode('-', $value);
-
- if (count($e) !== 3) {
- return false;
- }
- return checkdate($e[1], $e[2], $e[0]);
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-class IPF_ORM_Validator_Driver
-{
- protected $_args = array();
-
- public function __get($arg)
- {
- if (isset($this->_args[$arg])) {
- return $this->_args[$arg];
- }
- return null;
- }
-
- public function __isset($arg)
- {
- return isset($this->_args[$arg]);
- }
-
- public function __set($arg, $value)
- {
- $this->_args[$arg] = $value;
-
- return $this;
- }
-
- public function getArg($arg)
- {
- if ( ! isset($this->_args[$arg])) {
- throw new IPF_ORM_Exception_Validator('Unknown option ' . $arg);
- }
- return $this->_args[$arg];
- }
-
- public function setArg($arg, $value)
- {
- $this->_args[$arg] = $value;
-
- return $this;
- }
-
- public function getArgs()
- {
- return $this->_args;
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-class IPF_ORM_Validator_Email
-{
- public function validate($value)
- {
- if ($value === null)
- return true;
- return IPF_Utils::isEmail($value);
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-class IPF_ORM_Validator_Exclude
-{
- public function validate($value)
- {
- return true;
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Validator_File
-{
- public function validate($value){
- return true;
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-class IPF_ORM_Validator_Future
-{
- public function validate($value)
- {
- if ($value === null) {
- return true;
- }
- $e = explode('-', $value);
-
- if (count($e) !== 3) {
- return false;
- }
-
- if (is_array($this->args) && isset($this->args['timezone'])) {
- switch (strtolower($this->args['timezone'])) {
- case 'gmt':
- $now = gmdate("U") - date("Z");
- break;
- default:
- $now = getdate();
- break;
- }
- } else {
- $now = getdate();
- }
-
- if ($now['year'] > $e[0]) {
- return false;
- } else if ($now['year'] == $e[0]) {
- if ($now['mon'] > $e[1]) {
- return false;
- } else if ($now['mon'] == $e[1]) {
- return $now['mday'] < $e[2];
- } else {
- return true;
- }
- } else {
- return true;
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-class IPF_ORM_Validator_Html
-{
- public function validate($value){
- return true;
- }
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_Validator_Htmlcolor
-{
- public function validate($value)
- {
- if ( ! preg_match("/^#{0,1}[0-9a-fA-F]{6}$/", $value)) {
- return false;
- }
- return true;
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-class IPF_ORM_Validator_Image
-{
- public function validate($value){
- return true;
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-class IPF_ORM_Validator_Ip
-{
- public function validate($value)
- {
- return (bool) ip2long(str_replace("\0", '', $value));
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-class IPF_ORM_Validator_Minlength
-{
- public function validate($value)
- {
- if (isset($this->args) && strlen($value) < $this->args) {
- return false;
- }
- return true;
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-class IPF_ORM_Validator_Nospace extends IPF_ORM_Validator_Driver
-{
- public function validate($value)
- {
- return ($value === null || ! preg_match('/\s/', $value));
- }
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_Validator_Notblank extends IPF_ORM_Validator_Driver
-{
- public function validate($value)
- {
- return (trim($value) !== '' && $value !== null);
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-class IPF_ORM_Validator_Notnull extends IPF_ORM_Validator_Driver
-{
- public function validate($value)
- {
- return ($value !== null);
- }
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_Validator_Past
-{
- public function validate($value)
- {
- if ($value === null) {
- return true;
- }
- $e = explode('-', $value);
-
- if (count($e) !== 3) {
- return false;
- }
-
- if (is_array($this->args) && isset($this->args['timezone'])) {
- switch (strtolower($this->args['timezone'])) {
- case 'gmt':
- $now = gmdate("U") - date("Z");
- break;
- default:
- $now = getdate();
- break;
- }
- } else {
- $now = getdate();
- }
-
- if ($now['year'] < $e[0]) {
- return false;
- } else if ($now['year'] == $e[0]) {
- if ($now['mon'] < $e[1]) {
- return false;
- } else if ($now['mon'] == $e[1]) {
- return $now['mday'] > $e[2];
- } else {
- return true;
- }
- } else {
- return true;
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-class IPF_ORM_Validator_Range
-{
- public function validate($value)
- {
- if (isset($this->args[0]) && $value < $this->args[0]) {
- return false;
- }
- if (isset($this->args[1]) && $value > $this->args[1]) {
- return false;
- }
- return true;
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-class IPF_ORM_Validator_Readonly
-{
- public function validate($value)
- {
- $modified = $this->invoker->getModified();
- return array_key_exists($this->field, $modified) ? false : true;
- }
-}
+++ /dev/null
-<?php
-
-class IPF_ORM_Validator_Regexp
-{
- public function validate($value)
- {
- if ( ! isset($this->args)) {
- return true;
- }
- if (is_array($this->args)) {
- foreach ($this->args as $regexp) {
- if ( ! preg_match($regexp, $value)) {
- return false;
- }
- }
- return true;
- } else {
- if (preg_match($this->args, $value)) {
- return true;
- }
- }
-
- return false;
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-class IPF_ORM_Validator_Time
-{
- public function validate($value)
- {
- if ($value === null) {
- return true;
- }
-
- $e = explode(':', $value);
-
- if (count($e) !== 3) {
- return false;
- }
-
- if ( ! preg_match('/^ *[0-9]{2}:[0-9]{2}:[0-9]{2} *$/', $value)) {
- return false;
- }
-
- $hr = intval($e[0], 10);
- $min = intval($e[1], 10);
- $sec = intval($e[2], 10);
-
- return $hr >= 0 && $hr <= 23 && $min >= 0 && $min <= 59 && $sec >= 0 && $sec <= 59;
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-class IPF_ORM_Validator_Timestamp
-{
- public function validate($value)
- {
- if ($value === null) {
- return true;
- }
-
- if ( ! preg_match('/^ *[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} *$/', $value)) {
- return false;
- }
-
- list($date, $time) = explode(' ', trim($value));
-
- $dateValidator = IPF_ORM_Validator::getValidator('date');
- $timeValidator = IPF_ORM_Validator::getValidator('time');
-
- if ( ! $dateValidator->validate($date)) {
- return false;
- }
-
- if ( ! $timeValidator->validate($time)) {
- return false;
- }
-
- return true;
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-class IPF_ORM_Validator_Unique
-{
- public function validate($value)
- {
- $table = $this->invoker->getTable();
- $pks = $table->getIdentifier();
-
- if ( is_array($pks) ) {
- $pks = join(',', $pks);
- }
-
- $sql = 'SELECT ' . $pks . ' FROM ' . $table->getTableName() . ' WHERE ' . $this->field . ' = ?';
-
- $values = array();
- $values[] = $value;
-
- // If the record is not new we need to add primary key checks because its ok if the
- // unique value already exists in the database IF the record in the database is the same
- // as the one that is validated here.
- $state = $this->invoker->state();
- if ( ! ($state == IPF_ORM_Record::STATE_TDIRTY || $state == IPF_ORM_Record::STATE_TCLEAN)) {
- foreach ((array) $table->getIdentifier() as $pk) {
- $sql .= " AND {$pk} != ?";
- $values[] = $this->invoker->$pk;
- }
- }
-
- $stmt = $table->getConnection()->getDbh()->prepare($sql);
- $stmt->execute($values);
-
- return ( ! is_array($stmt->fetch()));
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-class IPF_ORM_Validator_Unsigned
-{
- public function validate($value)
- {
- $int = (int) $value;
-
- if ($int != $value || $int < 0) {
- return false;
- }
- return true;
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-class IPF_ORM_Validator_UploadTo
-{
- public function validate($value)
- {
- return true;
- }
-}
-
+++ /dev/null
-<?php
-
-class IPF_ORM_Validator_Usstate
-{
- private static $states = array(
- 'AK' => true,
- 'AL' => true,
- 'AR' => true,
- 'AZ' => true,
- 'CA' => true,
- 'CO' => true,
- 'CT' => true,
- 'DC' => true,
- 'DE' => true,
- 'FL' => true,
- 'GA' => true,
- 'HI' => true,
- 'IA' => true,
- 'ID' => true,
- 'IL' => true,
- 'IN' => true,
- 'KS' => true,
- 'KY' => true,
- 'LA' => true,
- 'MA' => true,
- 'MD' => true,
- 'ME' => true,
- 'MI' => true,
- 'MN' => true,
- 'MO' => true,
- 'MS' => true,
- 'MT' => true,
- 'NC' => true,
- 'ND' => true,
- 'NE' => true,
- 'NH' => true,
- 'NJ' => true,
- 'NM' => true,
- 'NV' => true,
- 'NY' => true,
- 'OH' => true,
- 'OK' => true,
- 'OR' => true,
- 'PA' => true,
- 'PR' => true,
- 'RI' => true,
- 'SC' => true,
- 'SD' => true,
- 'TN' => true,
- 'TX' => true,
- 'UT' => true,
- 'VA' => true,
- 'VI' => true,
- 'VT' => true,
- 'WA' => true,
- 'WI' => true,
- 'WV' => true,
- 'WY' => true
- );
- public function getStates()
- {
- return self::$states;
- }
-
- public function validate($value)
- {
- return isset(self::$states[$value]);
- }
-}
\ No newline at end of file
+++ /dev/null
-<?php
-
-class IPF_ORM_Validator_Verbose
-{
- public function validate($value)
- {
- return true;
- }
-}
-
--- /dev/null
+<?php
+
+use \PFF\HtmlBuilder\Text as Text;
+use \PFF\HtmlBuilder\Tag as Tag;
+
+class IPF_Admin_Model extends IPF_Admin_Component
+{
+ public $modelName;
+
+ public function __construct($modelName)
+ {
+ $this->modelName = $modelName;
+ }
+
+ public function verbose_name()
+ {
+ return IPF_Utils::humanTitle($this->modelName);
+ }
+
+ public function slug()
+ {
+ return strtolower($this->modelName);
+ }
+
+ private function table()
+ {
+ return IPF_ORM::getTable($this->modelName);
+ }
+
+ protected function query($searchValue, $filters)
+ {
+ if (method_exists($this->modelName, 'ordering')) {
+ $m = new $this->modelName;
+ $ord = $m->ordering();
+ } elseif ($this->table()->getOrdering()) {
+ $ord = implode(', ', $this->table()->getOrdering());
+ } elseif ($this->table()->hasTemplate('IPF_ORM_Template_Orderable')) {
+ $ord = $this->table()->getTemplate('IPF_ORM_Template_Orderable')->getColumnName();
+ } else {
+ $ord = '1 desc';
+ }
+
+ $q = IPF_ORM_Query::create()->from($this->modelName)->orderby($ord);
+
+ if ($searchValue) {
+ $wh = array();
+ $whv = array();
+ foreach ($this->_searchFields() as $f) {
+ $wh[] = $f.' like ?';
+ $whv[] = '%'.$searchValue.'%';
+ }
+ $q->addWhere($wh, $whv);
+ }
+
+ foreach ($filters as $f)
+ $f->applyToQuery($q);
+
+ return $q;
+ }
+
+ protected function getItems($searchValue, $filters, $page, $pageSize)
+ {
+ $idColumn = $this->table()->getIdentifier();
+
+ $result = array();
+ foreach ($this->query($searchValue, $filters)->limit($pageSize)->offset(($page - 1) * $pageSize)->execute() as $o) {
+ $id = $o->__get($idColumn);
+ $result[$id] = $o;
+ }
+ return $result;
+ }
+
+ protected function itemsCount($searchValue, $filters)
+ {
+ return $this->query($searchValue, $filters)->count();
+ }
+
+ public function getObjectByID($id)
+ {
+ return $this->table()->find($id);
+ }
+
+ public function searcheable()
+ {
+ return count($this->_searchFields()) > 0;
+ }
+
+ protected function _searchFields()
+ {
+ return array();
+ }
+
+ public function list_display()
+ {
+ return $this->table()->getColumnNames();
+ }
+
+ public function renderCell($object, $column)
+ {
+ $value = $object->$column;
+ switch ($object->getTable()->getTypeOf($column)) {
+ case 'boolean':
+ return $value
+ ? '<span class="positive">✔</span>'
+ : '<span class="negative">✘</span>';
+ case 'timestamp':
+ return Text::escape(IPF_Utils::formatDate($value));
+ default:
+ return Text::escape($value);
+ }
+ }
+
+ protected function columnTitle($column)
+ {
+ $columns = $this->table()->getColumns();
+ if (array_key_exists($column, $columns) && array_key_exists('verbose', $columns[$column]))
+ return $columns[$column]['verbose'];
+ else
+ return parent::columnTitle($column);
+ }
+
+ public function _orderable()
+ {
+ return $this->_orderableColumn() !== null;
+ }
+
+ public function _orderableColumn()
+ {
+ if (method_exists($this, 'list_order'))
+ return $this->list_order();
+ elseif ($this->table()->hasTemplate('IPF_ORM_Template_Orderable'))
+ return $this->table()->getTemplate('IPF_ORM_Template_Orderable')->getColumnName();
+ else
+ return null;
+ }
+
+ /* filters */
+
+ protected function _listFilters()
+ {
+ return array();
+ }
+
+ public function listFilters()
+ {
+ $filters = array();
+ foreach ($this->_listFilters() as $f) {
+ if (is_string($f))
+ $f = new IPF_Admin_Model_RelationFilter($this->modelName, $f);
+
+ $filters[] = $f;
+ }
+ return $filters;
+ }
+
+ public function reorder($ids)
+ {
+ if (!$this->_orderable())
+ return false;
+
+ $ord_field = $this->_orderableColumn();
+
+ $table = IPF_ORM::getTable($this->modelName);
+ $conn = $table->getConnection();
+
+ $idColumn = $table->getIdentifier();
+ if (is_array($idColumn))
+ $idColumn = $idColumn[0];
+
+ $questions = str_repeat('?,', count($ids)-1) . '?';
+ $query = 'SELECT ' . $conn->quoteIdentifier($ord_field) .
+ ' FROM ' . $conn->quoteIdentifier($table->getTableName()) .
+ ' WHERE ' . $conn->quoteIdentifier($idColumn) . ' IN (' . $questions . ')' .
+ ' ORDER BY ' . $conn->quoteIdentifier($ord_field);
+ $ords = $conn->fetchColumn($query, $ids);
+
+ $i = 0;
+ foreach ($ids as $id) {
+ $item = $table->find($id);
+ $item[$ord_field] = $ords[$i];
+ $item->save();
+ $i++;
+ }
+
+ return true;
+ }
+
+ /* edit */
+
+ protected function _getForm($model, $data)
+ {
+ $extra = array(
+ 'user_fields' => $this->fields(),
+ 'inlines' => $this->inlines(),
+ );
+ return new IPF_Admin_ModelForm($data, $model, $this->modelName, $extra);
+ }
+
+ public function inlines()
+ {
+ return array();
+ }
+
+ public function saveObject($form, $model)
+ {
+ $model = $form->save();
+ $idColumn = $this->table()->getIdentifier();
+ return array($model->$idColumn, $model);
+ }
+
+ public function deleteObject($model)
+ {
+ $model->delete();
+ }
+
+ public function fields()
+ {
+ return null;
+ }
+}
+
+class IPF_Admin_ModelForm extends IPF_Form_Model
+{
+ private $inlines = array();
+
+ function __construct($data=null, $model=null, $modelClass='', $extra=array())
+ {
+ $editMode = $model !== null;
+
+ if (!$model)
+ $model = new $modelClass;
+
+ $extra['model'] = $model;
+
+ if ($extra['inlines']) {
+ foreach ($extra['inlines'] as $inlineClassName) {
+ $this->inlines[] = new $inlineClassName($model);
+ }
+ }
+
+ if ($editMode) {
+ $extra['initial'] = $this->getFormData($model, $this->inlines);
+ }
+
+ parent::__construct($data, $extra);
+ }
+
+ function initFields($extra=array())
+ {
+ parent::initFields($extra);
+
+ if ($this->inlines) {
+ $this->field_groups[] = array('fields' => array_keys($this->fields));
+ }
+
+ foreach ($this->inlines as $inline) {
+ $name = $inline->getModelName();
+ $this->fields[$name] = $inline->createField();
+
+ $this->field_groups[] = array(
+ 'label' => $inline->getLegend(),
+ 'fields' => array($name),
+ );
+ }
+ }
+
+ private function getFormData($o, $inlines)
+ {
+ $data = $o->getData();
+ foreach ($o->getTable()->getRelations() as $rname => $rel) {
+ $fields = $this->fields();
+ if (!$fields || in_array($rname, $fields)) {
+ if ($rel->getType() == IPF_ORM_Relation::MANY_AGGREGATE) {
+ $data[$rname] = array();
+ foreach ($rel->fetchRelatedFor($o) as $ri) {
+ $data[$rname][] = $ri->pk();
+ }
+ }
+ }
+ }
+
+ foreach ($inlines as $inline) {
+ $objs = array();
+ foreach ($inline->getObjects() as $io) {
+ $d = $io->getData();
+ $d['id'] = $io->pk();
+ $objs[] = $d;
+ }
+ $data[$inline->getModelName()] = $objs;
+ }
+
+ return $data;
+ }
+
+ function save($commit=true)
+ {
+ $model = parent::save($commit);
+
+ foreach ($this->inlines as $inline) {
+ $this->saveInlineObject($inline, $model);
+ }
+
+ return $model;
+ }
+
+ private function saveInlineObject($inline, $model)
+ {
+ $objIndex = array();
+ foreach ($inline->getObjects() as $obj)
+ $objIndex[$obj->pk()] = $obj;
+
+ $modelName = $inline->getModelName();
+ $fk_local = $inline->getFkLocal();
+
+ foreach ($this->cleaned_data[$modelName] as $objData) {
+ if (array_key_exists('id', $objData) && array_key_exists($objData['id'], $objIndex)) {
+ $obj = $objIndex[$objData['id']];
+ if (isset($objData['is_remove'])) {
+ $obj->delete();
+ } else {
+ $obj->synchronizeWithArray($objData);
+ $obj->save();
+ }
+ } else {
+ $objData[$fk_local] = $model->id;
+ $obj = new $modelName;
+ $obj->synchronizeWithArray($objData);
+ $obj->save();
+ }
+ }
+ }
+}
+
+abstract class IPF_Admin_Model_Filter implements IPF_Admin_ListFilter
+{
+ abstract function applyToQuery($q);
+
+ protected $title, $paramName;
+
+ function title()
+ {
+ return $this->title;
+ }
+
+ protected function link($params, $value, $title)
+ {
+ if ($value !== null)
+ $params[$this->paramName] = $value;
+ else
+ unset($params[$this->paramName]);
+
+ return Tag::a()
+ ->attr('href', '?'.IPF_HTTP_URL::generateParams($params, false))
+ ->append($title);
+ }
+}
+
+class IPF_Admin_Model_RelationFilter extends IPF_Admin_Model_Filter
+{
+ private $relation, $selected = null;
+
+ function __construct($modelName, $relName)
+ {
+ $this->relation = IPF_ORM::getTable($modelName)->getRelation($relName);
+ $this->title = 'By '.IPF_Utils::humanTitle($relName);
+
+ $this->paramName = 'filter_'.$this->relation['local'];
+ }
+
+ function setParams($params)
+ {
+ $this->selected = \PFF\Arr::get($params, $this->paramName);
+ }
+
+ function applyToQuery($q)
+ {
+ // check ???
+ if ($this->selected)
+ $q->addWhere($this->relation['local'].' = ?', array($this->selected));
+ }
+
+ function render($extraParams)
+ {
+ $ul = Tag::ul();
+
+ // reset filter
+ $ul->append(Tag::li()
+ ->toggleClass('selected', !$this->selected)
+ ->append($this->link($extraParams, null, __('All'))));
+
+ // query related
+ $table = IPF_ORM::getTable($this->relation['class']);
+
+ $query = $table->createQuery();
+ if ($table->getOrdering()) {
+ $query->orderBy(implode(', ', $table->getOrdering()));
+ } elseif ($table->hasTemplate('IPF_ORM_Template_Orderable')) {
+ $query->orderBy($table->getTemplate('IPF_ORM_Template_Orderable')->getColumnName());
+ }
+
+ $foreign = $this->relation['foreign'];
+ foreach ($query->execute() as $val) {
+ $id = $val[$foreign];
+ $name = (string)$val;
+
+ $ul->append(Tag::li()
+ ->toggleClass('selected', $this->selected == $id)
+ ->append($this->link($extraParams, $id, $name)));
+ }
+
+ return $ul->html();
+ }
+}
+
+class IPF_Admin_Model_OwnedFilter extends IPF_Admin_Model_Filter
+{
+ private $column, $selected = null;
+
+ function __construct($modelName, $column, $title)
+ {
+ $this->column = $column;
+ $this->title = $title;
+ $this->paramName = 'filter_'.$this->column;
+ }
+
+ function setParams($params)
+ {
+ $this->selected = \PFF\Arr::get($params, $this->paramName);
+ }
+
+ function applyToQuery($q)
+ {
+ // check ???
+ if ($this->selected)
+ $q->addWhere($this->column.' = ?', array($this->selected));
+ }
+
+ function render($extraParams)
+ {
+ $ul = Tag::ul();
+
+ // reset filter
+ $ul->append(Tag::li()
+ ->toggleClass('selected', !$this->selected)
+ ->append($this->link($extraParams, null, __('All'))));
+
+ foreach (User::query()->fetchAll() as $val) {
+ $id = $val->id;
+ $name = (string)$val;
+
+ $ul->append(Tag::li()
+ ->toggleClass('selected', $this->selected == $id)
+ ->append($this->link($extraParams, $id, $name)));
+ }
+
+ return $ul->html();
+ }
+}
+
+class BooleanFilter extends IPF_Admin_Model_Filter
+{
+ private $column, $trueTitle, $falseTitle, $selected;
+
+ public function __construct($column, $title, $trueTitle='Yes', $falseTitle='No')
+ {
+ $this->column = $column;
+ $this->title = $title;
+ $this->trueTitle = $trueTitle;
+ $this->falseTitle = $falseTitle;
+
+ $this->paramName = 'filter_'.$column;
+ }
+
+ function setParams($params)
+ {
+ $this->selected = \PFF\Arr::get($params, $this->paramName);
+ }
+
+ function render($extraParams)
+ {
+ return Tag::ul(null,
+ Tag::li()
+ ->toggleClass('selected', $this->selected !== 'y' && $this->selected !== 'n')
+ ->append($this->link($extraParams, null, __('All'))),
+ Tag::li()
+ ->toggleClass('selected', $this->selected === 'y')
+ ->append($this->link($extraParams, 'y', $this->trueTitle)),
+ Tag::li()
+ ->toggleClass('selected', $this->selected === 'n')
+ ->append($this->link($extraParams, 'n', $this->falseTitle)));
+ }
+
+ function applyToQuery($q)
+ {
+ switch ($this->selected) {
+ case 'y':
+ $query->addWhere($this->column);
+ break;
+ case 'n':
+ $query->addWhere('NOT '.$this->column);
+ break;
+ }
+ }
+}
+
+class DateHierarchyListFilter extends IPF_Admin_Model_Filter
+{
+ private $model, $name;
+ private $day, $month, $year;
+
+ function __construct($title, $modelName, $fieldName)
+ {
+ $this->title = $title;
+ $this->modelName = $modelName;
+ $this->name = $fieldName;
+
+ $this->paramName = $fieldName;
+ }
+
+ function setParams($params)
+ {
+ $date = \PFF\Arr::get($params, $this->paramName, '');
+ $matches = array();
+ if (preg_match('/(\d{4})-(\d{2})-(\d{2})/', $date, $matches)) {
+ $this->year = intval($matches[1]);
+ $this->month = intval($matches[2]);
+ $this->day = intval($matches[3]);
+ }
+ }
+
+ function render($extraParams)
+ {
+ $ul = Tag::ul();
+
+ $ul->append(Tag::li()
+ ->toggleClass('selected', !$this->year)
+ ->append($this->link($extraParams, null, __('All'))));
+
+ if ($this->year)
+ $ul->append($this->renderChoice($extraParams, $this->year, 0, 0, $this->year)->addClass('selected'));
+
+ if ($this->month)
+ $ul->append($this->renderChoice($extraParams, $this->year, $this->month, 0, $this->monthName($this->month))->addClass('selected'));
+
+ if ($this->day)
+ $ul->append($this->renderChoice($extraParams, $this->year, $this->month, $this->day, $this->day)->addClass('selected'));
+
+ if ($this->day) {
+ } elseif ($this->month) {
+ $days = $this->choices('DAY(' . $this->name . ')',
+ 'YEAR(' . $this->name . ')', $this->year,
+ 'MONTH(' . $this->name . ')', $this->month);
+ foreach ($days as $day) {
+ $ul->append($this->renderChoice($extraParams, $this->year, $this->month, $day, $day));
+ }
+ } elseif ($this->year) {
+ $months = $this->choices('MONTH(' . $this->name . ')',
+ 'YEAR(' . $this->name . ')', $this->year);
+ foreach ($months as $month) {
+ $ul->append($this->renderChoice($extraParams, $this->year, $month, 0, $this->monthName($month)));
+ }
+ } else {
+ $years = $this->choices('YEAR(' . $this->name . ')');
+ foreach ($years as $year) {
+ $ul->append($this->renderChoice($extraParams, $year, 0, 0, $year));
+ }
+ }
+
+ return $ul->html();
+ }
+
+ function applyToQuery($q)
+ {
+ if ($this->day)
+ $q->addWhere('DAY(' . $this->name . ') = ?', array($this->day));
+ if ($this->month)
+ $q->addWhere('MONTH(' . $this->name . ') = ?', array($this->month));
+ if ($this->year)
+ $q->addWhere('YEAR(' . $this->name . ') = ?', array($this->year));
+ }
+
+ private function choices(/* $what, [$expr, $value, ...]*/)
+ {
+ $args = func_get_args();
+ $what = array_shift($args);
+
+ $q = IPF_ORM_Query::create()
+ ->from($this->modelName)
+ ->select($what . ' AS value')
+ ->groupBy('1')
+ ->orderBy('1');
+
+ while ($args) {
+ $expr = array_shift($args);
+ $value = array_shift($args);
+
+ $q->addWhere($expr . ' = ?', array($value));
+ }
+ return \PFF\Arr::create($q->fetchArray())->pluck('value')->arr();
+ }
+
+ private function renderChoice($extraParams, $year, $month, $day, $label)
+ {
+ return Tag::li(null,
+ $this->link($extraParams, sprintf('%04d-%02d-%02d', $year, $month, $day), $label));
+ }
+
+ protected function monthName($month)
+ {
+ return date('F', mktime(0, 0, 0, $month, 1));
+ }
+}
+
--- /dev/null
+<?php
+
+abstract class IPF_Admin_ModelInline
+{
+ public $parentModel = null;
+ public $orderby = 'id';
+
+ function __construct($parentModel)
+ {
+ $this->parentModel = $parentModel;
+ }
+
+ abstract function getModelName();
+
+ public function getApplication()
+ {
+ foreach (IPF_Project::getInstance()->appList() as $app)
+ foreach (IPF_Legacy_ORM_App::appModelList($app) as $model)
+ if ($model == $this->getModelName())
+ return $app;
+ return null;
+ }
+
+ function getAddNum()
+ {
+ return 4;
+ }
+
+ function getLegend()
+ {
+ return $this->getModelName();
+ }
+
+ function getFkName()
+ {
+ foreach($this->getTable()->getRelations() as $rel) {
+ if ($rel->getClass()==get_class($this->parentModel))
+ return $rel->getAlias();
+ }
+ throw new IPF_Exception(__('Cannot get fkName for '.$this->getModelName()));
+ }
+
+ function getFkLocal()
+ {
+ foreach($this->getTable()->getRelations() as $rel) {
+ if ($rel->getClass() == get_class($this->parentModel))
+ return $rel->getLocal();
+ }
+ throw new IPF_Exception(__('Cannot get fkLocal for '.$this->getModelName()));
+ }
+
+ function getObjects()
+ {
+ if (!$this->parentModel->exists())
+ return array();
+
+ $query = IPF_ORM_Query::create()
+ ->from($this->getModelName())
+ ->where($this->getFkLocal().'='.$this->parentModel->id);
+
+ $o = $this->_orderableColumn();
+ if ($o)
+ $query->orderby($o);
+ else
+ $query->orderby($this->orderby);
+
+ return $query->execute();
+ }
+
+ public function _orderable()
+ {
+ return $this->_orderableColumn() !== null;
+ }
+
+ public function _orderableColumn()
+ {
+ if ($this->getTable()->hasTemplate('IPF_ORM_Template_Orderable'))
+ return $this->getTable()->getTemplate('IPF_ORM_Template_Orderable')->getColumnName();
+ else
+ return null;
+ }
+
+ private function getTable()
+ {
+ return IPF_ORM::getTable($this->getModelName());
+ }
+
+ public function createField()
+ {
+ $exclude = array(
+ $this->getFkName(),
+ $this->getFkLocal(),
+ );
+ $o = $this->_orderableColumn();
+ if ($o)
+ $exclude[] = $o;
+
+ $fields = array();
+ foreach (IPF_Form_Model::suggestFields($this->getTable(), null, $exclude, null) as $field) {
+ list($n, $f) = $field;
+ $fields[$n] = $f;
+ }
+
+ return new IPF_Form_Field_Set(array(
+ 'fields' => $fields,
+ 'addCount' => $this->getAddNum(),
+ ));
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_Legacy_ORM_App extends IPF_Application
+{
+ private static $appModels = array();
+
+ public static function appModelList($app)
+ {
+ if (!array_key_exists($app->getName(), self::$appModels)) {
+ $models = array();
+ try {
+ $it = new DirectoryIterator($app->getPath().DIRECTORY_SEPARATOR.'models');
+ foreach ($it as $file) {
+ $e = explode('.', $file->getFileName(), 2);
+ if (count($e) == 2 && $e[1] === 'php') {
+ $models[] = $e[0];
+ }
+ }
+ } catch (RuntimeException $e) {
+ // nothing to do
+ }
+
+ self::$appModels[$app->getName()] = $models;
+ }
+ return self::$appModels[$app->getName()];
+ }
+
+ public function commands()
+ {
+ return array(
+ new IPF_Legacy_ORM_Command_BuildModels,
+ new IPF_Legacy_ORM_Command_Sql,
+ new IPF_Legacy_ORM_Command_SyncDB,
+ new IPF_Legacy_ORM_Command_Fixtures,
+ );
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_Legacy_ORM_Command_BuildModels
+{
+ public $command = 'buildmodels';
+ public $description = 'Build all model classes';
+
+ public function run($args=null)
+ {
+ print "Build all model classes\n\n";
+
+ $project = IPF_Project::getInstance();
+
+ $paths = array();
+ foreach ($project->appList() as $app)
+ $paths[] = $app->getPath();
+
+ IPF_ORM::generateModelsFromYaml($paths, array());
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_Legacy_ORM_Command_Fixtures
+{
+ public $command = 'fixtures';
+ public $description = 'Load fixtures into database';
+
+ public function run($args=null)
+ {
+ print "Load project fixtures to database\n";
+
+ $project = IPF_Project::getInstance();
+
+ $paths = array(IPF::get('project_path'));
+ foreach ($project->appList() as $app)
+ $paths[] = $app->path;
+
+ $fixtures = array();
+ foreach ($paths as $path) {
+ $path .= DIRECTORY_SEPARATOR.'fixtures.php';
+ if (is_file($path))
+ $fixtures = array_merge($fixtures, require $path);
+ }
+
+ if (!count($fixtures)) {
+ echo "No fixtures found\n";
+ return;
+ }
+
+ foreach ($fixtures as $fixture) {
+ $modelClass = $fixture['model'];
+ $key = $fixture['key'];
+ if (!is_array($key))
+ $key = array($key);
+ $records = $fixture['records'];
+ echo "Loading $modelClass ";
+ $table = IPF_ORM::getTable($modelClass);
+ $table->getConnection()->beginTransaction();
+
+ $query = $table
+ ->createQuery()
+ ->limit(1);
+ foreach ($key as $k)
+ $query->addWhere($k . ' = ?');
+
+ foreach ($records as $record) {
+ $params = array();
+ foreach ($key as $k)
+ $params[] = $record[$k];
+
+ $model = $query->execute($params);
+ if ($model)
+ $model = $model[0];
+ else
+ $model = new $modelClass;
+
+ foreach ($record as $k => $v)
+ $model->$k = $v;
+
+ $model->save();
+ echo '.';
+ }
+ $table->getConnection()->commit();
+ echo "\n";
+ }
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_Legacy_ORM_Command_Sql
+{
+ public $command = 'sql';
+ public $description = 'Show all SQL DDL from model classes';
+
+ public function run($args=null)
+ {
+ print "Show all SQL DDL from model classes\n";
+
+ foreach (IPF_Project::getInstance()->appList() as $app)
+ print IPF_ORM::generateSqlFromModels($app)."\n";
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_Legacy_ORM_Command_SyncDB
+{
+ public $command = 'syncdb';
+ public $description = 'Create tables from model classes';
+
+ public function run($args=null)
+ {
+ print "Create tables from model classes\n";
+
+ $project = IPF_Project::getInstance();
+
+ $models = array();
+ foreach ($project->appList() as $app)
+ $models = array_merge($models, IPF_Legacy_ORM_App::appModelList($app));
+
+ IPF_ORM::createTablesFromModels($models);
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_Form_Model extends IPF_Form
+{
+ public $model = null;
+ public $user_fields = null;
+
+ function initFields($extra=array())
+ {
+ if (isset($extra['model']))
+ $this->model = $extra['model'];
+ else
+ throw new IPF_Exception_Form(__('Unknown model for form'));
+
+ if (isset($extra['user_fields']))
+ $this->user_fields = $extra['user_fields'];
+
+ $exclude = \PFF\Arr::get($extra, 'exclude', array());
+ $fields = self::suggestFields($this->model->getTable(), $this->fields(), $exclude, $this);
+ foreach ($fields as $field) {
+ list($n, $f) = $field;
+ $this->fields[$n] = $f;
+ }
+ }
+
+ public static function suggestFields($table, $fields=null, $exclude=array(), $form=null)
+ {
+ $result = array();
+
+ $db_columns = $table->getColumns();
+ $db_relations = $table->getRelations();
+
+ if ($fields === null) {
+ foreach ($db_columns as $name => $col) {
+ if (array_search($name, $exclude) !== false)
+ continue;
+ if (isset($col['exclude']) && $col['exclude'])
+ continue;
+
+ if ($form && method_exists($form, 'add__'.$name.'__field')) {
+ $f = call_user_func(array($form, 'add__'.$name.'__field'));
+ if ($f)
+ $field = array($name, $f);
+ } else {
+ $field = self::createDBField($name, $table, $col);
+ }
+
+ if ($field)
+ $result[] = $field;
+ }
+
+ foreach ($db_relations as $name => $relation) {
+ if (array_search($name, $exclude) !== false)
+ continue;
+ if (isset($relation['exclude']) && $relation['exclude'])
+ continue;
+
+ if ($form && method_exists($form, 'add__'.$name.'__field')) {
+ $f = call_user_func(array($form, 'add__'.$name.'__field'));
+ if ($f)
+ $field = array($name, $f);
+ } else {
+ $field = self::createDBRelation($name, $relation, $db_columns);
+ }
+
+ if ($field)
+ $result[] = $field;
+ }
+ } else {
+ foreach ($fields as $uname) {
+ $field = null;
+ if ($form && method_exists($form, 'add__'.$uname.'__field')) {
+ $f = call_user_func(array($form, 'add__'.$uname.'__field'));
+ if ($f)
+ $field = array($uname, $f);
+ } elseif (array_key_exists($uname, $db_columns)) {
+ $field = self::createDBField($uname, $table, $db_columns[$uname]);
+ } elseif (array_key_exists($uname, $db_relations)) {
+ $field = self::createDBRelation($uname, $db_relations[$uname], $db_columns);
+ } else {
+ throw new IPF_Exception_Form(sprintf(__("Model '%s' has no column '%s'."), $table->getComponentName(), $uname));
+ }
+ if ($field)
+ $result[] = $field;
+ }
+ }
+ return $result;
+ }
+
+ public static function createDBField($name, $table, $col)
+ {
+ if ($name == $table->getIdentifier())
+ return null;
+
+ $required = isset($col['notblank']) && $col['notblank'];
+ $label = isset($col['verbose']) ? $col['verbose'] : IPF_Utils::humanTitle($name);
+
+ $params = array(
+ 'required' => $required,
+ 'label' => $label,
+ 'help_text' => '',
+ );
+
+ switch ($col['type']) {
+ case 'string':
+ if (isset($col['length']))
+ $params['max_length'] = (int)($col['length']);
+
+ if (isset($col['uploadTo']))
+ $params['uploadTo'] = $col['uploadTo'];
+
+ if (isset($col['email']) && $col['email']) { $form_field = new IPF_Form_Field_Email($params); }
+ elseif (isset($col['file']) && $col['file']) { $form_field = new IPF_Form_Field_File($params); }
+ elseif (isset($col['image']) && $col['image']) { $form_field = new IPF_Form_Field_Image($params); }
+ elseif (isset($col['html']) && $col['html']) { $form_field = new IPF_Form_Field_Html($params); }
+ else { $form_field = new IPF_Form_Field_Varchar($params); }
+
+ break;
+ case 'boolean':
+ $form_field = new IPF_Form_Field_Boolean($params);
+ break;
+ case 'integer':
+ $params['widget_attrs'] = array('style' => 'width:140px;');
+ $form_field = new IPF_Form_Field_Integer($params);
+ break;
+ case 'double':
+ case 'decimal':
+ $params['widget_attrs'] = array('style' => 'width:140px;');
+ $form_field = new IPF_Form_Field_Float($params);
+ break;
+ case 'date':
+ $format = IPF::get('date_format');
+ if ($format)
+ $params['widget_attrs'] = array('format' => $format);
+ $form_field = new IPF_Form_Field_Date($params);
+ break;
+ case 'datetime':
+ case 'timestamp':
+ $format = IPF::get('datetime_format');
+ if ($format)
+ $params['widget_attrs'] = array('format' => $format);
+ $form_field = new IPF_Form_Field_Datetime($params);
+ break;
+ default:
+ throw new IPF_Exception_Form(__('Unsupported column type \''.$col['type'].'\'.'));
+ }
+
+ if ($form_field !== null)
+ return array($name, $form_field);
+ else
+ return null;
+ }
+
+ public static function createDBRelation($name, $relation, $db_columns)
+ {
+ $rt = $relation->getType();
+ if ($rt !== IPF_ORM_Relation::ONE_AGGREGATE && $rt !== IPF_ORM_Relation::MANY_AGGREGATE)
+ return null;
+
+ $lfn = $relation->getLocalFieldName();
+ if (isset($db_columns[$lfn]))
+ $col = $db_columns[$lfn];
+ else
+ $col = array();
+
+ $table = IPF_ORM::getTable($relation->getClass());
+
+ $params = array(
+ 'required' => isset($col['notblank']),
+ 'label' => isset($col['verbose']) ? $col['verbose'] : IPF_Utils::humanTitle($name),
+ 'help_text' => '',
+ 'table' => $table,
+ 'model' => $relation->getClass(),
+ );
+
+ if ($rt === IPF_ORM_Relation::ONE_AGGREGATE) {
+ $choices = array('--------' => '');
+ $pk = $table->getIdentifier();
+ foreach ($table->findAll() as $o) {
+ $choices[$o->__toString()] = $o->$pk;
+ }
+
+ $params['choices'] = $choices;
+ return array($name.'_id', new IPF_Form_Field_Choice($params));
+ } elseif ($rt === IPF_ORM_Relation::MANY_AGGREGATE) {
+ $choices = array();
+ $pk = $table->getIdentifier();
+ foreach ($table->findAll() as $o) {
+ $choices[$o->__toString()] = $o->$pk;
+ }
+
+ $params['choices'] = $choices;
+ return array($name, new IPF_Form_Field_MultipleChoice($params));
+ } else {
+ return null;
+ }
+ }
+
+ function fields()
+ {
+ return $this->user_fields;
+ }
+
+ function save($commit=true)
+ {
+ if (!$this->isValid())
+ throw new IPF_Exception_Form(__('Cannot save the model from an invalid form.'));
+
+ $this->model->SetFromFormData($this->cleaned_data);
+ $this->model->save();
+ $rels = $this->model->getTable()->getRelations();
+ foreach ($rels as $rname => $rel) {
+ if (isset($this->cleaned_data[$rname])) {
+ $this->model->unlink($rel->getAlias());
+ if (is_array($this->cleaned_data[$rname])) {
+ $this->model->link($rel->getAlias(),$this->cleaned_data[$rname]);
+ }
+ }
+ }
+ return $this->model;
+ }
+}
+
--- /dev/null
+<?php
+
+final class IPF_ORM
+{
+ const ERR = -1;
+ const ERR_SYNTAX = -2;
+ const ERR_CONSTRAINT = -3;
+ const ERR_NOT_FOUND = -4;
+ const ERR_ALREADY_EXISTS = -5;
+ const ERR_UNSUPPORTED = -6;
+ const ERR_MISMATCH = -7;
+ const ERR_INVALID = -8;
+ const ERR_NOT_CAPABLE = -9;
+ const ERR_TRUNCATED = -10;
+ const ERR_INVALID_NUMBER = -11;
+ const ERR_INVALID_DATE = -12;
+ const ERR_DIVZERO = -13;
+ const ERR_NODBSELECTED = -14;
+ const ERR_CANNOT_CREATE = -15;
+ const ERR_CANNOT_DELETE = -16;
+ const ERR_CANNOT_DROP = -17;
+ const ERR_NOSUCHTABLE = -18;
+ const ERR_NOSUCHFIELD = -19;
+ const ERR_NEED_MORE_DATA = -20;
+ const ERR_NOT_LOCKED = -21;
+ const ERR_VALUE_COUNT_ON_ROW = -22;
+ const ERR_INVALID_DSN = -23;
+ const ERR_CONNECT_FAILED = -24;
+ const ERR_EXTENSION_NOT_FOUND = -25;
+ const ERR_NOSUCHDB = -26;
+ const ERR_ACCESS_VIOLATION = -27;
+ const ERR_CANNOT_REPLACE = -28;
+ const ERR_CONSTRAINT_NOT_NULL = -29;
+ const ERR_DEADLOCK = -30;
+ const ERR_CANNOT_ALTER = -31;
+ const ERR_MANAGER = -32;
+ const ERR_MANAGER_PARSE = -33;
+ const ERR_LOADMODULE = -34;
+ const ERR_INSUFFICIENT_DATA = -35;
+ const ERR_CLASS_NAME = -36;
+
+ const CASE_LOWER = 2;
+ const CASE_NATURAL = 0;
+ const CASE_UPPER = 1;
+ const CURSOR_FWDONLY = 0;
+ const CURSOR_SCROLL = 1;
+ const ERRMODE_EXCEPTION = 2;
+ const ERRMODE_SILENT = 0;
+ const ERRMODE_WARNING = 1;
+ const FETCH_ASSOC = 2;
+ const FETCH_BOTH = 4;
+ const FETCH_BOUND = 6;
+ const FETCH_CLASS = 8;
+ const FETCH_CLASSTYPE = 262144;
+ const FETCH_COLUMN = 7;
+ const FETCH_FUNC = 10;
+ const FETCH_GROUP = 65536;
+ const FETCH_INTO = 9;
+ const FETCH_LAZY = 1;
+ const FETCH_NAMED = 11;
+ const FETCH_NUM = 3;
+ const FETCH_OBJ = 5;
+ const FETCH_ORI_ABS = 4;
+ const FETCH_ORI_FIRST = 2;
+ const FETCH_ORI_LAST = 3;
+ const FETCH_ORI_NEXT = 0;
+ const FETCH_ORI_PRIOR = 1;
+ const FETCH_ORI_REL = 5;
+ const FETCH_SERIALIZE = 524288;
+ const FETCH_UNIQUE = 196608;
+ const NULL_EMPTY_STRING = 1;
+ const NULL_NATURAL = 0;
+ const NULL_TO_STRING = NULL;
+ const PARAM_BOOL = 5;
+ const PARAM_INPUT_OUTPUT = -2147483648;
+ const PARAM_INT = 1;
+ const PARAM_LOB = 3;
+ const PARAM_NULL = 0;
+ const PARAM_STMT = 4;
+ const PARAM_STR = 2;
+
+ const ATTR_AUTOCOMMIT = 0;
+ const ATTR_PREFETCH = 1;
+ const ATTR_TIMEOUT = 2;
+ const ATTR_ERRMODE = 3;
+ const ATTR_SERVER_VERSION = 4;
+ const ATTR_CLIENT_VERSION = 5;
+ const ATTR_SERVER_INFO = 6;
+ const ATTR_CONNECTION_STATUS = 7;
+ const ATTR_CASE = 8;
+ const ATTR_CURSOR_NAME = 9;
+ const ATTR_CURSOR = 10;
+ const ATTR_ORACLE_NULLS = 11;
+ const ATTR_PERSISTENT = 12;
+ const ATTR_STATEMENT_CLASS = 13;
+ const ATTR_FETCH_TABLE_NAMES = 14;
+ const ATTR_FETCH_CATALOG_NAMES = 15;
+ const ATTR_STRINGIFY_FETCHES = 17;
+ const ATTR_MAX_COLUMN_LEN = 18;
+
+ const ATTR_FIELD_CASE = 102;
+ const ATTR_CMPNAME_FORMAT = 118;
+ const ATTR_DBNAME_FORMAT = 117;
+ const ATTR_TBLCLASS_FORMAT = 119;
+ const ATTR_EXPORT = 140;
+ const ATTR_DECIMAL_PLACES = 141;
+
+ const ATTR_PORTABILITY = 106;
+ const ATTR_COLL_KEY = 108;
+ const ATTR_DEFAULT_TABLE_TYPE = 112;
+ const ATTR_DEF_TEXT_LENGTH = 113;
+ const ATTR_DEF_VARCHAR_LENGTH = 114;
+ const ATTR_DEF_TABLESPACE = 115;
+ const ATTR_EMULATE_DATABASE = 116;
+ const ATTR_USE_NATIVE_ENUM = 117;
+
+ //const ATTR_FETCHMODE = 118;
+ const ATTR_NAME_PREFIX = 121;
+ const ATTR_CREATE_TABLES = 122;
+ const ATTR_COLL_LIMIT = 123;
+
+ const ATTR_CACHE = 150;
+ const ATTR_RESULT_CACHE = 150;
+ const ATTR_CACHE_LIFESPAN = 151;
+ const ATTR_RESULT_CACHE_LIFESPAN = 151;
+ const ATTR_LOAD_REFERENCES = 153;
+ const ATTR_DEFAULT_PARAM_NAMESPACE = 156;
+ const ATTR_QUERY_CACHE = 157;
+ const ATTR_QUERY_CACHE_LIFESPAN = 158;
+ const ATTR_MODEL_LOADING = 161;
+ const ATTR_RECURSIVE_MERGE_FIXTURES = 162;
+ const ATTR_SINGULARIZE_IMPORT = 163;
+
+ const FETCH_IMMEDIATE = 0;
+ const FETCH_BATCH = 1;
+ const FETCH_OFFSET = 3;
+ const FETCH_LAZY_OFFSET = 4;
+ const FETCH_VHOLDER = 1;
+ const FETCH_RECORD = 2;
+ const FETCH_ARRAY = 3;
+
+ const PORTABILITY_NONE = 0;
+ const PORTABILITY_FIX_CASE = 1;
+ const PORTABILITY_RTRIM = 2;
+ const PORTABILITY_DELETE_COUNT = 4;
+ const PORTABILITY_EMPTY_TO_NULL = 8;
+ const PORTABILITY_FIX_ASSOC_FIELD_NAMES = 16;
+ const PORTABILITY_EXPR = 32;
+ const PORTABILITY_ALL = 63;
+
+ const LOCK_OPTIMISTIC = 0;
+ const LOCK_PESSIMISTIC = 1;
+
+ const EXPORT_NONE = 0;
+ const EXPORT_TABLES = 1;
+ const EXPORT_CONSTRAINTS = 2;
+ const EXPORT_ALL = 7;
+
+ const HYDRATE_RECORD = 2;
+ const HYDRATE_ARRAY = 3;
+
+ const HYDRATE_NONE = 4;
+ const IDENTIFIER_AUTOINC = 1;
+ const IDENTIFIER_NATURAL = 3;
+ const IDENTIFIER_COMPOSITE = 4;
+ const MODEL_LOADING_AGGRESSIVE = 1;
+ const MODEL_LOADING_CONSERVATIVE= 2;
+
+ const BASE_CLASSES_DIRECTORY = '_generated';
+
+ private function __construct() {}
+
+ public static function getTable($componentName)
+ {
+ return IPF_ORM_Manager::connection()->getTable($componentName);
+ }
+
+ public static function generateModelsFromYaml($directory, $extraAllwedReferences=array())
+ {
+ $files = array();
+ foreach ((array)$directory as $dir)
+ $files[] = $dir . '/models.yml';
+
+ // load schema
+ $import = new IPF_ORM_Import_Schema;
+ $definitions = $import->importSchema($files, $extraAllwedReferences);
+
+ // build
+ $models = array();
+ foreach ($definitions as $name => $definition) {
+ print " $name\n";
+ $targetPath = substr($definition['input_file'], 0, -4);
+
+ $builder = new IPF_ORM_Import_Builder;
+ $builder->buildRecord($definition, $targetPath);
+
+ $models[] = $name;
+ }
+
+ return $models;
+ }
+
+ public static function createTablesFromModels($models)
+ {
+ IPF_ORM_Manager::connection()->export->exportClasses($models);
+ }
+
+ public static function generateSqlFromModels($app)
+ {
+ $sql = IPF_ORM_Manager::connection()->export->exportSortedClassesSql(IPF_Legacy_ORM_App::appModelList($app), false);
+
+ $build = '';
+ foreach ($sql as $query) {
+ $build .= $query.";\n\n";
+ }
+ return $build;
+ }
+
+ public static function dump($var, $output = true, $indent = "")
+ {
+ $ret = array();
+ switch (gettype($var)) {
+ case 'array':
+ $ret[] = 'Array(';
+ $indent .= " ";
+ foreach ($var as $k => $v) {
+
+ $ret[] = $indent . $k . ' : ' . self::dump($v, false, $indent);
+ }
+ $indent = substr($indent,0, -4);
+ $ret[] = $indent . ")";
+ break;
+ case 'object':
+ $ret[] = 'Object(' . get_class($var) . ')';
+ break;
+ default:
+ $ret[] = var_export($var, true);
+ }
+
+ if ($output) {
+ print implode("\n", $ret);
+ }
+
+ return implode("\n", $ret);
+ }
+
+ public static function GetObjectOr404($object, $id)
+ {
+ $obj = IPF_ORM::getTable($object)->findOneById($id);
+ if ($obj)
+ return $obj;
+ throw new IPF_HTTP_Error404();
+ }
+}
+
--- /dev/null
+<?php
+
+abstract class IPF_ORM_Access implements ArrayAccess
+{
+ public function setArray(array $array)
+ {
+ foreach ($array as $k => $v) {
+ $this->set($k, $v);
+ }
+
+ return $this;
+ }
+
+ public function __set($name, $value)
+ {
+ $this->set($name, $value);
+ }
+
+ public function __get($name)
+ {
+ return $this->get($name);
+ }
+
+ public function __isset($name)
+ {
+ return $this->contains($name);
+ }
+
+ public function __unset($name)
+ {
+ return $this->remove($name);
+ }
+
+ public function offsetExists($offset)
+ {
+ return $this->contains($offset);
+ }
+
+ public function offsetGet($offset)
+ {
+ return $this->get($offset);
+ }
+
+ public function offsetSet($offset, $value)
+ {
+ if ( ! isset($offset)) {
+ $this->add($value);
+ } else {
+ $this->set($offset, $value);
+ }
+ }
+
+ public function offsetUnset($offset)
+ {
+ return $this->remove($offset);
+ }
+
+ public function remove($offset)
+ {
+ throw new IPF_ORM_Exception('Remove is not supported for ' . get_class($this));
+ }
+
+ public function get($offset)
+ {
+ throw new IPF_ORM_Exception('Get is not supported for ' . get_class($this));
+ }
+
+ public function set($offset, $value)
+ {
+ throw new IPF_ORM_Exception('Set is not supported for ' . get_class($this));
+ }
+
+ public function contains($offset)
+ {
+ throw new IPF_ORM_Exception('Contains is not supported for ' . get_class($this));
+ }
+
+ public function add($value)
+ {
+ throw new IPF_ORM_Exception('Add is not supported for ' . get_class($this));
+ }
+}
--- /dev/null
+<?php
+
+class IPF_ORM_Adapter
+{
+ const ATTR_AUTOCOMMIT = 0;
+ const ATTR_CASE = 8;
+ const ATTR_CLIENT_VERSION = 5;
+ const ATTR_CONNECTION_STATUS = 7;
+ const ATTR_CURSOR = 10;
+ const ATTR_CURSOR_NAME = 9;
+ const ATTR_ERRMODE = 3;
+ const ATTR_FETCH_CATALOG_NAMES = 15;
+ const ATTR_FETCH_TABLE_NAMES = 14;
+ const ATTR_MAX_COLUMN_LEN = 18;
+ const ATTR_ORACLE_NULLS = 11;
+ const ATTR_PERSISTENT = 12;
+ const ATTR_PREFETCH = 1;
+ const ATTR_SERVER_INFO = 6;
+ const ATTR_SERVER_VERSION = 4;
+ const ATTR_STATEMENT_CLASS = 13;
+ const ATTR_STRINGIFY_FETCHES = 17;
+ const ATTR_TIMEOUT = 2;
+ const CASE_LOWER = 2;
+ const CASE_NATURAL = 0;
+ const CASE_UPPER = 1;
+ const CURSOR_FWDONLY = 0;
+ const CURSOR_SCROLL = 1;
+ const ERR_ALREADY_EXISTS = NULL;
+ const ERR_CANT_MAP = NULL;
+ const ERR_CONSTRAINT = NULL;
+ const ERR_DISCONNECTED = NULL;
+ const ERR_MISMATCH = NULL;
+ const ERR_NO_PERM = NULL;
+ const ERR_NONE = '00000';
+ const ERR_NOT_FOUND = NULL;
+ const ERR_NOT_IMPLEMENTED = NULL;
+ const ERR_SYNTAX = NULL;
+ const ERR_TRUNCATED = NULL;
+ const ERRMODE_EXCEPTION = 2;
+ const ERRMODE_SILENT = 0;
+ const ERRMODE_WARNING = 1;
+ const FETCH_ASSOC = 2;
+ const FETCH_BOTH = 4;
+ const FETCH_BOUND = 6;
+ const FETCH_CLASS = 8;
+ const FETCH_CLASSTYPE = 262144;
+ const FETCH_COLUMN = 7;
+ const FETCH_FUNC = 10;
+ const FETCH_GROUP = 65536;
+ const FETCH_INTO = 9;
+ const FETCH_LAZY = 1;
+ const FETCH_NAMED = 11;
+ const FETCH_NUM = 3;
+ const FETCH_OBJ = 5;
+ const FETCH_ORI_ABS = 4;
+ const FETCH_ORI_FIRST = 2;
+ const FETCH_ORI_LAST = 3;
+ const FETCH_ORI_NEXT = 0;
+ const FETCH_ORI_PRIOR = 1;
+ const FETCH_ORI_REL = 5;
+ const FETCH_SERIALIZE = 524288;
+ const FETCH_UNIQUE = 196608;
+ const NULL_EMPTY_STRING = 1;
+ const NULL_NATURAL = 0;
+ const NULL_TO_STRING = NULL;
+ const PARAM_BOOL = 5;
+ const PARAM_INPUT_OUTPUT = -2147483648;
+ const PARAM_INT = 1;
+ const PARAM_LOB = 3;
+ const PARAM_NULL = 0;
+ const PARAM_STMT = 4;
+ const PARAM_STR = 2;
+}
--- /dev/null
+<?php
+
+interface IPF_ORM_Cache_Interface
+{
+ public function fetch($id, $testCacheValidity = true);
+ public function contains($id);
+ public function save($id, $data, $lifeTime = false);
+ public function delete($id);
+}
--- /dev/null
+<?php
+
+class IPF_ORM_Collection extends IPF_ORM_Access implements Countable, IteratorAggregate, Serializable
+{
+ protected $data = array();
+ protected $_table;
+ protected $_snapshot = array();
+ protected $reference;
+ protected $referenceField;
+ protected $relation;
+ protected $keyColumn;
+
+ public function __construct($table, $keyColumn = null)
+ {
+ if ( ! ($table instanceof IPF_ORM_Table)) {
+ $table = IPF_ORM::getTable($table);
+ }
+
+ $this->_table = $table;
+
+ if ($keyColumn === null) {
+ $keyColumn = $table->getBoundQueryPart('indexBy');
+ }
+
+ if ($keyColumn === null) {
+ $keyColumn = $table->getAttribute(IPF_ORM::ATTR_COLL_KEY);
+ }
+
+ if ($keyColumn !== null) {
+ $this->keyColumn = $keyColumn;
+ }
+ }
+
+ public function getTable()
+ {
+ return $this->_table;
+ }
+
+ public function setData(array $data)
+ {
+ $this->data = $data;
+ }
+
+ public function serialize()
+ {
+ $vars = get_object_vars($this);
+
+ unset($vars['reference']);
+ unset($vars['reference_field']);
+ unset($vars['relation']);
+ unset($vars['expandable']);
+ unset($vars['expanded']);
+ unset($vars['generator']);
+
+ $vars['_table'] = $vars['_table']->getComponentName();
+
+ return serialize($vars);
+ }
+
+ public function unserialize($serialized)
+ {
+ $connection = IPF_ORM_Manager::connection();
+
+ $array = unserialize($serialized);
+
+ foreach ($array as $name => $values) {
+ $this->$name = $values;
+ }
+
+ $this->_table = $connection->getTable($this->_table);
+
+ $keyColumn = isset($array['keyColumn']) ? $array['keyColumn'] : null;
+ if ($keyColumn === null) {
+ $keyColumn = $this->_table->getBoundQueryPart('indexBy');
+ }
+
+ if ($keyColumn !== null) {
+ $this->keyColumn = $keyColumn;
+ }
+ }
+
+ public function setKeyColumn($column)
+ {
+ $this->keyColumn = $column;
+
+ return $this;
+ }
+
+ public function getKeyColumn()
+ {
+ return $this->keyColumn;
+ }
+
+ public function getData()
+ {
+ return $this->data;
+ }
+
+ public function getFirst()
+ {
+ return reset($this->data);
+ }
+
+ public function getLast()
+ {
+ return end($this->data);
+ }
+
+ public function end()
+ {
+ return end($this->data);
+ }
+
+ public function key()
+ {
+ return key($this->data);
+ }
+
+ public function setReference(IPF_ORM_Record $record, IPF_ORM_Relation $relation)
+ {
+ $this->reference = $record;
+ $this->relation = $relation;
+
+ if ($relation instanceof IPF_ORM_Relation_ForeignKey ||
+ $relation instanceof IPF_ORM_Relation_LocalKey) {
+
+ $this->referenceField = $relation->getForeignFieldName();
+
+ $value = $record->get($relation->getLocalFieldName());
+
+ foreach ($this->data as $record) {
+ if ($value !== null) {
+ $record->set($this->referenceField, $value, false);
+ } else {
+ $record->set($this->referenceField, $this->reference, false);
+ }
+ }
+ } elseif ($relation instanceof IPF_ORM_Relation_Association) {
+
+ }
+ }
+
+ public function getReference()
+ {
+ return $this->reference;
+ }
+
+ public function remove($key)
+ {
+ $removed = $this->data[$key];
+
+ unset($this->data[$key]);
+ return $removed;
+ }
+
+ public function contains($key)
+ {
+ return isset($this->data[$key]);
+ }
+
+ public function search(IPF_ORM_Record $record)
+ {
+ return array_search($record, $this->data, true);
+ }
+
+ public function get($key)
+ {
+ if ( ! isset($this->data[$key])) {
+ $record = $this->_table->create();
+
+ if (isset($this->referenceField)) {
+ $value = $this->reference->get($this->relation->getLocalFieldName());
+
+ if ($value !== null) {
+ $record->set($this->referenceField, $value, false);
+ } else {
+ $record->set($this->referenceField, $this->reference, false);
+ }
+ }
+ if ($key === null) {
+ $this->data[] = $record;
+ } else {
+ $this->data[$key] = $record;
+ }
+
+ if (isset($this->keyColumn)) {
+ $record->set($this->keyColumn, $key);
+ }
+
+ return $record;
+ }
+
+ return $this->data[$key];
+ }
+
+ public function getPrimaryKeys()
+ {
+ $list = array();
+ $name = $this->_table->getIdentifier();
+
+ foreach ($this->data as $record) {
+ if (is_array($record) && isset($record[$name])) {
+ $list[] = $record[$name];
+ } else {
+ $list[] = $record->getIncremented();
+ }
+ }
+ return $list;
+ }
+
+ public function getKeys()
+ {
+ return array_keys($this->data);
+ }
+
+ public function count()
+ {
+ return count($this->data);
+ }
+
+ public function set($key, $record)
+ {
+ if (isset($this->referenceField)) {
+ $record->set($this->referenceField, $this->reference, false);
+ }
+
+ $this->data[$key] = $record;
+ }
+
+ public function add($record, $key = null)
+ {
+ if (isset($this->referenceField)) {
+ $value = $this->reference->get($this->relation->getLocalFieldName());
+
+ if ($value !== null) {
+ $record->set($this->referenceField, $value, false);
+ } else {
+ $record->set($this->referenceField, $this->reference, false);
+ }
+ }
+ /**
+ * for some weird reason in_array cannot be used here (php bug ?)
+ *
+ * if used it results in fatal error : [ nesting level too deep ]
+ */
+ foreach ($this->data as $val) {
+ if ($val === $record) {
+ return false;
+ }
+ }
+
+ if (isset($key)) {
+ if (isset($this->data[$key])) {
+ return false;
+ }
+ $this->data[$key] = $record;
+ return true;
+ }
+
+ if (isset($this->keyColumn)) {
+ $value = $record->get($this->keyColumn);
+ if ($value === null) {
+ throw new IPF_ORM_Exception("Couldn't create collection index. Record field '".$this->keyColumn."' was null.");
+ }
+ $this->data[$value] = $record;
+ } else {
+ $this->data[] = $record;
+ }
+
+ return true;
+ }
+
+ public function merge(IPF_ORM_Collection $coll)
+ {
+ $localBase = $this->getTable()->getComponentName();
+ $otherBase = $coll->getTable()->getComponentName();
+
+ if ($otherBase != $localBase && !is_subclass_of($otherBase, $localBase) ) {
+ throw new IPF_ORM_Exception("Can't merge collections with incompatible record types");
+ }
+
+ foreach ($coll->getData() as $record) {
+ $this->add($record);
+ }
+
+ return $this;
+ }
+
+ public function loadRelated($name = null)
+ {
+ $list = array();
+ $query = new IPF_ORM_Query($this->_table->getConnection());
+
+ if ( ! isset($name)) {
+ foreach ($this->data as $record) {
+ $value = $record->getIncremented();
+ if ($value !== null) {
+ $list[] = $value;
+ }
+ }
+ $query->from($this->_table->getComponentName());
+ $query->where($this->_table->getComponentName() . '.id IN (' . substr(str_repeat("?, ", count($list)),0,-2) . ')');
+
+ return $query;
+ }
+
+ $rel = $this->_table->getRelation($name);
+
+ if ($rel instanceof IPF_ORM_Relation_LocalKey || $rel instanceof IPF_ORM_Relation_ForeignKey) {
+ foreach ($this->data as $record) {
+ $list[] = $record[$rel->getLocal()];
+ }
+ } else {
+ foreach ($this->data as $record) {
+ $value = $record->getIncremented();
+ if ($value !== null) {
+ $list[] = $value;
+ }
+ }
+ }
+
+ $dql = $rel->getRelationDql(count($list), 'collection');
+
+ $coll = $query->query($dql, $list);
+
+ $this->populateRelated($name, $coll);
+ }
+
+ public function populateRelated($name, IPF_ORM_Collection $coll)
+ {
+ $rel = $this->_table->getRelation($name);
+ $table = $rel->getTable();
+ $foreign = $rel->getForeign();
+ $local = $rel->getLocal();
+
+ if ($rel instanceof IPF_ORM_Relation_LocalKey) {
+ foreach ($this->data as $key => $record) {
+ foreach ($coll as $k => $related) {
+ if ($related[$foreign] == $record[$local]) {
+ $this->data[$key]->setRelated($name, $related);
+ }
+ }
+ }
+ } elseif ($rel instanceof IPF_ORM_Relation_ForeignKey) {
+ foreach ($this->data as $key => $record) {
+ if ( ! $record->exists()) {
+ continue;
+ }
+ $sub = new IPF_ORM_Collection($table);
+
+ foreach ($coll as $k => $related) {
+ if ($related[$foreign] == $record[$local]) {
+ $sub->add($related);
+ $coll->remove($k);
+ }
+ }
+
+ $this->data[$key]->setRelated($name, $sub);
+ }
+ } elseif ($rel instanceof IPF_ORM_Relation_Association) {
+ $identifier = $this->_table->getIdentifier();
+ $asf = $rel->getAssociationFactory();
+ $name = $table->getComponentName();
+
+ foreach ($this->data as $key => $record) {
+ if ( ! $record->exists()) {
+ continue;
+ }
+ $sub = new IPF_ORM_Collection($table);
+ foreach ($coll as $k => $related) {
+ if ($related->get($local) == $record[$identifier]) {
+ $sub->add($related->get($name));
+ }
+ }
+ $this->data[$key]->setRelated($name, $sub);
+
+ }
+ }
+ }
+
+ public function getNormalIterator()
+ {
+ return new IPF_ORM_Collection_Iterator_Normal($this);
+ }
+
+ public function takeSnapshot()
+ {
+ $this->_snapshot = $this->data;
+
+ return $this;
+ }
+
+ public function getSnapshot()
+ {
+ return $this->_snapshot;
+ }
+
+ public function processDiff()
+ {
+ foreach (array_udiff($this->_snapshot, $this->data, array($this, "compareRecords")) as $record) {
+ $record->delete();
+ }
+
+ return $this;
+ }
+
+ public function toArray($deep = false, $prefixKey = false)
+ {
+ $data = array();
+ foreach ($this as $key => $record) {
+
+ $key = $prefixKey ? get_class($record) . '_' .$key:$key;
+
+ $data[$key] = $record->toArray($deep, $prefixKey);
+ }
+
+ return $data;
+ }
+
+ public function fromArray($array, $deep = true)
+ {
+ $data = array();
+ foreach ($array as $rowKey => $row) {
+ $this[$rowKey]->fromArray($row, $deep);
+ }
+ }
+
+ public function synchronizeWithArray(array $array)
+ {
+ foreach ($this as $key => $record) {
+ if (isset($array[$key])) {
+ $record->synchronizeWithArray($array[$key]);
+ unset($array[$key]);
+ } else {
+ // remove records that don't exist in the array
+ $this->remove($key);
+ }
+ }
+ // create new records for each new row in the array
+ foreach ($array as $rowKey => $row) {
+ $this[$rowKey]->fromArray($row);
+ }
+ }
+ public function synchronizeFromArray(array $array)
+ {
+ return $this->synchronizeWithArray($array);
+ }
+
+ public function getDeleteDiff()
+ {
+ return array_udiff($this->_snapshot, $this->data, array($this, 'compareRecords'));
+ }
+
+ public function getInsertDiff()
+ {
+ return array_udiff($this->data, $this->_snapshot, array($this, "compareRecords"));
+ }
+
+ protected function compareRecords($a, $b)
+ {
+ if ($a->getOid() == $b->getOid()) {
+ return 0;
+ }
+
+ return ($a->getOid() > $b->getOid()) ? 1 : -1;
+ }
+
+ public function save(IPF_ORM_Connection $conn = null)
+ {
+ if ($conn == null) {
+ $conn = $this->_table->getConnection();
+ }
+
+ $conn->beginInternalTransaction();
+
+ $conn->transaction->addCollection($this);
+
+ $this->processDiff();
+
+ foreach ($this->getData() as $key => $record) {
+ $record->save($conn);
+ }
+
+ $conn->commit();
+
+ return $this;
+ }
+
+ public function delete(IPF_ORM_Connection $conn = null, $clearColl = true)
+ {
+ if ($conn == null) {
+ $conn = $this->_table->getConnection();
+ }
+
+ $conn->beginInternalTransaction();
+ $conn->transaction->addCollection($this);
+
+ foreach ($this as $key => $record) {
+ $record->delete($conn);
+ }
+
+ $conn->commit();
+
+ if ($clearColl) {
+ $this->clear();
+ }
+
+ return $this;
+ }
+
+ public function clear()
+ {
+ $this->data = array();
+ }
+
+ public function free($deep = false)
+ {
+ foreach ($this->getData() as $key => $record) {
+ if ( ! ($record instanceof IPF_ORM_Null)) {
+ $record->free($deep);
+ }
+ }
+
+ $this->data = array();
+
+ if ($this->reference) {
+ $this->reference->free($deep);
+ $this->reference = null;
+ }
+ }
+
+ public function getIterator()
+ {
+ $data = $this->data;
+ return new ArrayIterator($data);
+ }
+
+ public function __toString()
+ {
+ return IPF_ORM_Utils::getCollectionAsString($this);
+ }
+
+ public function getRelation()
+ {
+ return $this->relation;
+ }
+
+ public function __debugInfo()
+ {
+ $r = array();
+ foreach ($this->data as $item)
+ $r[] = $item;
+ return $r;
+ }
+}
+
--- /dev/null
+<?php
+
+abstract class IPF_ORM_Configurable
+{
+ protected $attributes = array();
+ protected $parent;
+ protected $_params = array();
+
+ public function getAttributeFromString($stringAttributeName)
+ {
+ if (is_string($stringAttributeName)) {
+ $upper = strtoupper($stringAttributeName);
+ $const = 'IPF_ORM::ATTR_' . $upper;
+ if (defined($const)) {
+ return constant($const);
+ } else {
+ throw new IPF_ORM_Exception('Unknown attribute: "' . $stringAttributeName . '"');
+ }
+ } else {
+ return false;
+ }
+ }
+
+ public function getAttributeValueFromString($stringAttributeName, $stringAttributeValueName)
+ {
+ $const = 'IPF_ORM::' . strtoupper($stringAttributeName) . '_' . strtoupper($stringAttributeValueName);
+ if (defined($const)) {
+ return constant($const);
+ } else {
+ throw new IPF_ORM_Exception('Unknown attribute value: "' . $value . '"');
+ }
+ }
+
+ public function setAttribute($attribute, $value)
+ {
+ if (is_string($attribute)) {
+ $stringAttribute = $attribute;
+ $attribute = $this->getAttributeFromString($attribute);
+ $this->_state = $attribute;
+ }
+
+ if (is_string($value) && isset($stringAttribute)) {
+ $value = $this->getAttributeValueFromString($stringAttribute, $value);
+ }
+
+ switch ($attribute) {
+ case IPF_ORM::ATTR_COLL_KEY:
+ if ( ! ($this instanceof IPF_ORM_Table)) {
+ throw new IPF_ORM_Exception("This attribute can only be set at table level.");
+ }
+ if ($value !== null && ! $this->hasField($value)) {
+ throw new IPF_ORM_Exception("Couldn't set collection key attribute. No such field '$value'.");
+ }
+ break;
+ case IPF_ORM::ATTR_CACHE:
+ case IPF_ORM::ATTR_RESULT_CACHE:
+ case IPF_ORM::ATTR_QUERY_CACHE:
+ if ($value !== null) {
+ if ( ! ($value instanceof IPF_ORM_Cache_Interface)) {
+ throw new IPF_ORM_Exception('Cache driver should implement IPF_ORM_Cache_Interface');
+ }
+ }
+ break;
+ case IPF_ORM::ATTR_PORTABILITY:
+ case IPF_ORM::ATTR_DEFAULT_TABLE_TYPE:
+ case IPF_ORM::ATTR_EMULATE_DATABASE:
+ case IPF_ORM::ATTR_USE_NATIVE_ENUM:
+ case IPF_ORM::ATTR_EXPORT:
+ case IPF_ORM::ATTR_DECIMAL_PLACES:
+ case IPF_ORM::ATTR_LOAD_REFERENCES:
+ case IPF_ORM::ATTR_DEFAULT_PARAM_NAMESPACE:
+ case IPF_ORM::ATTR_MODEL_LOADING:
+ case IPF_ORM::ATTR_RESULT_CACHE_LIFESPAN:
+ case IPF_ORM::ATTR_QUERY_CACHE_LIFESPAN:
+ case IPF_ORM::ATTR_RECURSIVE_MERGE_FIXTURES;
+ case IPF_ORM::ATTR_SINGULARIZE_IMPORT;
+
+ break;
+ case IPF_ORM::ATTR_FIELD_CASE:
+ if ($value != 0 && $value != CASE_LOWER && $value != CASE_UPPER)
+ throw new IPF_ORM_Exception('Field case attribute should be either 0, CASE_LOWER or CASE_UPPER constant.');
+ break;
+ default:
+ throw new IPF_ORM_Exception("Unknown attribute.");
+ }
+
+ $this->attributes[$attribute] = $value;
+ }
+
+ public function getParams($namespace = null)
+ {
+ if ($namespace == null) {
+ $namespace = $this->getAttribute(IPF_ORM::ATTR_DEFAULT_PARAM_NAMESPACE);
+ }
+
+ if (!isset($this->_params[$namespace])) {
+ return null;
+ }
+
+ return $this->_params[$namespace];
+ }
+
+ public function getParamNamespaces()
+ {
+ return array_keys($this->_params);
+ }
+
+ public function setParam($name, $value, $namespace = null)
+ {
+ if ($namespace == null) {
+ $namespace = $this->getAttribute(IPF_ORM::ATTR_DEFAULT_PARAM_NAMESPACE);
+ }
+
+ $this->_params[$namespace][$name] = $value;
+
+ return $this;
+ }
+
+ public function getParam($name, $namespace = null)
+ {
+ if ($namespace == null) {
+ $namespace = $this->getAttribute(IPF_ORM::ATTR_DEFAULT_PARAM_NAMESPACE);
+ }
+
+ if (!isset($this->_params[$name])) {
+ if (isset($this->parent)) {
+ return $this->parent->getParam($name, $namespace);
+ }
+ return null;
+ }
+
+ return $this->_params[$namespace][$name];
+ }
+
+ public function getAttribute($attribute)
+ {
+ if (is_string($attribute)) {
+ $upper = strtoupper($attribute);
+
+ $const = 'IPF_ORM::ATTR_' . $upper;
+
+ if (defined($const)) {
+ $attribute = constant($const);
+ $this->_state = $attribute;
+ } else {
+ throw new IPF_ORM_Exception('Unknown attribute: "' . $attribute . '"');
+ }
+ }
+
+ $attribute = (int) $attribute;
+
+ if ($attribute < 0) {
+ throw new IPF_ORM_Exception('Unknown attribute.');
+ }
+
+ if (isset($this->attributes[$attribute])) {
+ return $this->attributes[$attribute];
+ }
+
+ if (isset($this->parent)) {
+ return $this->parent->getAttribute($attribute);
+ }
+ return null;
+ }
+
+ public function getAttributes()
+ {
+ return $this->attributes;
+ }
+
+ public function setParent(IPF_ORM_Configurable $component)
+ {
+ $this->parent = $component;
+ }
+
+ public function getParent()
+ {
+ return $this->parent;
+ }
+}
--- /dev/null
+<?php
+
+abstract class IPF_ORM_Connection extends IPF_ORM_Configurable implements Countable, IteratorAggregate
+{
+ protected $dbh;
+ protected $tables = array();
+ protected $_name = 0;
+ protected $driverName;
+ protected $pendingAttributes = array();
+
+ private $modules = array('transaction' => false,
+ 'expression' => false,
+ 'export' => false,
+ 'unitOfWork' => false,
+ );
+
+ protected $properties = array('sql_comments' => array(array('start' => '--', 'end' => "\n", 'escape' => false),
+ array('start' => '/*', 'end' => '*/', 'escape' => false)),
+ 'identifier_quoting' => '"',
+ 'string_quoting' => array('start' => "'",
+ 'end' => "'",
+ 'escape' => false,
+ 'escape_pattern' => false),
+ 'wildcards' => array('%', '_'),
+ 'varchar_max_length' => 255,
+ );
+
+ protected $options = array();
+ private static $availableDrivers = array(
+ 'Mysql',
+ 'Pgsql',
+ 'Oracle',
+ 'Informix',
+ 'Mssql',
+ 'Sqlite',
+ 'Firebird'
+ );
+ protected $_count = 0;
+
+ public $dbListeners = array();
+
+ public function __construct(IPF_ORM_Manager $manager, $pdo, $user = null, $pass = null)
+ {
+ $this->dbh = $pdo;
+
+ $this->setParent($manager);
+ $this->dbListeners = $manager->dbListeners;
+
+ $this->setAttribute(IPF_ORM::ATTR_CASE, IPF_ORM::CASE_NATURAL);
+ $this->setAttribute(IPF_ORM::ATTR_ERRMODE, IPF_ORM::ERRMODE_EXCEPTION);
+
+ $this->notifyDBListeners('onOpen', $this);
+ }
+
+ public function getOptions()
+ {
+ return $this->options;
+ }
+
+ public function getOption($option)
+ {
+ if (isset($this->options[$option])) {
+ return $this->options[$option];
+ }
+ }
+
+ public function setOption($option, $value)
+ {
+ return $this->options[$option] = $value;
+ }
+
+ public function getAttribute($attribute)
+ {
+ if (is_string($attribute)) {
+ $stringAttribute = $attribute;
+ $attribute = $this->getAttributeFromString($attribute);
+ }
+
+ if ($attribute >= 100) {
+ if ( ! isset($this->attributes[$attribute])) {
+ return parent::getAttribute($attribute);
+ }
+ return $this->attributes[$attribute];
+ }
+
+ try {
+ return $this->dbh->getAttribute($attribute);
+ } catch (Exception $e) {
+ throw new IPF_ORM_Exception('Attribute ' . $attribute . ' not found.');
+ }
+ }
+
+ public static function getAvailableDrivers()
+ {
+ return PDO::getAvailableDrivers();
+ }
+
+ public function setAttribute($attribute, $value)
+ {
+ if (is_string($attribute)) {
+ $attributeString = $attribute;
+ $attribute = parent::getAttributeFromString($attribute);
+ }
+
+ if (is_string($value) && isset($attributeString)) {
+ $value = parent::getAttributeValueFromString($attributeString, $value);
+ }
+
+ if ($attribute >= 100) {
+ parent::setAttribute($attribute, $value);
+ } else {
+ $this->dbh->setAttribute($attribute, $value);
+ }
+
+ return $this;
+ }
+
+ public function getName()
+ {
+ return $this->_name;
+ }
+
+ public function getDriverName()
+ {
+ return $this->driverName;
+ }
+
+ public function __get($name)
+ {
+ if (isset($this->properties[$name])) {
+ return $this->properties[$name];
+ }
+
+ if ( ! isset($this->modules[$name])) {
+ throw new IPF_ORM_Exception('Unknown module / property ' . $name);
+ }
+ if ($this->modules[$name] === false) {
+ switch ($name) {
+ case 'unitOfWork':
+ $this->modules[$name] = new IPF_ORM_Connection_UnitOfWork($this);
+ break;
+ default:
+ $class = 'IPF_ORM_' . ucwords($name) . '_' . $this->getDriverName();
+ $this->modules[$name] = new $class($this);
+ }
+ }
+
+ return $this->modules[$name];
+ }
+
+ public function getManager()
+ {
+ return $this->getParent();
+ }
+
+ public function getDbh()
+ {
+ return $this->dbh;
+ }
+
+ public function incrementQueryCount()
+ {
+ $this->_count++;
+ }
+
+ public function replace(IPF_ORM_Table $table, array $fields, array $keys)
+ {
+ if (empty($keys)) {
+ throw new IPF_ORM_Exception('Not specified which fields are keys');
+ }
+ $identifier = (array) $table->getIdentifier();
+ $condition = array();
+
+ foreach ($fields as $fieldName => $value) {
+ if (in_array($fieldName, $keys)) {
+ if ($value !== null) {
+ $condition[] = $table->getColumnName($fieldName) . ' = ?';
+ $conditionValues[] = $value;
+ }
+ }
+ }
+
+ $affectedRows = 0;
+ if ( ! empty($condition) && ! empty($conditionValues)) {
+ $query = 'DELETE FROM ' . $this->quoteIdentifier($table->getTableName())
+ . ' WHERE ' . implode(' AND ', $condition);
+
+ $affectedRows = $this->exec($query, $conditionValues);
+ }
+
+ $this->insert($table, $fields);
+
+ $affectedRows++;
+
+ return $affectedRows;
+ }
+
+ public function delete(IPF_ORM_Table $table, array $identifier)
+ {
+ $tmp = array();
+
+ foreach (array_keys($identifier) as $id) {
+ $tmp[] = $this->quoteIdentifier($table->getColumnName($id)) . ' = ?';
+ }
+
+ $query = 'DELETE FROM '
+ . $this->quoteIdentifier($table->getTableName())
+ . ' WHERE ' . implode(' AND ', $tmp);
+
+ return $this->exec($query, array_values($identifier));
+ }
+
+ public function update(IPF_ORM_Table $table, array $fields, array $identifier)
+ {
+ if (empty($fields)) {
+ return false;
+ }
+
+ $set = array();
+ foreach ($fields as $fieldName => $value) {
+ if ($value instanceof IPF_ORM_Expression) {
+ $set[] = $this->quoteIdentifier($table->getColumnName($fieldName)) . ' = ' . $value->getSql();
+ unset($fields[$fieldName]);
+ } else {
+ $set[] = $this->quoteIdentifier($table->getColumnName($fieldName)) . ' = ?';
+ }
+ }
+
+ $params = array_merge(array_values($fields), array_values($identifier));
+
+ $sql = 'UPDATE ' . $this->quoteIdentifier($table->getTableName())
+ . ' SET ' . implode(', ', $set)
+ . ' WHERE ' . implode(' = ? AND ', $table->getIdentifierColumnNames())
+ . ' = ?';
+
+ return $this->exec($sql, $params);
+ }
+
+ public function insert(IPF_ORM_Table $table, array $fields)
+ {
+ $tableName = $table->getTableName();
+
+ // column names are specified as array keys
+ $cols = array();
+ // the query VALUES will contain either expresions (eg 'NOW()') or ?
+ $a = array();
+ foreach ($fields as $fieldName => $value) {
+ $cols[] = $this->quoteIdentifier($table->getColumnName($fieldName));
+ if ($value instanceof IPF_ORM_Expression) {
+ $a[] = $value->getSql();
+ unset($fields[$fieldName]);
+ } else {
+ $a[] = '?';
+ }
+ }
+
+ // build the statement
+ $query = 'INSERT INTO ' . $this->quoteIdentifier($tableName)
+ . ' (' . implode(', ', $cols) . ')'
+ . ' VALUES (' . implode(', ', $a) . ')';
+
+ return $this->exec($query, array_values($fields));
+ }
+
+ public abstract function quoteIdentifier($str);
+
+ public function convertBooleans($item)
+ {
+ if (is_array($item)) {
+ foreach ($item as $k => $value) {
+ if (is_bool($value)) {
+ $item[$k] = (int) $value;
+ }
+ }
+ } else {
+ if (is_bool($item)) {
+ $item = (int) $item;
+ }
+ }
+ return $item;
+ }
+
+ public function quote($input, $type=null)
+ {
+ if ($type === null)
+ $type = gettype($input);
+
+ switch ($type) {
+ case 'integer':
+ case 'enum':
+ case 'boolean':
+ case 'double':
+ case 'float':
+ case 'bool':
+ case 'decimal':
+ case 'int':
+ return $input;
+ case 'array':
+ case 'object':
+ $input = serialize($input);
+ case 'date':
+ case 'time':
+ case 'timestamp':
+ case 'string':
+ case 'char':
+ case 'varchar':
+ case 'text':
+ case 'gzip':
+ case 'blob':
+ case 'clob':
+ return $this->getDbh()->quote($input);
+ default:
+ throw new IPF_ORM_Exception('Unsupported type \''.$type.'\'.');
+ }
+ }
+
+ public function escapePattern($text)
+ {
+ $q = $this->string_quoting['escape_pattern'];
+ $text = str_replace($q, $q . $q, $text);
+ foreach ($this->wildcards as $wildcard) {
+ $text = str_replace($wildcard, $q . $wildcard, $text);
+ }
+ return $text;
+ }
+
+ public function setDateFormat($format = null)
+ {
+ }
+
+ public function fetchAll($statement, array $params = array())
+ {
+ return $this->execute($statement, $params)->fetchAll(IPF_ORM::FETCH_ASSOC);
+ }
+
+ public function fetchOne($statement, array $params = array(), $colnum = 0)
+ {
+ return $this->execute($statement, $params)->fetchColumn($colnum);
+ }
+
+ public function fetchRow($statement, array $params = array())
+ {
+ return $this->execute($statement, $params)->fetch(IPF_ORM::FETCH_ASSOC);
+ }
+
+ public function fetchArray($statement, array $params = array())
+ {
+ return $this->execute($statement, $params)->fetch(IPF_ORM::FETCH_NUM);
+ }
+
+ public function fetchColumn($statement, array $params = array(), $colnum = 0)
+ {
+ return $this->execute($statement, $params)->fetchAll(IPF_ORM::FETCH_COLUMN, $colnum);
+ }
+
+ public function fetchAssoc($statement, array $params = array())
+ {
+ return $this->execute($statement, $params)->fetchAll(IPF_ORM::FETCH_ASSOC);
+ }
+
+ public function fetchBoth($statement, array $params = array())
+ {
+ return $this->execute($statement, $params)->fetchAll(IPF_ORM::FETCH_BOTH);
+ }
+
+ public function query($query, array $params = array(), $hydrationMode = null)
+ {
+ $parser = new IPF_ORM_Query($this);
+ $res = $parser->query($query, $params, $hydrationMode);
+ $parser->free();
+
+ return $res;
+ }
+
+ public function prepare($statement)
+ {
+ try {
+ $event = new IPF_ORM_Event($this, IPF_ORM_Event::CONN_PREPARE, $statement);
+
+ $this->notifyDBListeners('prePrepare', $event);
+
+ $stmt = false;
+
+ if ( ! $event->skipOperation) {
+ $stmt = $this->dbh->prepare($statement);
+ }
+
+ $this->notifyDBListeners('postPrepare', $event);
+
+ return new IPF_ORM_Connection_Statement($this, $stmt);
+ } catch (IPF_ORM_Exception_Adapter $e) {
+ } catch (PDOException $e) {
+ }
+
+ $this->rethrowException($e, $this);
+ }
+
+ public function queryOne($query, array $params = array())
+ {
+ $parser = new IPF_ORM_Query($this);
+
+ $coll = $parser->query($query, $params);
+ if ( ! $coll->contains(0)) {
+ return false;
+ }
+ return $coll[0];
+ }
+
+ public function select($query, $limit = 0, $offset = 0)
+ {
+ if ($limit > 0 || $offset > 0) {
+ $query = $this->modifyLimitQuery($query, $limit, $offset);
+ }
+ return $this->execute($query);
+ }
+
+ public function standaloneQuery($query, $params = array())
+ {
+ return $this->execute($query, $params);
+ }
+
+ public function execute($query, array $params = array())
+ {
+ try {
+ if ( ! empty($params)) {
+ $stmt = $this->prepare($query);
+ $stmt->execute($params);
+
+ return $stmt;
+ } else {
+ $event = new IPF_ORM_Event($this, IPF_ORM_Event::CONN_QUERY, $query, $params);
+
+ $this->notifyDBListeners('preQuery', $event);
+
+ if ( ! $event->skipOperation) {
+ $stmt = $this->dbh->query($query);
+ $this->_count++;
+ }
+ $this->notifyDBListeners('postQuery', $event);
+
+ return $stmt;
+ }
+ } catch (IPF_ORM_Exception_Adapter $e) {
+ } catch (PDOException $e) {
+ }
+
+ $this->rethrowException($e, $this);
+ }
+
+ public function exec($query, array $params = array())
+ {
+ try {
+ if ( ! empty($params)) {
+ $stmt = $this->prepare($query);
+ $stmt->execute($params);
+
+ return $stmt->rowCount();
+ } else {
+ $event = new IPF_ORM_Event($this, IPF_ORM_Event::CONN_EXEC, $query, $params);
+
+ $this->notifyDBListeners('preExec', $event);
+ if ( ! $event->skipOperation) {
+ $count = $this->dbh->exec($query);
+
+ $this->_count++;
+ }
+ $this->notifyDBListeners('postExec', $event);
+
+ return $count;
+ }
+ } catch (IPF_ORM_Exception_Adapter $e) {
+ } catch (PDOException $e) { }
+
+ $this->rethrowException($e, $this);
+ }
+
+ public function rethrowException(Exception $e, $invoker)
+ {
+ $event = new IPF_ORM_Event($this, IPF_ORM_Event::CONN_ERROR);
+ $this->notifyDBListeners('onError', $event);
+
+ $name = 'IPF_ORM_Exception_' . $this->driverName;
+ $exc = new $name($e->getMessage(), (int) $e->getCode());
+ if (!is_array($e->errorInfo)) {
+ $e->errorInfo = array(null, null, null, null);
+ }
+ $exc->processErrorInfo($e->errorInfo);
+ throw $exc;
+ }
+
+ public function hasTable($name)
+ {
+ return isset($this->tables[$name]);
+ }
+
+ public function getTable($name)
+ {
+ if (!isset($this->tables[$name]))
+ $this->tables[$name] = new IPF_ORM_Table($name, $this);
+ return $this->tables[$name];
+ }
+
+ public function getTables()
+ {
+ return $this->tables;
+ }
+
+ public function getIterator()
+ {
+ return new ArrayIterator($this->tables);
+ }
+
+ public function count()
+ {
+ return $this->_count;
+ }
+
+ public function addTable(IPF_ORM_Table $table)
+ {
+ $name = $table->getComponentName();
+
+ if (isset($this->tables[$name])) {
+ return false;
+ }
+ $this->tables[$name] = $table;
+ return true;
+ }
+
+ public function create($name)
+ {
+ return $this->getTable($name)->create();
+ }
+
+ public function createQuery()
+ {
+ return new IPF_ORM_Query($this);
+ }
+
+ public function flush()
+ {
+ $this->beginInternalTransaction();
+ $this->unitOfWork->saveAll();
+ $this->commit();
+ }
+
+ public function clear()
+ {
+ foreach ($this->tables as $k => $table) {
+ $table->getRepository()->evictAll();
+ $table->clear();
+ }
+ }
+
+ public function evictTables()
+ {
+ $this->tables = array();
+ $this->exported = array();
+ }
+
+ public function getTransactionLevel()
+ {
+ return $this->transaction->getTransactionLevel();
+ }
+
+ public function errorCode()
+ {
+ return $this->dbh->errorCode();
+ }
+
+ public function errorInfo()
+ {
+ return $this->dbh->errorInfo();
+ }
+
+ public function getCacheDriver()
+ {
+ return $this->getResultCacheDriver();
+ }
+
+ public function getResultCacheDriver()
+ {
+ if ( ! $this->getAttribute(IPF_ORM::ATTR_RESULT_CACHE)) {
+ throw new IPF_ORM_Exception('Result Cache driver not initialized.');
+ }
+
+ return $this->getAttribute(IPF_ORM::ATTR_RESULT_CACHE);
+ }
+
+ public function getQueryCacheDriver()
+ {
+ if ( ! $this->getAttribute(IPF_ORM::ATTR_QUERY_CACHE)) {
+ throw new IPF_ORM_Exception('Query Cache driver not initialized.');
+ }
+
+ return $this->getAttribute(IPF_ORM::ATTR_QUERY_CACHE);
+ }
+
+ public function lastInsertId()
+ {
+ return $this->getDbh()->lastInsertId();
+ }
+
+ public function beginTransaction($savepoint = null)
+ {
+ return $this->transaction->beginTransaction($savepoint);
+ }
+
+ public function beginInternalTransaction($savepoint = null)
+ {
+ return $this->transaction->beginInternalTransaction($savepoint);
+ }
+
+ public function commit($savepoint = null)
+ {
+ return $this->transaction->commit($savepoint);
+ }
+
+ public function rollback($savepoint = null)
+ {
+ $this->transaction->rollback($savepoint);
+ }
+
+ public function modifyLimitQuery($query, $limit = false, $offset = false, $isManip = false)
+ {
+ return $query;
+ }
+
+ public function modifyLimitSubquery(IPF_ORM_Table $rootTable, $query, $limit = false,
+ $offset = false, $isManip = false)
+ {
+ return $this->modifyLimitQuery($query, $limit, $offset, $isManip);
+ }
+
+ public function __toString()
+ {
+ return IPF_ORM_Utils::getConnectionAsString($this);
+ }
+
+ public function notifyDBListeners($method, $event)
+ {
+ foreach ($this->dbListeners as $listener)
+ if (is_callable(array($listener, $method)))
+ $listener->$method($event);
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Connection_Module
+{
+ protected $conn;
+ protected $moduleName;
+
+ public function __construct($conn = null)
+ {
+ if ( ! ($conn instanceof IPF_ORM_Connection)) {
+ $conn = IPF_ORM_Manager::connection();
+ }
+ $this->conn = $conn;
+
+ $e = explode('_', get_class($this));
+
+ $this->moduleName = $e[1];
+ }
+
+ public function getConnection()
+ {
+ return $this->conn;
+ }
+
+ public function getModuleName()
+ {
+ return $this->moduleName;
+ }
+}
--- /dev/null
+<?php
+
+class IPF_ORM_Connection_Mysql extends IPF_ORM_Connection
+{
+ protected $driverName = 'Mysql';
+
+ protected static $keywords = array(
+ 'ADD', 'ALL', 'ALTER', 'ANALYZE', 'AND', 'AS', 'ASC',
+ 'ASENSITIVE', 'BEFORE', 'BETWEEN', 'BIGINT', 'BINARY', 'BLOB',
+ 'BOTH', 'BY', 'BIT', 'CALL', 'CASCADE', 'CASE', 'CHANGE',
+ 'CHAR', 'CHARACTER', 'CHECK', 'COLLATE', 'COLUMN',
+ 'CONDITION', 'CONNECTION', 'CONSTRAINT', 'CONTINUE',
+ 'CONVERT', 'CREATE', 'CROSS', 'CURRENT_DATE', 'CURRENT_TIME',
+ 'CURRENT_TIMESTAMP', 'CURRENT_USER', 'CURSOR', 'DATABASE',
+ 'DATABASES', 'DAY_HOUR', 'DAY_MICROSECOND', 'DAY_MINUTE',
+ 'DAY_SECOND', 'DEC', 'DECIMAL', 'DECLARE', 'DEFAULT',
+ 'DELAYED', 'DELETE', 'DESC', 'DESCRIBE', 'DETERMINISTIC',
+ 'DISTINCT', 'DISTINCTROW', 'DIV', 'DOUBLE', 'DROP', 'DUAL',
+ 'EACH', 'ELSE', 'ELSEIF', 'ENCLOSED', 'ESCAPED', 'EXISTS',
+ 'EXIT', 'EXPLAIN', 'FALSE', 'FETCH', 'FLOAT', 'FLOAT4',
+ 'FLOAT8', 'FOR', 'FORCE', 'FOREIGN', 'FROM', 'FULLTEXT',
+ 'GRANT', 'GROUP', 'HAVING', 'HIGH_PRIORITY',
+ 'HOUR_MICROSECOND', 'HOUR_MINUTE', 'HOUR_SECOND', 'IF',
+ 'IGNORE', 'IN', 'INDEX', 'INFILE', 'INNER', 'INOUT',
+ 'INSENSITIVE', 'INSERT', 'INT', 'INT1', 'INT2', 'INT3',
+ 'INT4', 'INT8', 'INTEGER', 'INTERVAL', 'INTO', 'IS',
+ 'ITERATE', 'JOIN', 'KEY', 'KEYS', 'KILL', 'LEADING', 'LEAVE',
+ 'LEFT', 'LIKE', 'LIMIT', 'LINES', 'LOAD', 'LOCALTIME',
+ 'LOCALTIMESTAMP', 'LOCK', 'LONG', 'LONGBLOB', 'LONGTEXT',
+ 'LOOP', 'LOW_PRIORITY', 'MATCH', 'MEDIUMBLOB', 'MEDIUMINT',
+ 'MEDIUMTEXT', 'MIDDLEINT', 'MINUTE_MICROSECOND',
+ 'MINUTE_SECOND', 'MOD', 'MODIFIES', 'NATURAL', 'NOT',
+ 'NO_WRITE_TO_BINLOG', 'NULL', 'NUMERIC', 'ON', 'OPTIMIZE',
+ 'OPTION', 'OPTIONALLY', 'OR', 'ORDER', 'OUT', 'OUTER',
+ 'OUTFILE', 'PRECISION', 'PRIMARY', 'PROCEDURE', 'PURGE',
+ 'RAID0', 'READ', 'READS', 'REAL', 'REFERENCES', 'REGEXP',
+ 'RELEASE', 'RENAME', 'REPEAT', 'REPLACE', 'REQUIRE',
+ 'RESTRICT', 'RETURN', 'REVOKE', 'RIGHT', 'RLIKE', 'SCHEMA',
+ 'SCHEMAS', 'SECOND_MICROSECOND', 'SELECT', 'SENSITIVE',
+ 'SEPARATOR', 'SET', 'SHOW', 'SMALLINT', 'SONAME', 'SPATIAL',
+ 'SPECIFIC', 'SQL', 'SQLEXCEPTION', 'SQLSTATE', 'SQLWARNING',
+ 'SQL_BIG_RESULT', 'SQL_CALC_FOUND_ROWS', 'SQL_SMALL_RESULT',
+ 'SSL', 'STARTING', 'STRAIGHT_JOIN', 'TABLE', 'TERMINATED',
+ 'THEN', 'TINYBLOB', 'TINYINT', 'TINYTEXT', 'TO', 'TRAILING',
+ 'TRIGGER', 'TRUE', 'UNDO', 'UNION', 'UNIQUE', 'UNLOCK',
+ 'UNSIGNED', 'UPDATE', 'USAGE', 'USE', 'USING', 'UTC_DATE',
+ 'UTC_TIME', 'UTC_TIMESTAMP', 'VALUES', 'VARBINARY', 'VARCHAR',
+ 'VARCHARACTER', 'VARYING', 'WHEN', 'WHERE', 'WHILE', 'WITH',
+ 'WRITE', 'X509', 'XOR', 'YEAR_MONTH', 'ZEROFILL'
+ );
+
+ public function __construct(IPF_ORM_Manager $manager, $adapter)
+ {
+ $this->attributes[IPF_ORM::ATTR_DEFAULT_TABLE_TYPE] = 'INNODB';
+
+ $this->properties['string_quoting'] = array('start' => "'",
+ 'end' => "'",
+ 'escape' => '\\',
+ 'escape_pattern' => '\\');
+
+ $this->properties['identifier_quoting'] = '`';
+
+ $this->properties['sql_comments'] = array(
+ array('start' => '-- ', 'end' => "\n", 'escape' => false),
+ array('start' => '#', 'end' => "\n", 'escape' => false),
+ array('start' => '/*', 'end' => '*/', 'escape' => false),
+ );
+
+ $this->properties['varchar_max_length'] = 255;
+
+ parent::__construct($manager, $adapter);
+ }
+
+ public function quoteIdentifier($str)
+ {
+ $quote = $this->identifier_quoting;
+ $q = array();
+ foreach (explode('.', $str) as $s) {
+ if (in_array(strtoupper($s), self::$keywords))
+ $q[] = $quote . str_replace($quote, $quote . $quote, $s) . $quote;
+ else
+ $q[] = $s;
+ }
+ return implode('.', $q);
+ }
+
+ public function getDatabaseName()
+ {
+ return $this->fetchOne('SELECT DATABASE()');
+ }
+
+ public function replace(IPF_ORM_Table $table, array $fields, array $keys)
+ {
+ if (empty($keys)) {
+ throw new IPF_ORM_Exception('Not specified which fields are keys');
+ }
+ $columns = array();
+ $values = array();
+ $params = array();
+ foreach ($fields as $fieldName => $value) {
+ $columns[] = $table->getColumnName($fieldName);
+ $values[] = '?';
+ $params[] = $value;
+ }
+ $query = 'REPLACE INTO ' . $table->getTableName() . ' (' . implode(',', $columns) . ') VALUES (' . implode(',', $values) . ')';
+ return $this->exec($query, $params);
+ }
+
+ public function modifyLimitQuery($query, $limit = false,$offset = false,$isManip=false)
+ {
+ $limit = (int) $limit;
+ $offset = (int) $offset;
+
+ if ($limit && $offset) {
+ $query .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
+ } elseif ($limit && ! $offset) {
+ $query .= ' LIMIT ' . $limit;
+ } elseif ( ! $limit && $offset) {
+ $query .= ' LIMIT 999999999999 OFFSET ' . $offset;
+ }
+ return $query;
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Connection_Statement
+{
+ protected $_conn;
+ protected $_stmt;
+ public function __construct(IPF_ORM_Connection $conn, $stmt)
+ {
+ $this->_conn = $conn;
+ $this->_stmt = $stmt;
+
+ if ($stmt === false) {
+ throw new IPF_ORM_Exception('Unknown statement object given.');
+ }
+ }
+
+ public function getConnection()
+ {
+ return $this->_conn;
+ }
+ public function getStatement()
+ {
+ return $this->_stmt;
+ }
+ public function getQuery()
+ {
+ return $this->_stmt->queryString;
+ }
+
+ public function bindColumn($column, $param, $type = null)
+ {
+ if ($type === null) {
+ return $this->_stmt->bindColumn($column, $param);
+ } else {
+ return $this->_stmt->bindColumn($column, $param, $type);
+ }
+ }
+
+ public function bindValue($param, $value, $type = null)
+ {
+ if ($type === null) {
+ return $this->_stmt->bindValue($param, $value);
+ } else {
+ return $this->_stmt->bindValue($param, $value, $type);
+ }
+ }
+
+ public function bindParam($column, &$variable, $type = null, $length = null, $driverOptions = array())
+ {
+ if ($type === null) {
+ return $this->_stmt->bindParam($column, $variable);
+ } else {
+ return $this->_stmt->bindParam($column, $variable, $type, $length, $driverOptions);
+ }
+ }
+
+ public function closeCursor()
+ {
+ return $this->_stmt->closeCursor();
+ }
+
+ public function columnCount()
+ {
+ return $this->_stmt->columnCount();
+ }
+
+ public function errorCode()
+ {
+ return $this->_stmt->errorCode();
+ }
+
+ public function errorInfo()
+ {
+ return $this->_stmt->errorInfo();
+ }
+
+ public function execute($params = null)
+ {
+ try {
+ $event = new IPF_ORM_Event($this, IPF_ORM_Event::STMT_EXECUTE, $this->getQuery(), $params);
+ $this->_conn->notifyDBListeners('preStmtExecute', $event);
+
+ $result = true;
+ if ( ! $event->skipOperation) {
+ $result = $this->_stmt->execute($params);
+ $this->_conn->incrementQueryCount();
+ }
+
+ $this->_conn->notifyDBListeners('postStmtExecute', $event);
+
+ return $result;
+ } catch (PDOException $e) {
+ } catch (IPF_ORM_Exception_Adapter $e) {
+ }
+
+ $this->_conn->rethrowException($e, $this);
+
+ return false;
+ }
+
+ public function fetch($fetchMode = IPF_ORM::FETCH_BOTH,
+ $cursorOrientation = IPF_ORM::FETCH_ORI_NEXT,
+ $cursorOffset = null)
+ {
+ $event = new IPF_ORM_Event($this, IPF_ORM_Event::STMT_FETCH, $this->getQuery());
+
+ $event->fetchMode = $fetchMode;
+ $event->cursorOrientation = $cursorOrientation;
+ $event->cursorOffset = $cursorOffset;
+
+ $data = $this->_conn->notifyDBListeners('preFetch', $event);
+
+ if ( ! $event->skipOperation) {
+ $data = $this->_stmt->fetch($fetchMode, $cursorOrientation, $cursorOffset);
+ }
+
+ $this->_conn->notifyDBListeners('postFetch', $event);
+
+ return $data;
+ }
+
+ public function fetchAll($fetchMode = IPF_ORM::FETCH_BOTH,
+ $columnIndex = null)
+ {
+ $event = new IPF_ORM_Event($this, IPF_ORM_Event::STMT_FETCHALL, $this->getQuery());
+ $event->fetchMode = $fetchMode;
+ $event->columnIndex = $columnIndex;
+
+ $this->_conn->notifyDBListeners('preFetchAll', $event);
+
+ if ( ! $event->skipOperation) {
+ if ($columnIndex !== null) {
+ $data = $this->_stmt->fetchAll($fetchMode, $columnIndex);
+ } else {
+ $data = $this->_stmt->fetchAll($fetchMode);
+ }
+
+ $event->data = $data;
+ }
+
+ $this->_conn->notifyDBListeners('postFetchAll', $event);
+
+ return $data;
+ }
+
+ public function fetchColumn($columnIndex = 0)
+ {
+ return $this->_stmt->fetchColumn($columnIndex);
+ }
+
+ public function fetchObject($className = 'stdClass', $args = array())
+ {
+ return $this->_stmt->fetchObject($className, $args);
+ }
+
+ public function getAttribute($attribute)
+ {
+ return $this->_stmt->getAttribute($attribute);
+ }
+
+ public function getColumnMeta($column)
+ {
+ return $this->_stmt->getColumnMeta($column);
+ }
+
+ public function nextRowset()
+ {
+ return $this->_stmt->nextRowset();
+ }
+
+ public function rowCount()
+ {
+ return $this->_stmt->rowCount();
+ }
+
+ public function setAttribute($attribute, $value)
+ {
+ return $this->_stmt->setAttribute($attribute, $value);
+ }
+
+ public function setFetchMode($mode, $arg1 = null, $arg2 = null)
+ {
+ return $this->_stmt->setFetchMode($mode, $arg1, $arg2);
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Connection_UnitOfWork extends IPF_ORM_Connection_Module
+{
+ public function saveGraph(IPF_ORM_Record $record)
+ {
+ $conn = $this->getConnection();
+
+ $state = $record->state();
+ if ($state === IPF_ORM_Record::STATE_LOCKED) {
+ return false;
+ }
+
+ $record->state(IPF_ORM_Record::STATE_LOCKED);
+
+ $conn->beginInternalTransaction();
+ $saveLater = $this->saveRelated($record);
+
+ $record->state($state);
+
+ if ($record->isValid()) {
+ $event = new IPF_ORM_Event($record, IPF_ORM_Event::RECORD_SAVE);
+ $record->preSave($event);
+ $record->getTable()->notifyRecordListeners('preSave', $event);
+ $state = $record->state();
+
+ if ( ! $event->skipOperation) {
+ switch ($state) {
+ case IPF_ORM_Record::STATE_TDIRTY:
+ $this->insert($record);
+ break;
+ case IPF_ORM_Record::STATE_DIRTY:
+ case IPF_ORM_Record::STATE_PROXY:
+ $this->update($record);
+ break;
+ case IPF_ORM_Record::STATE_CLEAN:
+ case IPF_ORM_Record::STATE_TCLEAN:
+ break;
+ }
+ }
+
+ // NOTE: what about referential integrity issues?
+ foreach ($record->getPendingDeletes() as $pendingDelete) {
+ $pendingDelete->delete();
+ }
+
+ $record->getTable()->notifyRecordListeners('postSave', $event);
+ $record->postSave($event);
+ } else {
+ $conn->transaction->addInvalid($record);
+ }
+
+ $state = $record->state();
+
+ $record->state(IPF_ORM_Record::STATE_LOCKED);
+
+ foreach ($saveLater as $fk) {
+ $alias = $fk->getAlias();
+
+ if ($record->hasReference($alias)) {
+ $obj = $record->$alias;
+ // check that the related object is not an instance of IPF_ORM_Null
+ if ( ! ($obj instanceof IPF_ORM_Null)) {
+ $obj->save($conn);
+ }
+ }
+ }
+
+ // save the MANY-TO-MANY associations
+ $this->saveAssociations($record);
+
+ $record->state($state);
+ $conn->commit();
+
+ return true;
+ }
+
+ public function save(IPF_ORM_Record $record)
+ {
+ $event = new IPF_ORM_Event($record, IPF_ORM_Event::RECORD_SAVE);
+
+ $record->preSave($event);
+
+ $record->getTable()->notifyRecordListeners('preSave', $event);
+
+ if ( ! $event->skipOperation) {
+ switch ($record->state()) {
+ case IPF_ORM_Record::STATE_TDIRTY:
+ $this->insert($record);
+ break;
+ case IPF_ORM_Record::STATE_DIRTY:
+ case IPF_ORM_Record::STATE_PROXY:
+ $this->update($record);
+ break;
+ case IPF_ORM_Record::STATE_CLEAN:
+ case IPF_ORM_Record::STATE_TCLEAN:
+ // do nothing
+ break;
+ }
+ }
+
+ $record->getTable()->notifyRecordListeners('postSave', $event);
+
+ $record->postSave($event);
+ }
+
+ public function delete(IPF_ORM_Record $record)
+ {
+ $deletions = array();
+ $this->_collectDeletions($record, $deletions);
+ return $this->_executeDeletions($deletions);
+ }
+
+ private function _collectDeletions(IPF_ORM_Record $record, array &$deletions)
+ {
+ if ( ! $record->exists()) {
+ return;
+ }
+
+ $deletions[$record->getOid()] = $record;
+ $this->_cascadeDelete($record, $deletions);
+ }
+
+ private function _executeDeletions(array $deletions)
+ {
+ // collect class names
+ $classNames = array();
+ foreach ($deletions as $record) {
+ $classNames[] = $record->getTable()->getComponentName();
+ }
+ $classNames = array_unique($classNames);
+
+ // order deletes
+ $executionOrder = $this->buildFlushTree($classNames);
+
+ // execute
+ try {
+ $this->conn->beginInternalTransaction();
+
+ for ($i = count($executionOrder) - 1; $i >= 0; $i--) {
+ $className = $executionOrder[$i];
+ $table = $this->conn->getTable($className);
+
+ // collect identifiers
+ $identifierMaps = array();
+ $deletedRecords = array();
+ foreach ($deletions as $oid => $record) {
+ if ($record->getTable()->getComponentName() == $className) {
+ $veto = $this->_preDelete($record);
+ if ( ! $veto) {
+ $identifierMaps[] = $record->identifier();
+ $deletedRecords[] = $record;
+ unset($deletions[$oid]);
+ }
+ }
+ }
+
+ if (count($deletedRecords) < 1) {
+ continue;
+ }
+
+ // extract query parameters (only the identifier values are of interest)
+ $params = array();
+ $columnNames = array();
+ foreach ($identifierMaps as $idMap) {
+ while (list($fieldName, $value) = each($idMap)) {
+ $params[] = $value;
+ $columnNames[] = $table->getColumnName($fieldName);
+ }
+ }
+ $columnNames = array_unique($columnNames);
+
+ // delete
+ $tableName = $table->getTableName();
+ $sql = "DELETE FROM " . $this->conn->quoteIdentifier($tableName) . " WHERE ";
+
+ if ($table->isIdentifierComposite()) {
+ $sql .= $this->_buildSqlCompositeKeyCondition($columnNames, count($identifierMaps));
+ $this->conn->exec($sql, $params);
+ } else {
+ $sql .= $this->_buildSqlSingleKeyCondition($columnNames, count($params));
+ $this->conn->exec($sql, $params);
+ }
+
+ // adjust state, remove from identity map and inform postDelete listeners
+ foreach ($deletedRecords as $record) {
+ $record->state(IPF_ORM_Record::STATE_TCLEAN);
+ $record->getTable()->removeRecord($record);
+ $this->_postDelete($record);
+ }
+ }
+
+ $this->conn->commit();
+ // trigger postDelete for records skipped during the deletion (veto!)
+ foreach ($deletions as $skippedRecord) {
+ $this->_postDelete($skippedRecord);
+ }
+
+ return true;
+ } catch (Exception $e) {
+ $this->conn->rollback();
+ throw $e;
+ }
+ }
+
+ private function _buildSqlSingleKeyCondition($columnNames, $numRecords)
+ {
+ $idColumn = $this->conn->quoteIdentifier($columnNames[0]);
+ return implode(' OR ', array_fill(0, $numRecords, "$idColumn = ?"));
+ }
+
+ private function _buildSqlCompositeKeyCondition($columnNames, $numRecords)
+ {
+ $singleCondition = "";
+ foreach ($columnNames as $columnName) {
+ $columnName = $this->conn->quoteIdentifier($columnName);
+ if ($singleCondition === "") {
+ $singleCondition .= "($columnName = ?";
+ } else {
+ $singleCondition .= " AND $columnName = ?";
+ }
+ }
+ $singleCondition .= ")";
+ $fullCondition = implode(' OR ', array_fill(0, $numRecords, $singleCondition));
+
+ return $fullCondition;
+ }
+
+ protected function _cascadeDelete(IPF_ORM_Record $record, array &$deletions)
+ {
+ foreach ($record->getTable()->getRelations() as $relation) {
+ if ($relation->isCascadeDelete()) {
+ $fieldName = $relation->getAlias();
+ // if it's a xToOne relation and the related object is already loaded
+ // we don't need to refresh.
+ if ( ! ($relation->getType() == IPF_ORM_Relation::ONE && isset($record->$fieldName))) {
+ $record->refreshRelated($relation->getAlias());
+ }
+ $relatedObjects = $record->get($relation->getAlias());
+ if ($relatedObjects instanceof IPF_ORM_Record && $relatedObjects->exists()
+ && ! isset($deletions[$relatedObjects->getOid()])) {
+ $this->_collectDeletions($relatedObjects, $deletions);
+ } else if ($relatedObjects instanceof IPF_ORM_Collection && count($relatedObjects) > 0) {
+ // cascade the delete to the other objects
+ foreach ($relatedObjects as $object) {
+ if ( ! isset($deletions[$object->getOid()])) {
+ $this->_collectDeletions($object, $deletions);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ public function saveRelated(IPF_ORM_Record $record)
+ {
+ $saveLater = array();
+ foreach ($record->getReferences() as $k => $v) {
+ $rel = $record->getTable()->getRelation($k);
+
+ $local = $rel->getLocal();
+ $foreign = $rel->getForeign();
+
+ if ($rel instanceof IPF_ORM_Relation_ForeignKey) {
+ $saveLater[$k] = $rel;
+ } else if ($rel instanceof IPF_ORM_Relation_LocalKey) {
+ // ONE-TO-ONE relationship
+ $obj = $record->get($rel->getAlias());
+
+ // Protection against infinite function recursion before attempting to save
+ if ($obj instanceof IPF_ORM_Record && $obj->isModified()) {
+ $obj->save($this->conn);
+
+ /** Can this be removed?
+ $id = array_values($obj->identifier());
+
+ foreach ((array) $rel->getLocal() as $k => $field) {
+ $record->set($field, $id[$k]);
+ }
+ */
+ }
+ }
+ }
+
+ return $saveLater;
+ }
+
+ public function saveAssociations(IPF_ORM_Record $record)
+ {
+ foreach ($record->getReferences() as $k => $v) {
+ $rel = $record->getTable()->getRelation($k);
+ //print get_class($rel);
+ if ($rel instanceof IPF_ORM_Relation_Association) {
+ $v->save($this->conn);
+
+ $assocTable = $rel->getAssociationTable();
+
+ foreach ($v->getDeleteDiff() as $r) {
+ $query = 'DELETE FROM ' . $assocTable->getTableName()
+ . ' WHERE ' . $rel->getForeign() . ' = ?'
+ . ' AND ' . $rel->getLocal() . ' = ?';
+
+ $this->conn->execute($query, array($r->getIncremented(), $record->getIncremented()));
+ }
+
+ foreach ($v->getInsertDiff() as $r) {
+ $assocRecord = $assocTable->create();
+ $assocRecord->set($assocTable->getFieldName($rel->getForeign()), $r);
+ $assocRecord->set($assocTable->getFieldName($rel->getLocal()), $record);
+ $this->saveGraph($assocRecord);
+ }
+ }
+ }
+ }
+
+ private function _preDelete(IPF_ORM_Record $record)
+ {
+ $event = new IPF_ORM_Event($record, IPF_ORM_Event::RECORD_DELETE);
+ $record->preDelete($event);
+ $record->getTable()->notifyRecordListeners('preDelete', $event);
+
+ return $event->skipOperation;
+ }
+
+ private function _postDelete(IPF_ORM_Record $record)
+ {
+ $event = new IPF_ORM_Event($record, IPF_ORM_Event::RECORD_DELETE);
+ $record->postDelete($event);
+ $record->getTable()->notifyRecordListeners('postDelete', $event);
+ }
+
+ public function saveAll()
+ {
+ // get the flush tree
+ $tree = $this->buildFlushTree($this->conn->getTables());
+
+ // save all records
+ foreach ($tree as $name) {
+ $table = $this->conn->getTable($name);
+ foreach ($table->getRepository() as $record) {
+ $this->saveGraph($record);
+ }
+ }
+ }
+
+ public function update(IPF_ORM_Record $record)
+ {
+ $event = new IPF_ORM_Event($record, IPF_ORM_Event::RECORD_UPDATE);
+ $record->preUpdate($event);
+ $table = $record->getTable();
+ $table->notifyRecordListeners('preUpdate', $event);
+
+ if ( ! $event->skipOperation) {
+ $identifier = $record->identifier();
+ $array = $record->getPrepared();
+ $this->conn->update($table, $array, $identifier);
+ $record->assignIdentifier(true);
+ }
+
+ $table->notifyRecordListeners('postUpdate', $event);
+
+ $record->postUpdate($event);
+
+ return true;
+ }
+
+ public function insert(IPF_ORM_Record $record)
+ {
+ // listen the onPreInsert event
+ $event = new IPF_ORM_Event($record, IPF_ORM_Event::RECORD_INSERT);
+ $record->preInsert($event);
+ $table = $record->getTable();
+ $table->notifyRecordListeners('preInsert', $event);
+
+ if ( ! $event->skipOperation) {
+ $this->processSingleInsert($record);
+ }
+
+ $table->addRecord($record);
+ $table->notifyRecordListeners('postInsert', $event);
+ $record->postInsert($event);
+
+ return true;
+ }
+
+ public function processSingleInsert(IPF_ORM_Record $record)
+ {
+ $fields = $record->getPrepared();
+ $table = $record->getTable();
+
+ // Populate fields with a blank array so that a blank records can be inserted
+ if (empty($fields)) {
+ foreach ($table->getFieldNames() as $field) {
+ $fields[$field] = null;
+ }
+ }
+
+ $identifier = (array) $table->getIdentifier();
+
+ $this->conn->insert($table, $fields);
+
+ if (count($identifier) == 1 && $table->getIdentifierType() !== IPF_ORM::IDENTIFIER_NATURAL) {
+ $id = $this->conn->lastInsertId();
+ if (!$id)
+ throw new IPF_ORM_Exception("Couldn't get last insert identifier.");
+
+ $record->assignIdentifier($id);
+ } else {
+ $record->assignIdentifier(true);
+ }
+ }
+
+ public function buildFlushTree(array $tables)
+ {
+ // determine classes to order. only necessary because the $tables param
+ // can contain strings or table objects...
+ $classesToOrder = array();
+ foreach ($tables as $table) {
+ if ( ! ($table instanceof IPF_ORM_Table)) {
+ $table = $this->conn->getTable($table, false);
+ }
+ $classesToOrder[] = $table->getComponentName();
+ }
+ $classesToOrder = array_unique($classesToOrder);
+
+ if (count($classesToOrder) < 2) {
+ return $classesToOrder;
+ }
+
+ // build the correct order
+ $flushList = array();
+ foreach ($classesToOrder as $class) {
+ $table = $this->conn->getTable($class, false);
+ $currentClass = $table->getComponentName();
+
+ $index = array_search($currentClass, $flushList);
+
+ if ($index === false) {
+ //echo "adding $currentClass to flushlist";
+ $flushList[] = $currentClass;
+ $index = max(array_keys($flushList));
+ }
+
+ $rels = $table->getRelations();
+
+ // move all foreignkey relations to the beginning
+ foreach ($rels as $key => $rel) {
+ if ($rel instanceof IPF_ORM_Relation_ForeignKey) {
+ unset($rels[$key]);
+ array_unshift($rels, $rel);
+ }
+ }
+
+ foreach ($rels as $rel) {
+ $relatedClassName = $rel->getTable()->getComponentName();
+
+ if ( ! in_array($relatedClassName, $classesToOrder)) {
+ continue;
+ }
+
+ $relatedCompIndex = array_search($relatedClassName, $flushList);
+ $type = $rel->getType();
+
+ // skip self-referenced relations
+ if ($relatedClassName === $currentClass) {
+ continue;
+ }
+
+ if ($rel instanceof IPF_ORM_Relation_ForeignKey) {
+ // the related component needs to come after this component in
+ // the list (since it holds the fk)
+
+ if ($relatedCompIndex !== false) {
+ // the component is already in the list
+ if ($relatedCompIndex >= $index) {
+ // it's already in the right place
+ continue;
+ }
+
+ unset($flushList[$index]);
+ // the related comp has the fk. so put "this" comp immediately
+ // before it in the list
+ array_splice($flushList, $relatedCompIndex, 0, $currentClass);
+ $index = $relatedCompIndex;
+ } else {
+ $flushList[] = $relatedClassName;
+ }
+
+ } else if ($rel instanceof IPF_ORM_Relation_LocalKey) {
+ // the related component needs to come before the current component
+ // in the list (since this component holds the fk).
+
+ if ($relatedCompIndex !== false) {
+ // already in flush list
+ if ($relatedCompIndex <= $index) {
+ // it's in the right place
+ continue;
+ }
+
+ unset($flushList[$relatedCompIndex]);
+ // "this" comp has the fk. so put the related comp before it
+ // in the list
+ array_splice($flushList, $index, 0, $relatedClassName);
+ } else {
+ array_unshift($flushList, $relatedClassName);
+ $index++;
+ }
+ } else if ($rel instanceof IPF_ORM_Relation_Association) {
+ // the association class needs to come after both classes
+ // that are connected through it in the list (since it holds
+ // both fks)
+
+ $assocTable = $rel->getAssociationFactory();
+ $assocClassName = $assocTable->getComponentName();
+
+ if ($relatedCompIndex !== false) {
+ unset($flushList[$relatedCompIndex]);
+ }
+
+ array_splice($flushList, $index, 0, $relatedClassName);
+ $index++;
+
+ $index3 = array_search($assocClassName, $flushList);
+
+ if ($index3 !== false) {
+ if ($index3 >= $index) {
+ continue;
+ }
+
+ unset($flushList[$index]);
+ array_splice($flushList, $index3, 0, $assocClassName);
+ $index = $relatedCompIndex;
+ } else {
+ $flushList[] = $assocClassName;
+ }
+ }
+ }
+ }
+
+ return array_values($flushList);
+ }
+
+ private function _formatDataSet(IPF_ORM_Record $record)
+ {
+ $table = $record->getTable();
+ $dataSet = array();
+ $component = $table->getComponentName();
+ $array = $record->getPrepared();
+
+ foreach ($table->getColumns() as $columnName => $definition) {
+ if ( ! isset($dataSet[$component])) {
+ $dataSet[$component] = array();
+ }
+
+ $fieldName = $table->getFieldName($columnName);
+ if (isset($definition['primary']) && $definition['primary']) {
+ continue;
+ }
+
+ if ( ! array_key_exists($fieldName, $array)) {
+ continue;
+ }
+
+ if (isset($definition['owner'])) {
+ $dataSet[$definition['owner']][$fieldName] = $array[$fieldName];
+ } else {
+ $dataSet[$component][$fieldName] = $array[$fieldName];
+ }
+ }
+
+ return $dataSet;
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Event
+{
+ const CONN_QUERY = 1;
+ const CONN_EXEC = 2;
+ const CONN_PREPARE = 3;
+ const CONN_CONNECT = 4;
+ const CONN_CLOSE = 5;
+ const CONN_ERROR = 6;
+
+ const STMT_EXECUTE = 10;
+ const STMT_FETCH = 11;
+ const STMT_FETCHALL = 12;
+
+ const TX_BEGIN = 31;
+ const TX_COMMIT = 32;
+ const TX_ROLLBACK = 33;
+ const SAVEPOINT_CREATE = 34;
+ const SAVEPOINT_ROLLBACK = 35;
+ const SAVEPOINT_COMMIT = 36;
+
+ const HYDRATE = 40;
+
+ const RECORD_DELETE = 21;
+ const RECORD_SAVE = 22;
+ const RECORD_UPDATE = 23;
+ const RECORD_INSERT = 24;
+ const RECORD_SERIALIZE = 25;
+ const RECORD_UNSERIALIZE = 26;
+ const RECORD_DQL_SELECT = 28;
+ const RECORD_DQL_DELETE = 27;
+ const RECORD_DQL_UPDATE = 29;
+
+ protected $_invoker;
+
+ protected $_query;
+
+ protected $_params;
+
+ protected $_code;
+
+ protected $_startedMicrotime;
+
+ protected $_endedMicrotime;
+
+ protected $_options = array();
+
+ public function __construct($invoker, $code, $query = null, $params = array())
+ {
+ $this->_invoker = $invoker;
+ $this->_code = $code;
+ $this->_query = $query;
+ $this->_params = $params;
+ }
+
+ public function getQuery()
+ {
+ return $this->_query;
+ }
+
+ public function getName()
+ {
+ switch ($this->_code) {
+ case self::CONN_QUERY:
+ return 'query';
+ case self::CONN_EXEC:
+ return 'exec';
+ case self::CONN_PREPARE:
+ return 'prepare';
+ case self::CONN_CONNECT:
+ return 'connect';
+ case self::CONN_CLOSE:
+ return 'close';
+ case self::CONN_ERROR:
+ return 'error';
+
+ case self::STMT_EXECUTE:
+ return 'execute';
+ case self::STMT_FETCH:
+ return 'fetch';
+ case self::STMT_FETCHALL:
+ return 'fetch all';
+
+ case self::TX_BEGIN:
+ return 'begin';
+ case self::TX_COMMIT:
+ return 'commit';
+ case self::TX_ROLLBACK:
+ return 'rollback';
+
+ case self::SAVEPOINT_CREATE:
+ return 'create savepoint';
+ case self::SAVEPOINT_ROLLBACK:
+ return 'rollback savepoint';
+ case self::SAVEPOINT_COMMIT:
+ return 'commit savepoint';
+
+ case self::RECORD_DELETE:
+ return 'delete record';
+ case self::RECORD_SAVE:
+ return 'save record';
+ case self::RECORD_UPDATE:
+ return 'update record';
+ case self::RECORD_INSERT:
+ return 'insert record';
+ case self::RECORD_SERIALIZE:
+ return 'serialize record';
+ case self::RECORD_UNSERIALIZE:
+ return 'unserialize record';
+ case self::RECORD_DQL_SELECT:
+ return 'select records';
+ case self::RECORD_DQL_DELETE:
+ return 'delete records';
+ case self::RECORD_DQL_UPDATE:
+ return 'update records';
+ }
+ }
+
+ public function getCode()
+ {
+ return $this->_code;
+ }
+
+ public function __get($option)
+ {
+ if ( ! isset($this->_options[$option])) {
+ return null;
+ }
+
+ return $this->_options[$option];
+ }
+
+ public function skipOperation()
+ {
+ $this->_options['skipOperation'] = true;
+
+ return $this;
+ }
+
+ public function __set($option, $value)
+ {
+ $this->_options[$option] = $value;
+
+ return $this;
+ }
+
+ public function set($option, &$value)
+ {
+ $this->_options[$option] =& $value;
+
+ return $this;
+ }
+
+ public function start()
+ {
+ $this->_startedMicrotime = microtime(true);
+ }
+
+ public function hasEnded()
+ {
+ return ($this->_endedMicrotime != null);
+ }
+
+ public function end()
+ {
+ $this->_endedMicrotime = microtime(true);
+
+ return $this;
+ }
+
+ public function getInvoker()
+ {
+ return $this->_invoker;
+ }
+
+ public function setInvoker($invoker)
+ {
+ $this->_invoker = $invoker;
+ }
+
+ public function getParams()
+ {
+ return $this->_params;
+ }
+
+ public function getElapsedSecs()
+ {
+ if (is_null($this->_endedMicrotime)) {
+ return false;
+ }
+ return ($this->_endedMicrotime - $this->_startedMicrotime);
+ }
+}
--- /dev/null
+<?php
+
+class IPF_ORM_Exception extends IPF_Exception
+{
+ protected static $_errorMessages = array(
+ IPF_ORM::ERR => 'unknown error',
+ IPF_ORM::ERR_ALREADY_EXISTS => 'already exists',
+ IPF_ORM::ERR_CANNOT_CREATE => 'can not create',
+ IPF_ORM::ERR_CANNOT_ALTER => 'can not alter',
+ IPF_ORM::ERR_CANNOT_REPLACE => 'can not replace',
+ IPF_ORM::ERR_CANNOT_DELETE => 'can not delete',
+ IPF_ORM::ERR_CANNOT_DROP => 'can not drop',
+ IPF_ORM::ERR_CONSTRAINT => 'constraint violation',
+ IPF_ORM::ERR_CONSTRAINT_NOT_NULL=> 'null value violates not-null constraint',
+ IPF_ORM::ERR_DIVZERO => 'division by zero',
+ IPF_ORM::ERR_INVALID => 'invalid',
+ IPF_ORM::ERR_INVALID_DATE => 'invalid date or time',
+ IPF_ORM::ERR_INVALID_NUMBER => 'invalid number',
+ IPF_ORM::ERR_MISMATCH => 'mismatch',
+ IPF_ORM::ERR_NODBSELECTED => 'no database selected',
+ IPF_ORM::ERR_NOSUCHFIELD => 'no such field',
+ IPF_ORM::ERR_NOSUCHTABLE => 'no such table',
+ IPF_ORM::ERR_NOT_CAPABLE => 'IPF backend not capable',
+ IPF_ORM::ERR_NOT_FOUND => 'not found',
+ IPF_ORM::ERR_NOT_LOCKED => 'not locked',
+ IPF_ORM::ERR_SYNTAX => 'syntax error',
+ IPF_ORM::ERR_UNSUPPORTED => 'not supported',
+ IPF_ORM::ERR_VALUE_COUNT_ON_ROW => 'value count on row',
+ IPF_ORM::ERR_INVALID_DSN => 'invalid DSN',
+ IPF_ORM::ERR_CONNECT_FAILED => 'connect failed',
+ IPF_ORM::ERR_NEED_MORE_DATA => 'insufficient data supplied',
+ IPF_ORM::ERR_EXTENSION_NOT_FOUND=> 'extension not found',
+ IPF_ORM::ERR_NOSUCHDB => 'no such database',
+ IPF_ORM::ERR_ACCESS_VIOLATION => 'insufficient permissions',
+ IPF_ORM::ERR_LOADMODULE => 'error while including on demand module',
+ IPF_ORM::ERR_TRUNCATED => 'truncated',
+ IPF_ORM::ERR_DEADLOCK => 'deadlock detected',
+ );
+
+ public function errorMessage($value = null)
+ {
+ if (is_null($value)) {
+ return self::$_errorMessages;
+ }
+
+ return isset(self::$_errorMessages[$value]) ?
+ self::$_errorMessages[$value] : self::$_errorMessages[IPF_ORM::ERR];
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Exception_Adapter extends IPF_ORM_Exception{}
--- /dev/null
+<?php
+
+class IPF_ORM_Exception_Connection extends IPF_ORM_Exception
+{
+ static protected $errorMessages = array(
+ IPF_ORM::ERR => 'unknown error',
+ IPF_ORM::ERR_ALREADY_EXISTS => 'already exists',
+ IPF_ORM::ERR_CANNOT_CREATE => 'can not create',
+ IPF_ORM::ERR_CANNOT_ALTER => 'can not alter',
+ IPF_ORM::ERR_CANNOT_REPLACE => 'can not replace',
+ IPF_ORM::ERR_CANNOT_DELETE => 'can not delete',
+ IPF_ORM::ERR_CANNOT_DROP => 'can not drop',
+ IPF_ORM::ERR_CONSTRAINT => 'constraint violation',
+ IPF_ORM::ERR_CONSTRAINT_NOT_NULL=> 'null value violates not-null constraint',
+ IPF_ORM::ERR_DIVZERO => 'division by zero',
+ IPF_ORM::ERR_INVALID => 'invalid',
+ IPF_ORM::ERR_INVALID_DATE => 'invalid date or time',
+ IPF_ORM::ERR_INVALID_NUMBER => 'invalid number',
+ IPF_ORM::ERR_MISMATCH => 'mismatch',
+ IPF_ORM::ERR_NODBSELECTED => 'no database selected',
+ IPF_ORM::ERR_NOSUCHFIELD => 'no such field',
+ IPF_ORM::ERR_NOSUCHTABLE => 'no such table',
+ IPF_ORM::ERR_NOT_CAPABLE => 'IPF_ORM backend not capable',
+ IPF_ORM::ERR_NOT_FOUND => 'not found',
+ IPF_ORM::ERR_NOT_LOCKED => 'not locked',
+ IPF_ORM::ERR_SYNTAX => 'syntax error',
+ IPF_ORM::ERR_UNSUPPORTED => 'not supported',
+ IPF_ORM::ERR_VALUE_COUNT_ON_ROW => 'value count on row',
+ IPF_ORM::ERR_INVALID_DSN => 'invalid DSN',
+ IPF_ORM::ERR_CONNECT_FAILED => 'connect failed',
+ IPF_ORM::ERR_NEED_MORE_DATA => 'insufficient data supplied',
+ IPF_ORM::ERR_EXTENSION_NOT_FOUND=> 'extension not found',
+ IPF_ORM::ERR_NOSUCHDB => 'no such database',
+ IPF_ORM::ERR_ACCESS_VIOLATION => 'insufficient permissions',
+ IPF_ORM::ERR_LOADMODULE => 'error while including on demand module',
+ IPF_ORM::ERR_TRUNCATED => 'truncated',
+ IPF_ORM::ERR_DEADLOCK => 'deadlock detected',
+ );
+
+ protected $portableCode;
+
+ public function getPortableCode()
+ {
+ return $this->portableCode;
+ }
+
+ public function getPortableMessage()
+ {
+ return self::errorMessage($this->portableCode);
+ }
+
+ public function errorMessage($value = null)
+ {
+ return isset(self::$errorMessages[$value]) ?
+ self::$errorMessages[$value] : self::$errorMessages[IPF_ORM::ERR];
+ }
+
+ public function processErrorInfo(array $errorInfo)
+ { }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+class IPF_ORM_Exception_Locator extends IPF_ORM_Exception_Base{}
--- /dev/null
+<?php
+
+class IPF_ORM_Exception_Mysql extends IPF_ORM_Exception_Connection
+{
+ protected static $errorCodeMap = array(
+ 1004 => IPF_ORM::ERR_CANNOT_CREATE,
+ 1005 => IPF_ORM::ERR_CANNOT_CREATE,
+ 1006 => IPF_ORM::ERR_CANNOT_CREATE,
+ 1007 => IPF_ORM::ERR_ALREADY_EXISTS,
+ 1008 => IPF_ORM::ERR_CANNOT_DROP,
+ 1022 => IPF_ORM::ERR_ALREADY_EXISTS,
+ 1044 => IPF_ORM::ERR_ACCESS_VIOLATION,
+ 1046 => IPF_ORM::ERR_NODBSELECTED,
+ 1048 => IPF_ORM::ERR_CONSTRAINT,
+ 1049 => IPF_ORM::ERR_NOSUCHDB,
+ 1050 => IPF_ORM::ERR_ALREADY_EXISTS,
+ 1051 => IPF_ORM::ERR_NOSUCHTABLE,
+ 1054 => IPF_ORM::ERR_NOSUCHFIELD,
+ 1061 => IPF_ORM::ERR_ALREADY_EXISTS,
+ 1062 => IPF_ORM::ERR_ALREADY_EXISTS,
+ 1064 => IPF_ORM::ERR_SYNTAX,
+ 1091 => IPF_ORM::ERR_NOT_FOUND,
+ 1100 => IPF_ORM::ERR_NOT_LOCKED,
+ 1136 => IPF_ORM::ERR_VALUE_COUNT_ON_ROW,
+ 1142 => IPF_ORM::ERR_ACCESS_VIOLATION,
+ 1146 => IPF_ORM::ERR_NOSUCHTABLE,
+ 1216 => IPF_ORM::ERR_CONSTRAINT,
+ 1217 => IPF_ORM::ERR_CONSTRAINT,
+ 1451 => IPF_ORM::ERR_CONSTRAINT,
+ );
+ public function processErrorInfo(array $errorInfo)
+ {
+ $code = $errorInfo[1];
+ if (isset(self::$errorCodeMap[$code])) {
+ $this->portableCode = self::$errorCodeMap[$code];
+ return true;
+ }
+ return false;
+ }
+}
--- /dev/null
+<?php
+
+class IPF_ORM_Exception_Profiler extends IPF_ORM_Exception{}
--- /dev/null
+<?php
+
+class IPF_ORM_Exception_Validator extends IPF_ORM_Exception
+{
+ private $invalid = array();
+
+ public function __construct(array $invalid)
+ {
+ $this->invalid = $invalid;
+ parent::__construct($this->generateMessage());
+ }
+
+ public function getInvalidRecords()
+ {
+ return $this->invalid;
+ }
+
+ private function generateMessage()
+ {
+ $message = "";
+ foreach ($this->invalid as $record) {
+ $errors = array();
+ foreach ($record->getErrors() as $field => $validators)
+ $errors[] = 'Field "' . $field . '" failed following validators: ' . implode(', ', $validators) . '.';
+ $message .= "Validaton error in class " . get_class($record) . ' (' . implode(' ', $errors) . ') ';
+ }
+ return $message;
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Export extends IPF_ORM_Connection_Module
+{
+ protected $valid_default_values = array(
+ 'text' => '',
+ 'boolean' => true,
+ 'integer' => 0,
+ 'decimal' => 0.0,
+ 'float' => 0.0,
+ 'double' => 0.0,
+ 'timestamp' => '1970-01-01 00:00:00',
+ 'time' => '00:00:00',
+ 'date' => '1970-01-01',
+ 'clob' => '',
+ 'blob' => '',
+ 'string' => ''
+ );
+
+ protected function getIndexName($name)
+ {
+ return $name . '_idx';
+ }
+
+ public function dropTableSql($table)
+ {
+ return 'DROP TABLE ' . $this->conn->quoteIdentifier($table);
+ }
+
+ public function dropTable($table)
+ {
+ $this->conn->execute($this->dropTableSql($table));
+ }
+
+ public function dropIndex($table, $name)
+ {
+ return $this->conn->exec($this->dropIndexSql($table, $name));
+ }
+
+ public function dropIndexSql($table, $name)
+ {
+ $name = $this->conn->quoteIdentifier($this->getIndexName($name));
+
+ return 'DROP INDEX ' . $name;
+ }
+
+ public function dropConstraint($table, $name, $primary = false)
+ {
+ $table = $this->conn->quoteIdentifier($table);
+ $name = $this->conn->quoteIdentifier($name);
+
+ return $this->conn->exec('ALTER TABLE ' . $table . ' DROP CONSTRAINT ' . $name);
+ }
+
+ public function dropForeignKey($table, $name)
+ {
+ return $this->dropConstraint($table, $name);
+ }
+
+ public function createConstraint($table, $name, $definition)
+ {
+ $sql = $this->createConstraintSql($table, $name, $definition);
+
+ return $this->conn->exec($sql);
+ }
+
+ public function createConstraintSql($table, $name, $definition)
+ {
+ $table = $this->conn->quoteIdentifier($table);
+ $name = $this->conn->quoteIdentifier($this->getIndexName($name));
+ $query = 'ALTER TABLE ' . $table . ' ADD CONSTRAINT ' . $name;
+
+ if (isset($definition['primary']) && $definition['primary']) {
+ $query .= ' PRIMARY KEY';
+ } elseif (isset($definition['unique']) && $definition['unique']) {
+ $query .= ' UNIQUE';
+ }
+
+ $fields = array();
+ foreach (array_keys($definition['fields']) as $field) {
+ $fields[] = $this->conn->quoteIdentifier($field);
+ }
+ $query .= ' ('. implode(', ', $fields) . ')';
+
+ return $query;
+ }
+
+ public function createIndex($table, $name, array $definition)
+ {
+ return $this->conn->execute($this->createIndexSql($table, $name, $definition));
+ }
+
+ public function createIndexSql($table, $name, array $definition)
+ {
+ $table = $this->conn->quoteIdentifier($table);
+ $name = $this->conn->quoteIdentifier($name);
+ $type = '';
+
+ if (isset($definition['type'])) {
+ switch (strtolower($definition['type'])) {
+ case 'unique':
+ $type = strtoupper($definition['type']) . ' ';
+ break;
+ default:
+ throw new IPF_ORM_Exception('Unknown index type ' . $definition['type']);
+ }
+ }
+
+ $query = 'CREATE ' . $type . 'INDEX ' . $name . ' ON ' . $table;
+
+ $fields = array();
+ foreach ($definition['fields'] as $field) {
+ $fields[] = $this->conn->quoteIdentifier($field);
+ }
+ $query .= ' (' . implode(', ', $fields) . ')';
+
+ return $query;
+ }
+
+ public function createForeignKeySql($table, array $definition)
+ {
+ $table = $this->conn->quoteIdentifier($table);
+
+ $query = 'ALTER TABLE ' . $table . ' ADD ' . $this->getForeignKeyDeclaration($definition);
+
+ return $query;
+ }
+
+ public function createForeignKey($table, array $definition)
+ {
+ $sql = $this->createForeignKeySql($table, $definition);
+
+ return $this->conn->execute($sql);
+ }
+
+ public function alterTable($name, array $changes, $check = false)
+ {
+ $sql = $this->alterTableSql($name, $changes, $check);
+
+ if (is_string($sql) && $sql) {
+ $this->conn->execute($sql);
+ }
+ }
+
+ public function alterTableSql($name, array $changes, $check = false)
+ {
+ throw new IPF_ORM_Exception('Alter table not supported by this driver.');
+ }
+
+ public function getFieldDeclarationList(array $fields)
+ {
+ foreach ($fields as $fieldName => $field) {
+ $query = $this->getDeclaration($fieldName, $field);
+
+ $queryFields[] = $query;
+ }
+ return implode(', ', $queryFields);
+ }
+
+ public function getCheckDeclaration(array $definition)
+ {
+ $constraints = array();
+ foreach ($definition as $field => $def) {
+ if (is_string($def)) {
+ $constraints[] = 'CHECK (' . $def . ')';
+ } else {
+ if (isset($def['min'])) {
+ $constraints[] = 'CHECK (' . $field . ' >= ' . $def['min'] . ')';
+ }
+
+ if (isset($def['max'])) {
+ $constraints[] = 'CHECK (' . $field . ' <= ' . $def['max'] . ')';
+ }
+ }
+ }
+
+ return implode(', ', $constraints);
+ }
+
+ public function getIndexDeclaration($name, array $definition)
+ {
+ $name = $this->conn->quoteIdentifier($name);
+ $type = '';
+
+ if (isset($definition['type'])) {
+ if (strtolower($definition['type']) == 'unique') {
+ $type = strtoupper($definition['type']) . ' ';
+ } else {
+ throw new IPF_ORM_Exception('Unknown index type ' . $definition['type']);
+ }
+ }
+
+ if ( ! isset($definition['fields']) || ! is_array($definition['fields'])) {
+ throw new IPF_ORM_Exception('No index columns given.');
+ }
+
+ $query = $type . 'INDEX ' . $name;
+
+ $query .= ' (' . $this->getIndexFieldDeclarationList($definition['fields']) . ')';
+
+ return $query;
+ }
+
+ public function getIndexFieldDeclarationList(array $fields)
+ {
+ $ret = array();
+ foreach ($fields as $field => $definition) {
+ if (is_array($definition)) {
+ $ret[] = $this->conn->quoteIdentifier($field);
+ } else {
+ $ret[] = $this->conn->quoteIdentifier($definition);
+ }
+ }
+ return implode(', ', $ret);
+ }
+
+ public function getTemporaryTableQuery()
+ {
+ return 'TEMPORARY';
+ }
+
+ public function getForeignKeyDeclaration(array $definition)
+ {
+ $sql = $this->getForeignKeyBaseDeclaration($definition);
+ $sql .= $this->getAdvancedForeignKeyOptions($definition);
+ return $sql;
+ }
+
+ public function getAdvancedForeignKeyOptions(array $definition)
+ {
+ $query = '';
+ if ( ! empty($definition['onUpdate'])) {
+ $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialAction($definition['onUpdate']);
+ }
+ if ( ! empty($definition['onDelete'])) {
+ $query .= ' ON DELETE ' . $this->getForeignKeyReferentialAction($definition['onDelete']);
+ }
+ return $query;
+ }
+
+ public function getForeignKeyReferentialAction($action)
+ {
+ $upper = strtoupper($action);
+ switch ($upper) {
+ case 'CASCADE':
+ case 'SET NULL':
+ case 'NO ACTION':
+ case 'RESTRICT':
+ case 'SET DEFAULT':
+ return $upper;
+ break;
+ default:
+ throw new IPF_ORM_Exception('Unknown foreign key referential action \'' . $upper . '\' given.');
+ }
+ }
+
+ public function getForeignKeyBaseDeclaration(array $definition)
+ {
+ $sql = '';
+ if (isset($definition['name'])) {
+ $sql .= ' CONSTRAINT ' . $this->conn->quoteIdentifier($definition['name']) . ' ';
+ }
+ $sql .= 'FOREIGN KEY (';
+
+ if ( ! isset($definition['local'])) {
+ throw new IPF_ORM_Exception('Local reference field missing from definition.');
+ }
+ if ( ! isset($definition['foreign'])) {
+ throw new IPF_ORM_Exception('Foreign reference field missing from definition.');
+ }
+ if ( ! isset($definition['foreignTable'])) {
+ throw new IPF_ORM_Exception('Foreign reference table missing from definition.');
+ }
+
+ if ( ! is_array($definition['local'])) {
+ $definition['local'] = array($definition['local']);
+ }
+ if ( ! is_array($definition['foreign'])) {
+ $definition['foreign'] = array($definition['foreign']);
+ }
+
+ $sql .= implode(', ', array_map(array($this->conn, 'quoteIdentifier'), $definition['local']))
+ . ') REFERENCES '
+ . $this->conn->quoteIdentifier($definition['foreignTable']) . '('
+ . implode(', ', array_map(array($this->conn, 'quoteIdentifier'), $definition['foreign'])) . ')';
+
+ return $sql;
+ }
+
+ public function exportSortedClassesSql($classes, $groupByConnection = true)
+ {
+ $connections = array();
+ foreach ($classes as $class) {
+ $connection = IPF_ORM_Manager::connection();
+ $connectionName = $connection->getName();
+
+ if ( ! isset($connections[$connectionName])) {
+ $connections[$connectionName] = array(
+ 'create_tables' => array(),
+ 'create_indexes' => array(),
+ 'alters' => array()
+ );
+ }
+
+ $sql = $this->exportClassesSql($class);
+
+ // Build array of all the creates
+ // We need these to happen first
+ foreach ($sql as $key => $query) {
+ // If create table statement
+ if (substr($query, 0, strlen('CREATE TABLE')) == 'CREATE TABLE') {
+ $connections[$connectionName]['create_tables'][] = $query;
+
+ unset($sql[$key]);
+ continue;
+ }
+
+ // If create index statement
+ if (preg_grep("/CREATE .* INDEX/", array($query))) {
+ $connections[$connectionName]['create_indexes'][] = $query;
+
+ unset($sql[$key]);
+ continue;
+ }
+
+ // If alter table statement
+ if (substr($query, 0, strlen('ALTER TABLE')) == 'ALTER TABLE') {
+ $connections[$connectionName]['alters'][] = $query;
+
+ unset($sql[$key]);
+ continue;
+ }
+ }
+ }
+
+ // Loop over all the sql again to merge everything together so it is in the correct order
+ $build = array();
+ foreach ($connections as $connectionName => $sql) {
+ $build[$connectionName] = array_merge($sql['create_tables'], $sql['create_indexes'], $sql['alters']);
+ }
+
+ if ( ! $groupByConnection) {
+ $new = array();
+ foreach($build as $connectionname => $sql) {
+ $new = array_merge($new, $sql);
+ }
+ $build = $new;
+ }
+ return $build;
+ }
+
+ public function exportClasses(array $classes)
+ {
+ $queries = $this->exportSortedClassesSql($classes);
+
+ foreach ($queries as $connectionName => $sql) {
+ $connection = IPF_ORM_Manager::connection();
+
+ $connection->beginTransaction();
+
+ foreach ($sql as $query) {
+ try {
+ $connection->exec($query);
+ } catch (IPF_ORM_Exception $e) {
+ // we only want to silence table already exists errors
+ if ($e->getPortableCode() !== IPF_ORM::ERR_ALREADY_EXISTS) {
+ $connection->rollback();
+ throw new IPF_ORM_Exception($e->getMessage() . '. Failing Query: ' . $query);
+ }
+ }
+ }
+ $connection->commit();
+ }
+ }
+
+ public function exportClassesSql($name)
+ {
+ $sql = array();
+
+ $table = IPF_ORM::getTable($name);
+
+ $data = $this->getExportableFormat($table);
+
+ $query = $this->createTableSql($data['tableName'], $data['columns'], $data['options']);
+
+ if (is_array($query)) {
+ $sql = array_merge($sql, $query);
+ } else {
+ $sql[] = $query;
+ }
+
+ $sql = array_unique($sql);
+
+ rsort($sql);
+
+ return $sql;
+ }
+
+ private function getExportableFormat($table)
+ {
+ $columns = array();
+ $primary = array();
+
+ foreach ($table->getColumns() as $name => $definition) {
+
+ if (isset($definition['owner'])) {
+ continue;
+ }
+
+ switch ($definition['type']) {
+ case 'enum':
+ if (isset($definition['default'])) {
+ $definition['default'] = $table->enumIndex($name, $definition['default']);
+ }
+ break;
+ case 'boolean':
+ if (isset($definition['default'])) {
+ $definition['default'] = $table->getConnection()->convertBooleans($definition['default']);
+ }
+ break;
+ }
+ $columns[$name] = $definition;
+
+ if (isset($definition['primary']) && $definition['primary']) {
+ $primary[] = $name;
+ }
+ }
+
+ $options['foreignKeys'] = $table->getOption('foreignKeys', array());
+
+ if ($table->getAttribute(IPF_ORM::ATTR_EXPORT) & IPF_ORM::EXPORT_CONSTRAINTS) {
+ $constraints = array();
+
+ $emptyIntegrity = array('onUpdate' => null,
+ 'onDelete' => null);
+
+ foreach ($table->getRelations() as $name => $relation) {
+ $fk = $relation->toArray();
+ $fk['foreignTable'] = $relation->getTable()->getTableName();
+
+ if ($relation->getTable() === $table && in_array($relation->getLocal(), $primary)) {
+ if ($relation->hasConstraint()) {
+ throw new IPF_ORM_Exception("Badly constructed integrity constraints.");
+ }
+ continue;
+ }
+
+ $integrity = array('onUpdate' => $fk['onUpdate'],
+ 'onDelete' => $fk['onDelete']);
+
+ if ($relation instanceof IPF_ORM_Relation_LocalKey) {
+ $def = array('local' => $relation->getLocal(),
+ 'foreign' => $relation->getForeign(),
+ 'foreignTable' => $relation->getTable()->getTableName());
+
+ if (($key = array_search($def, $options['foreignKeys'])) === false) {
+ $options['foreignKeys'][] = $def;
+ $constraints[] = $integrity;
+ } else {
+ if ($integrity !== $emptyIntegrity) {
+ $constraints[$key] = $integrity;
+ }
+ }
+ }
+ }
+
+ foreach ($constraints as $k => $def) {
+ $options['foreignKeys'][$k] = array_merge($options['foreignKeys'][$k], $def);
+ }
+ }
+
+ $options['primary'] = $primary;
+
+ return array('tableName' => $table->getOption('tableName'),
+ 'columns' => $columns,
+ 'options' => array_merge($table->getOptions(), $options));
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Export_Mysql extends IPF_ORM_Export
+{
+ public function createTableSql($name, array $fields, array $options = array())
+ {
+ if ( ! $name)
+ throw new IPF_ORM_Exception('no valid table name specified');
+
+ if (empty($fields)) {
+ throw new IPF_ORM_Exception('no fields specified for table "'.$name.'"');
+ }
+ $queryFields = $this->getFieldDeclarationList($fields);
+
+ // build indexes for all foreign key fields (needed in MySQL!!)
+ if (isset($options['foreignKeys'])) {
+ foreach ($options['foreignKeys'] as $fk) {
+ $local = $fk['local'];
+ $found = false;
+ if (isset($options['indexes'])) {
+ foreach ($options['indexes'] as $definition) {
+ if (is_string($definition['fields'])) {
+ // Check if index already exists on the column
+ $found = ($local == $definition['fields']);
+ } else if (in_array($local, $definition['fields']) && count($definition['fields']) === 1) {
+ // Index already exists on the column
+ $found = true;
+ }
+ }
+ }
+ if (isset($options['primary']) && !empty($options['primary']) &&
+ in_array($local, $options['primary'])) {
+ // field is part of the PK and therefore already indexed
+ $found = true;
+ }
+
+ if ( ! $found) {
+ if (is_array($local)) {
+ foreach($local as $localidx) {
+ $options['indexes'][$localidx] = array('fields' => array($localidx => array()));
+ }
+ } else {
+ $options['indexes'][$local] = array('fields' => array($local => array()));
+ }
+ }
+ }
+ }
+
+ // add all indexes
+ if (isset($options['indexes']) && ! empty($options['indexes'])) {
+ foreach($options['indexes'] as $index => $definition) {
+ $queryFields .= ', ' . $this->getIndexDeclaration($index, $definition);
+ }
+ }
+
+ // attach all primary keys
+ if (isset($options['primary']) && ! empty($options['primary'])) {
+ $keyColumns = array_values($options['primary']);
+ $keyColumns = array_map(array($this->conn, 'quoteIdentifier'), $keyColumns);
+ $queryFields .= ', PRIMARY KEY(' . implode(', ', $keyColumns) . ')';
+ }
+
+ $query = 'CREATE TABLE ' . $this->conn->quoteIdentifier($name) . ' (' . $queryFields . ')';
+
+ $optionStrings = array();
+
+ if (isset($options['comment'])) {
+ $optionStrings['comment'] = 'COMMENT = ' . $this->conn->quote($options['comment'], 'text');
+ }
+ if (isset($options['charset'])) {
+ $optionStrings['charset'] = 'DEFAULT CHARACTER SET ' . $options['charset'];
+ }
+ if (isset($options['collate'])) {
+ $optionStrings['collate'] = 'COLLATE ' . $options['collate'];
+ }
+
+ $type = false;
+
+ // get the type of the table
+ if (isset($options['type'])) {
+ $type = $options['type'];
+ } else {
+ $type = $this->conn->getAttribute(IPF_ORM::ATTR_DEFAULT_TABLE_TYPE);
+ }
+
+ if ($type) {
+ $optionStrings[] = 'ENGINE = ' . $type;
+ }
+
+ if ( ! empty($optionStrings)) {
+ $query.= ' '.implode(' ', $optionStrings);
+ }
+ $sql[] = $query;
+
+ if (isset($options['foreignKeys'])) {
+
+ foreach ((array) $options['foreignKeys'] as $k => $definition) {
+ if (is_array($definition)) {
+ $sql[] = $this->createForeignKeySql($name, $definition);
+ }
+ }
+ }
+ return $sql;
+ }
+
+ public function getDeclaration($name, array $field)
+ {
+ $declaration = $this->conn->quoteIdentifier($name) . ' ';
+
+ if (!isset($field['type']))
+ throw new IPF_ORM_Exception('Missing column type.');
+
+ switch ($field['type']) {
+ case 'char':
+ $length = ( ! empty($field['length'])) ? $field['length'] : false;
+
+ $declaration .= $length ? 'CHAR('.$length.')' : 'CHAR(255)';
+ break;
+ case 'varchar':
+ case 'array':
+ case 'object':
+ case 'string':
+ case 'gzip':
+ if (!isset($field['length'])) {
+ if (array_key_exists('default', $field)) {
+ $field['length'] = $this->conn->varchar_max_length;
+ } else {
+ $field['length'] = false;
+ }
+ }
+
+ $length = ($field['length'] <= $this->conn->varchar_max_length) ? $field['length'] : false;
+ $fixed = (isset($field['fixed'])) ? $field['fixed'] : false;
+
+ $declaration .= $fixed ? ($length ? 'CHAR(' . $length . ')' : 'CHAR(255)')
+ : ($length ? 'VARCHAR(' . $length . ')' : 'TEXT');
+ break;
+ case 'clob':
+ if (!empty($field['length'])) {
+ $length = $field['length'];
+ if ($length <= 255) {
+ return 'TINYTEXT';
+ } elseif ($length <= 65532) {
+ return 'TEXT';
+ } elseif ($length <= 16777215) {
+ return 'MEDIUMTEXT';
+ }
+ }
+ $declaration .= 'LONGTEXT';
+ break;
+ case 'blob':
+ if ( ! empty($field['length'])) {
+ $length = $field['length'];
+ if ($length <= 255) {
+ return 'TINYBLOB';
+ } elseif ($length <= 65532) {
+ return 'BLOB';
+ } elseif ($length <= 16777215) {
+ return 'MEDIUMBLOB';
+ }
+ }
+ $declaration .= 'LONGBLOB';
+ break;
+ case 'enum':
+ if ($this->conn->getAttribute(IPF_ORM::ATTR_USE_NATIVE_ENUM)) {
+ $values = array();
+ foreach ($field['values'] as $value) {
+ $values[] = $this->conn->quote($value, 'varchar');
+ }
+ $declaration .= 'ENUM('.implode(', ', $values).')';
+ break;
+ }
+ // fall back to integer
+ case 'integer':
+ case 'int':
+ $type = 'INT';
+ if (!empty($field['length'])) {
+ $length = $field['length'];
+ if ($length <= 1) {
+ $type = 'TINYINT';
+ } elseif ($length == 2) {
+ $type = 'SMALLINT';
+ } elseif ($length == 3) {
+ $type = 'MEDIUMINT';
+ } elseif ($length == 4) {
+ $type = 'INT';
+ } elseif ($length > 4) {
+ $type = 'BIGINT';
+ }
+ }
+ $declaration .= $type;
+ if (isset($field['unsigned']) && $field['unsigned'])
+ $declaration .= ' UNSIGNED';
+ break;
+ case 'boolean':
+ $declaration .= 'TINYINT(1)';
+ break;
+ case 'date':
+ $declaration .= 'DATE';
+ break;
+ case 'time':
+ $declaration .= 'TIME';
+ break;
+ case 'datetime':
+ $declaration .= 'DATETIME';
+ break;
+ case 'timestamp':
+ $declaration .= 'TIMESTAMP';
+ break;
+ case 'float':
+ case 'double':
+ $declaration .= 'DOUBLE';
+ break;
+ case 'decimal':
+ $scale = !empty($field['scale']) ? $field['scale'] : $this->conn->getAttribute(IPF_ORM::ATTR_DECIMAL_PLACES);
+ if (!empty($field['length'])) {
+ $length = $field['length'];
+ if (is_array($length)) {
+ list($length, $scale) = $length;
+ }
+ } else {
+ $length = 18;
+ }
+ $declaration .= 'DECIMAL('.$length.','.$scale.')';
+ break;
+ case 'bit':
+ $declaration .= 'BIT';
+ break;
+ default:
+ throw new IPF_ORM_Exception('Unknown field type \'' . $field['type'] . '\'.');
+ }
+
+ if (isset($field['charset']) && $field['charset'])
+ $declaration .= ' CHARACTER SET ' . $field['charset'];
+
+ if (isset($field['collate']) && $field['collate'])
+ $declaration .= ' COLLATE ' . $field['collate'];
+
+ if (isset($field['notnull']) && $field['notnull'])
+ $declaration .= ' NOT NULL';
+
+ if (!empty($field['autoincrement'])) {
+ $declaration .= ' AUTO_INCREMENT';
+ } else {
+ $declaration .= $this->getDefaultFieldDeclaration($field);
+
+ if (isset($field['unique']) && $field['unique'])
+ $declaration .= ' UNIQUE';
+ }
+
+ if (isset($field['comment']) && $field['comment'])
+ $declaration .= ' COMMENT ' . $this->conn->quote($field['comment'], 'varchar');
+
+ return $declaration;
+ }
+
+ private function getDefaultFieldDeclaration($field)
+ {
+ $default = '';
+ if (isset($field['default']) && (!isset($field['length']) || $field['length'] <= 255)) {
+ if ($field['default'] === '') {
+ $field['default'] = empty($field['notnull']) ? null : $this->valid_default_values[$field['type']];
+ }
+
+ if ($field['default'] === '' && ($this->conn->getAttribute(IPF_ORM::ATTR_PORTABILITY) & IPF_ORM::PORTABILITY_EMPTY_TO_NULL))
+ $field['default'] = null;
+
+ if (is_null($field['default'])) {
+ $default = ' DEFAULT NULL';
+ } else {
+ if ($field['type'] === 'boolean') {
+ $fieldType = 'boolean';
+ $field['default'] = $this->conn->convertBooleans($field['default']);
+ } elseif ($field['type'] == 'enum' && $this->conn->getAttribute(IPF_ORM::ATTR_USE_NATIVE_ENUM)) {
+ $fieldType = 'varchar';
+ } else {
+ $fieldType = $field['type'];
+ }
+ $default = ' DEFAULT ' . $this->conn->quote($field['default'], $fieldType);
+ }
+ }
+ return $default;
+ }
+
+ public function alterTableSql($name, array $changes, $check = false)
+ {
+ if ( ! $name) {
+ throw new IPF_ORM_Exception('no valid table name specified');
+ }
+ foreach ($changes as $changeName => $change) {
+ switch ($changeName) {
+ case 'add':
+ case 'remove':
+ case 'change':
+ case 'rename':
+ case 'name':
+ break;
+ default:
+ throw new IPF_ORM_Exception('change type "' . $changeName . '" not yet supported');
+ }
+ }
+
+ if ($check) {
+ return true;
+ }
+
+ $query = '';
+ if ( ! empty($changes['name'])) {
+ $change_name = $this->conn->quoteIdentifier($changes['name']);
+ $query .= 'RENAME TO ' . $change_name;
+ }
+
+ if ( ! empty($changes['add']) && is_array($changes['add'])) {
+ foreach ($changes['add'] as $fieldName => $field) {
+ if ($query) {
+ $query.= ', ';
+ }
+ $query.= 'ADD ' . $this->getDeclaration($fieldName, $field);
+ }
+ }
+
+ if ( ! empty($changes['remove']) && is_array($changes['remove'])) {
+ foreach ($changes['remove'] as $fieldName => $field) {
+ if ($query) {
+ $query .= ', ';
+ }
+ $fieldName = $this->conn->quoteIdentifier($fieldName);
+ $query .= 'DROP ' . $fieldName;
+ }
+ }
+
+ $rename = array();
+ if ( ! empty($changes['rename']) && is_array($changes['rename'])) {
+ foreach ($changes['rename'] as $fieldName => $field) {
+ $rename[$field['name']] = $fieldName;
+ }
+ }
+
+ if ( ! empty($changes['change']) && is_array($changes['change'])) {
+ foreach ($changes['change'] as $fieldName => $field) {
+ if ($query) {
+ $query.= ', ';
+ }
+ if (isset($rename[$fieldName])) {
+ $oldFieldName = $rename[$fieldName];
+ unset($rename[$fieldName]);
+ } else {
+ $oldFieldName = $fieldName;
+ }
+ $oldFieldName = $this->conn->quoteIdentifier($oldFieldName);
+ $query .= 'CHANGE ' . $oldFieldName . ' '
+ . $this->getDeclaration($fieldName, $field['definition']);
+ }
+ }
+
+ if ( ! empty($rename) && is_array($rename)) {
+ foreach ($rename as $renameName => $renamedField) {
+ if ($query) {
+ $query.= ', ';
+ }
+ $field = $changes['rename'][$renamedField];
+ $renamedField = $this->conn->quoteIdentifier($renamedField);
+ $query .= 'CHANGE ' . $renamedField . ' '
+ . $this->getDeclaration($field['name'], $field['definition']);
+ }
+ }
+
+ if ( ! $query) {
+ return false;
+ }
+
+ $name = $this->conn->quoteIdentifier($name);
+
+ return 'ALTER TABLE ' . $name . ' ' . $query;
+ }
+
+ public function createIndexSql($table, $name, array $definition)
+ {
+ $name = $this->conn->quoteIdentifier($this->getIndexName($name));
+ $type = '';
+ if (isset($definition['type'])) {
+ switch (strtolower($definition['type'])) {
+ case 'fulltext':
+ case 'unique':
+ $type = strtoupper($definition['type']) . ' ';
+ break;
+ default:
+ throw new IPF_ORM_Exception('Unknown index type ' . $definition['type']);
+ }
+ }
+ $query = 'CREATE ' . $type . 'INDEX ' . $name . ' ON ' . $table;
+ $query .= ' (' . $this->getIndexFieldDeclarationList($definition['fields']) . ')';
+
+ return $query;
+ }
+
+ public function getIndexDeclaration($name, array $definition)
+ {
+ $name = $this->getIndexName($name);
+ $type = '';
+ if (isset($definition['type'])) {
+ switch (strtolower($definition['type'])) {
+ case 'fulltext':
+ case 'unique':
+ $type = strtoupper($definition['type']) . ' ';
+ break;
+ default:
+ throw new IPF_ORM_Exception('Unknown index type ' . $definition['type']);
+ }
+ }
+
+ if ( ! isset($definition['fields'])) {
+ throw new IPF_ORM_Exception('No index columns given.');
+ }
+ if ( ! is_array($definition['fields'])) {
+ $definition['fields'] = array($definition['fields']);
+ }
+
+ $query = $type . 'INDEX ' . $this->conn->quoteIdentifier($name);
+
+ $query .= ' (' . $this->getIndexFieldDeclarationList($definition['fields']) . ')';
+
+ return $query;
+ }
+
+ public function getIndexFieldDeclarationList(array $fields)
+ {
+ $declFields = array();
+
+ foreach ($fields as $fieldName => $field) {
+ $fieldString = $this->conn->quoteIdentifier($fieldName);
+
+ if (is_array($field)) {
+ if (isset($field['length'])) {
+ $fieldString .= '(' . $field['length'] . ')';
+ }
+
+ if (isset($field['sorting'])) {
+ $sort = strtoupper($field['sorting']);
+ switch ($sort) {
+ case 'ASC':
+ case 'DESC':
+ $fieldString .= ' ' . $sort;
+ break;
+ default:
+ throw new IPF_ORM_Exception('Unknown index sorting option given.');
+ }
+ }
+ } else {
+ $fieldString = $this->conn->quoteIdentifier($field);
+ }
+ $declFields[] = $fieldString;
+ }
+ return implode(', ', $declFields);
+ }
+
+ public function getAdvancedForeignKeyOptions(array $definition)
+ {
+ $query = '';
+ if ( ! empty($definition['match'])) {
+ $query .= ' MATCH ' . $definition['match'];
+ }
+ if ( ! empty($definition['onUpdate'])) {
+ $query .= ' ON UPDATE ' . $this->getForeignKeyReferentialAction($definition['onUpdate']);
+ }
+ if ( ! empty($definition['onDelete'])) {
+ $query .= ' ON DELETE ' . $this->getForeignKeyReferentialAction($definition['onDelete']);
+ }
+ return $query;
+ }
+
+ public function dropIndexSql($table, $name)
+ {
+ $table = $this->conn->quoteIdentifier($table);
+ $name = $this->conn->quoteIdentifier($this->getIndexName($name));
+ return 'DROP INDEX ' . $name . ' ON ' . $table;
+ }
+
+ public function dropTableSql($table)
+ {
+ $table = $this->conn->quoteIdentifier($table);
+ return 'DROP TABLE ' . $table;
+ }
+
+ public function dropForeignKey($table, $name)
+ {
+ $table = $this->conn->quoteIdentifier($table);
+ $name = $this->conn->quoteIdentifier($name);
+
+ return $this->conn->exec('ALTER TABLE ' . $table . ' DROP FOREIGN KEY ' . $name);
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Expression
+{
+ protected $_expression;
+ protected $_conn;
+ protected $_tokenizer;
+
+ public function __construct($expr, $conn = null)
+ {
+ $this->_tokenizer = new IPF_ORM_Query_Tokenizer();
+ $this->setExpression($expr);
+ if ($conn !== null) {
+ $this->_conn = $conn;
+ }
+ }
+
+ public function getConnection()
+ {
+ if ( ! isset($this->_conn)) {
+ return IPF_ORM_Manager::connection();
+ }
+
+ return $this->_conn;
+ }
+
+ public function setExpression($clause)
+ {
+ $this->_expression = $this->parseClause($clause);
+ }
+
+ public function parseExpression($expr)
+ {
+ $pos = strpos($expr, '(');
+ $quoted = (substr($expr, 0, 1) === "'" && substr($expr, -1) === "'");
+ if ($pos === false || $quoted) {
+ return $expr;
+ }
+
+ // get the name of the function
+ $name = substr($expr, 0, $pos);
+ $argStr = substr($expr, ($pos + 1), -1);
+
+ // parse args
+ foreach ($this->_tokenizer->bracketExplode($argStr, ',') as $arg) {
+ $args[] = $this->parseClause($arg);
+ }
+
+ return call_user_func_array(array($this->getConnection()->expression, $name), $args);
+ }
+
+ public function parseClause($clause)
+ {
+ $e = $this->_tokenizer->bracketExplode($clause, ' ');
+
+ foreach ($e as $k => $expr) {
+ $e[$k] = $this->parseExpression($expr);
+ }
+
+ return implode(' ', $e);
+ }
+
+ public function getSql()
+ {
+ return $this->_expression;
+ }
+
+ public function __toString()
+ {
+ return $this->getSql();
+ }
+}
--- /dev/null
+<?php
+
+class IPF_ORM_Expression_Driver extends IPF_ORM_Connection_Module
+{
+ public function getIdentifier($column)
+ {
+ return $column;
+ }
+ public function getIdentifiers($columns)
+ {
+ return $columns;
+ }
+
+ public function regexp()
+ {
+ throw new IPF_ORM_Exception('Regular expression operator is not supported by this database driver.');
+ }
+
+ public function avg($column)
+ {
+ $column = $this->getIdentifier($column);
+ return 'AVG(' . $column . ')';
+ }
+
+ public function count($column)
+ {
+ $column = $this->getIdentifier($column);
+ return 'COUNT(' . $column . ')';
+ }
+
+ public function max($column)
+ {
+ $column = $this->getIdentifier($column);
+ return 'MAX(' . $column . ')';
+ }
+
+ public function min($column)
+ {
+ $column = $this->getIdentifier($column);
+ return 'MIN(' . $column . ')';
+ }
+
+ public function sum($column)
+ {
+ $column = $this->getIdentifier($column);
+ return 'SUM(' . $column . ')';
+ }
+
+ public function md5($column)
+ {
+ $column = $this->getIdentifier($column);
+ return 'MD5(' . $column . ')';
+ }
+
+ public function length($column)
+ {
+ $column = $this->getIdentifier($column);
+ return 'LENGTH(' . $column . ')';
+ }
+
+ public function round($column, $decimals = 0)
+ {
+ $column = $this->getIdentifier($column);
+
+ return 'ROUND(' . $column . ', ' . $decimals . ')';
+ }
+
+ public function mod($expression1, $expression2)
+ {
+ $expression1 = $this->getIdentifier($expression1);
+ $expression2 = $this->getIdentifier($expression2);
+ return 'MOD(' . $expression1 . ', ' . $expression2 . ')';
+ }
+
+ public function trim($str)
+ {
+ return 'TRIM(' . $str . ')';
+ }
+
+ public function rtrim($str)
+ {
+ return 'RTRIM(' . $str . ')';
+ }
+
+ public function ltrim($str)
+ {
+ return 'LTRIM(' . $str . ')';
+ }
+
+ public function upper($str)
+ {
+ return 'UPPER(' . $str . ')';
+ }
+
+ public function lower($str)
+ {
+ return 'LOWER(' . $str . ')';
+ }
+
+ public function locate($str, $substr)
+ {
+ return 'LOCATE(' . $str . ', ' . $substr . ')';
+ }
+
+ public function now()
+ {
+ return 'NOW()';
+ }
+
+ public function coalesce($expression1, $expression2)
+ {
+ $expression1 = $this->getIdentifier($expression1);
+ $expression2 = $this->getIdentifier($expression2);
+ return 'COALESCE(' . $expression1 . ', ' . $expression2 . ')';
+ }
+
+ public function soundex($value)
+ {
+ throw new IPF_ORM_Exception('SQL soundex function not supported by this driver.');
+ }
+
+ public function substring($value, $from, $len = null)
+ {
+ $value = $this->getIdentifier($value);
+ if ($len === null)
+ return 'SUBSTRING(' . $value . ' FROM ' . $from . ')';
+ else {
+ $len = $this->getIdentifier($len);
+ return 'SUBSTRING(' . $value . ' FROM ' . $from . ' FOR ' . $len . ')';
+ }
+ }
+
+ public function concat()
+ {
+ $args = func_get_args();
+
+ return 'CONCAT(' . join(', ', (array) $args) . ')';
+ }
+
+ public function not($expression)
+ {
+ $expression = $this->getIdentifier($expression);
+ return 'NOT(' . $expression . ')';
+ }
+
+ private function basicMath($type, array $args)
+ {
+ $elements = $this->getIdentifiers($args);
+ if (count($elements) < 1) {
+ return '';
+ }
+ if (count($elements) == 1) {
+ return $elements[0];
+ } else {
+ return '(' . implode(' ' . $type . ' ', $elements) . ')';
+ }
+ }
+
+ public function add(array $args)
+ {
+ return $this->basicMath('+', $args);
+ }
+
+ public function sub(array $args)
+ {
+ return $this->basicMath('-', $args );
+ }
+
+ public function mul(array $args)
+ {
+ return $this->basicMath('*', $args);
+ }
+
+ public function div(array $args)
+ {
+ return $this->basicMath('/', $args);
+ }
+
+ public function eq($value1, $value2)
+ {
+ $value1 = $this->getIdentifier($value1);
+ $value2 = $this->getIdentifier($value2);
+ return $value1 . ' = ' . $value2;
+ }
+
+ public function neq($value1, $value2)
+ {
+ $value1 = $this->getIdentifier($value1);
+ $value2 = $this->getIdentifier($value2);
+ return $value1 . ' <> ' . $value2;
+ }
+
+ public function gt($value1, $value2)
+ {
+ $value1 = $this->getIdentifier($value1);
+ $value2 = $this->getIdentifier($value2);
+ return $value1 . ' > ' . $value2;
+ }
+
+ public function gte($value1, $value2)
+ {
+ $value1 = $this->getIdentifier($value1);
+ $value2 = $this->getIdentifier($value2);
+ return $value1 . ' >= ' . $value2;
+ }
+
+ public function lt($value1, $value2)
+ {
+ $value1 = $this->getIdentifier($value1);
+ $value2 = $this->getIdentifier($value2);
+ return $value1 . ' < ' . $value2;
+ }
+
+ public function lte($value1, $value2)
+ {
+ $value1 = $this->getIdentifier($value1);
+ $value2 = $this->getIdentifier($value2);
+ return $value1 . ' <= ' . $value2;
+ }
+
+ public function in($column, $values)
+ {
+ if ( ! is_array($values)) {
+ $values = array($values);
+ }
+ $values = $this->getIdentifiers($values);
+ $column = $this->getIdentifier($column);
+
+ if (count($values) == 0) {
+ throw new IPF_ORM_Exception('Values array for IN operator should not be empty.');
+ }
+ return $column . ' IN (' . implode(', ', $values) . ')';
+ }
+
+ public function isNull($expression)
+ {
+ $expression = $this->getIdentifier($expression);
+ return $expression . ' IS NULL';
+ }
+
+ public function isNotNull($expression)
+ {
+ $expression = $this->getIdentifier($expression);
+ return $expression . ' IS NOT NULL';
+ }
+
+ public function between($expression, $value1, $value2)
+ {
+ $expression = $this->getIdentifier($expression);
+ $value1 = $this->getIdentifier($value1);
+ $value2 = $this->getIdentifier($value2);
+ return $expression . ' BETWEEN ' .$value1 . ' AND ' . $value2;
+ }
+
+ public function guid()
+ {
+ throw new IPF_ORM_Exception('method not implemented');
+ }
+
+ public function acos($value)
+ {
+ return 'ACOS(' . $value . ')';
+ }
+
+ public function sin($value)
+ {
+ return 'SIN(' . $value . ')';
+ }
+
+ public function pi()
+ {
+ return 'PI()';
+ }
+
+ public function cos($value)
+ {
+ return 'COS(' . $value . ')';
+ }
+
+ public function __call($m, $a)
+ {
+ if ($this->conn->getAttribute(IPF_ORM::ATTR_PORTABILITY) & IPF_ORM::PORTABILITY_EXPR) {
+ throw new IPF_ORM_Exception('Unknown expression ' . $m);
+ }
+ return $m . '(' . implode(', ', $a) . ')';
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Expression_Mysql extends IPF_ORM_Expression_Driver
+{
+ public function regexp()
+ {
+ return 'RLIKE';
+ }
+
+ public function random()
+ {
+ return 'RAND()';
+ }
+
+ public function guid()
+ {
+ return 'UUID()';
+ }
+
+ public function year($column)
+ {
+ $column = $this->getIdentifier($column);
+ return 'YEAR(' . $column . ')';
+ }
+
+ public function month($column)
+ {
+ $column = $this->getIdentifier($column);
+ return 'MONTH(' . $column . ')';
+ }
+
+ public function monthname($column)
+ {
+ $column = $this->getIdentifier($column);
+ return 'MONTHNAME(' . $column . ')';
+ }
+
+ public function day($column)
+ {
+ $column = $this->getIdentifier($column);
+ return 'DAY(' . $column . ')';
+ }
+}
--- /dev/null
+<?php
+
+class IPF_ORM_Hydrator extends IPF_ORM_Hydrator_Abstract
+{
+ public function hydrateResultSet($stmt, $tableAliases)
+ {
+ $hydrationMode = $this->_hydrationMode;
+
+ $this->_tableAliases = $tableAliases;
+
+ if ($hydrationMode == IPF_ORM::HYDRATE_NONE) {
+ return $stmt->fetchAll(PDO::FETCH_NUM);
+ }
+
+ if ($hydrationMode == IPF_ORM::HYDRATE_ARRAY) {
+ $driver = new IPF_ORM_Hydrator_ArrayDriver();
+ } else {
+ $driver = new IPF_ORM_Hydrator_RecordDriver();
+ }
+
+ // Used variables during hydration
+ reset($this->_queryComponents);
+ $rootAlias = key($this->_queryComponents);
+ $rootComponentName = $this->_queryComponents[$rootAlias]['table']->getComponentName();
+ // if only one component is involved we can make our lives easier
+ $isSimpleQuery = count($this->_queryComponents) <= 1;
+ // Holds the resulting hydrated data structure
+ $result = array();
+ // Lookup map to quickly discover/lookup existing records in the result
+ $identifierMap = array();
+ // Holds for each component the last previously seen element in the result set
+ $prev = array();
+ // holds the values of the identifier/primary key fields of components,
+ // separated by a pipe '|' and grouped by component alias (r, u, i, ... whatever)
+ // the $idTemplate is a prepared template. $id is set to a fresh template when
+ // starting to process a row.
+ $id = array();
+ $idTemplate = array();
+
+ $result = $driver->getElementCollection($rootComponentName);
+
+ if ($stmt === false || $stmt === 0) {
+ return $result;
+ }
+
+ // Initialize
+ foreach ($this->_queryComponents as $dqlAlias => $data) {
+ $componentName = $data['table']->getComponentName();
+ $identifierMap[$dqlAlias] = array();
+ $prev[$dqlAlias] = null;
+ $idTemplate[$dqlAlias] = '';
+ }
+
+ $event = new IPF_ORM_Event(null, IPF_ORM_Event::HYDRATE, null);
+
+ // Process result set
+ $cache = array();
+ while ($data = $stmt->fetch(IPF_ORM::FETCH_ASSOC)) {
+ $id = $idTemplate; // initialize the id-memory
+ $nonemptyComponents = array();
+ $rowData = $this->_gatherRowData($data, $cache, $id, $nonemptyComponents);
+
+ //
+ // hydrate the data of the root component from the current row
+ //
+ $table = $this->_queryComponents[$rootAlias]['table'];
+ $componentName = $table->getComponentName();
+ // Ticket #1115 (getInvoker() should return the component that has addEventListener)
+ $event->setInvoker($table);
+ $event->set('data', $rowData[$rootAlias]);
+ $table->notifyRecordListeners('preHydrate', $event);
+
+ $index = false;
+
+ // Check for an existing element
+ if ($isSimpleQuery || ! isset($identifierMap[$rootAlias][$id[$rootAlias]])) {
+ $element = $driver->getElement($rowData[$rootAlias], $componentName);
+ $event->set('data', $element);
+ $table->notifyRecordListeners('postHydrate', $event);
+
+ // do we need to index by a custom field?
+ if ($field = $this->_getCustomIndexField($rootAlias)) {
+ if (isset($result[$field])) {
+ throw new IPF_ORM_Exception("Couldn't hydrate. Found non-unique key mapping.");
+ } else if ( ! isset($element[$field])) {
+ throw new IPF_ORM_Exception("Couldn't hydrate. Found a non-existent key.");
+ }
+ $result[$element[$field]] = $element;
+ } else {
+ $result[] = $element;
+ }
+
+ $identifierMap[$rootAlias][$id[$rootAlias]] = $driver->getLastKey($result);
+ } else {
+ $index = $identifierMap[$rootAlias][$id[$rootAlias]];
+ }
+
+ $this->_setLastElement($prev, $result, $index, $rootAlias, false);
+ unset($rowData[$rootAlias]);
+
+ // end hydrate data of the root component for the current row
+
+
+ // $prev[$rootAlias] now points to the last element in $result.
+ // now hydrate the rest of the data found in the current row, that belongs to other
+ // (related) components.
+ foreach ($rowData as $dqlAlias => $data) {
+ $index = false;
+ $map = $this->_queryComponents[$dqlAlias];
+ $table = $map['table'];
+ $componentName = $table->getComponentName();
+ $event->set('data', $data);
+ $table->notifyRecordListeners('preHydrate', $event);
+
+ $parent = $map['parent'];
+ $relation = $map['relation'];
+ $relationAlias = $map['relation']->getAlias();
+
+ $path = $parent . '.' . $dqlAlias;
+
+ if ( ! isset($prev[$parent])) {
+ unset($prev[$dqlAlias]); // Ticket #1228
+ continue;
+ }
+
+ // check the type of the relation
+ if ( ! $relation->isOneToOne() && $driver->initRelated($prev[$parent], $relationAlias)) {
+ $oneToOne = false;
+ // append element
+ if (isset($nonemptyComponents[$dqlAlias])) {
+ $indexExists = isset($identifierMap[$path][$id[$parent]][$id[$dqlAlias]]);
+ $index = $indexExists ? $identifierMap[$path][$id[$parent]][$id[$dqlAlias]] : false;
+ $indexIsValid = $index !== false ? isset($prev[$parent][$relationAlias][$index]) : false;
+ if ( ! $indexExists || ! $indexIsValid) {
+ $element = $driver->getElement($data, $componentName);
+ $event->set('data', $element);
+ $table->notifyRecordListeners('postHydrate', $event);
+
+ if ($field = $this->_getCustomIndexField($dqlAlias)) {
+ if (isset($prev[$parent][$relationAlias][$element[$field]])) {
+ throw new IPF_ORM_Exception("Couldn't hydrate. Found non-unique key mapping.");
+ } else if ( ! isset($element[$field])) {
+ throw new IPF_ORM_Exception("Couldn't hydrate. Found a non-existent key.");
+ }
+ $prev[$parent][$relationAlias][$element[$field]] = $element;
+ } else {
+ $prev[$parent][$relationAlias][] = $element;
+ }
+ $identifierMap[$path][$id[$parent]][$id[$dqlAlias]] = $driver->getLastKey($prev[$parent][$relationAlias]);
+ }
+ // register collection for later snapshots
+ $driver->registerCollection($prev[$parent][$relationAlias]);
+ }
+ } else {
+ // 1-1 relation
+ $oneToOne = true;
+
+ if ( ! isset($nonemptyComponents[$dqlAlias]) && ! isset($prev[$parent][$relationAlias])) {
+ $prev[$parent][$relationAlias] = $driver->getNullPointer();
+ } else if ( ! isset($prev[$parent][$relationAlias])) {
+ $element = $driver->getElement($data, $componentName);
+
+ // [FIX] Tickets #1205 and #1237
+ $event->set('data', $element);
+ $table->notifyRecordListeners('postHydrate', $event);
+
+ $prev[$parent][$relationAlias] = $element;
+ }
+ }
+
+ $coll =& $prev[$parent][$relationAlias];
+ $this->_setLastElement($prev, $coll, $index, $dqlAlias, $oneToOne);
+ }
+ }
+
+ $stmt->closeCursor();
+ $driver->flush();
+ //$e = microtime(true);
+ //echo 'Hydration took: ' . ($e - $s) . ' for '.count($result).' records<br />';
+
+ return $result;
+ }
+
+ protected function _setLastElement(&$prev, &$coll, $index, $dqlAlias, $oneToOne)
+ {
+ if (IPF_ORM_Null::isNull($coll) || $coll === null) {
+ unset($prev[$dqlAlias]); // Ticket #1228
+ return;
+ }
+
+ if ($index !== false) {
+ // Link element at $index to previous element for the component
+ // identified by the DQL alias $alias
+ $prev[$dqlAlias] =& $coll[$index];
+ return;
+ }
+
+ if (is_array($coll) && $coll) {
+ if ($oneToOne) {
+ $prev[$dqlAlias] =& $coll;
+ } else {
+ end($coll);
+ $prev[$dqlAlias] =& $coll[key($coll)];
+ }
+ } else if (count($coll) > 0) {
+ $prev[$dqlAlias] = $coll->getLast();
+ }
+ }
+
+ protected function _gatherRowData(&$data, &$cache, &$id, &$nonemptyComponents)
+ {
+ $rowData = array();
+
+ foreach ($data as $key => $value) {
+ // Parse each column name only once. Cache the results.
+ if ( ! isset($cache[$key])) {
+ // check ignored names. fastest solution for now. if we get more we'll start
+ // to introduce a list.
+ if ($key == 'IPF_ORM_ROWNUM') continue;
+ $e = explode('__', $key);
+ $last = strtolower(array_pop($e));
+ $cache[$key]['dqlAlias'] = $this->_tableAliases[strtolower(implode('__', $e))];
+ $table = $this->_queryComponents[$cache[$key]['dqlAlias']]['table'];
+ $fieldName = $table->getFieldName($last);
+ $cache[$key]['fieldName'] = $fieldName;
+ if ($table->isIdentifier($fieldName)) {
+ $cache[$key]['isIdentifier'] = true;
+ } else {
+ $cache[$key]['isIdentifier'] = false;
+ }
+ $type = $table->getTypeOfColumn($last);
+ if ($type == 'integer' || $type == 'string') {
+ $cache[$key]['isSimpleType'] = true;
+ } else {
+ $cache[$key]['type'] = $type;
+ $cache[$key]['isSimpleType'] = false;
+ }
+ }
+
+ $map = $this->_queryComponents[$cache[$key]['dqlAlias']];
+ $table = $map['table'];
+ $dqlAlias = $cache[$key]['dqlAlias'];
+ $fieldName = $cache[$key]['fieldName'];
+ if (isset($this->_queryComponents[$dqlAlias]['agg'][$fieldName])) {
+ $fieldName = $this->_queryComponents[$dqlAlias]['agg'][$fieldName];
+ }
+
+ if ($cache[$key]['isIdentifier']) {
+ $id[$dqlAlias] .= '|' . $value;
+ }
+
+ if ($cache[$key]['isSimpleType']) {
+ $rowData[$dqlAlias][$fieldName] = $value;
+ } else {
+ $rowData[$dqlAlias][$fieldName] = $table->prepareValue(
+ $fieldName, $value, $cache[$key]['type']);
+ }
+
+ if ( ! isset($nonemptyComponents[$dqlAlias]) && $value !== null) {
+ $nonemptyComponents[$dqlAlias] = true;
+ }
+ }
+
+ return $rowData;
+ }
+
+ protected function _getCustomIndexField($alias)
+ {
+ return isset($this->_queryComponents[$alias]['map']) ? $this->_queryComponents[$alias]['map'] : null;
+ }
+}
--- /dev/null
+<?php
+
+abstract class IPF_ORM_Hydrator_Abstract
+{
+ protected $_queryComponents = array();
+
+ protected $_hydrationMode = IPF_ORM::HYDRATE_RECORD;
+
+ public function __construct() {}
+
+ public function setHydrationMode($hydrationMode)
+ {
+ $this->_hydrationMode = $hydrationMode;
+ }
+
+ public function getHydrationMode()
+ {
+ return $this->_hydrationMode;
+ }
+
+ public function setQueryComponents(array $queryComponents)
+ {
+ $this->_queryComponents = $queryComponents;
+ }
+
+ public function getQueryComponents()
+ {
+ return $this->_queryComponents;
+ }
+
+ abstract public function hydrateResultSet($stmt, $tableAliases);
+}
--- /dev/null
+<?php
+
+class IPF_ORM_Hydrator_ArrayDriver
+{
+ public function getElementCollection($component)
+ {
+ return array();
+ }
+ public function getElement(array $data, $component)
+ {
+ return $data;
+ }
+
+ public function registerCollection($coll)
+ {
+ }
+
+ public function initRelated(array &$data, $name)
+ {
+ if ( ! isset($data[$name])) {
+ $data[$name] = array();
+ }
+ return true;
+ }
+
+ public function getNullPointer()
+ {
+ return null;
+ }
+
+ public function getLastKey(&$data)
+ {
+ end($data);
+ return key($data);
+ }
+
+ public function flush()
+ {
+ }
+}
--- /dev/null
+<?php
+
+class IPF_ORM_Hydrator_RecordDriver
+{
+ protected $_collections = array();
+ protected $_tables = array();
+ private $_initializedRelations = array();
+
+ public function getElementCollection($component)
+ {
+ $coll = new IPF_ORM_Collection($component);
+ $this->_collections[] = $coll;
+
+ return $coll;
+ }
+
+ public function getLastKey($coll)
+ {
+ $coll->end();
+
+ return $coll->key();
+ }
+
+ public function initRelated(IPF_ORM_Record $record, $name)
+ {
+ if ( ! isset($this->_initializedRelations[$record->getOid()][$name])) {
+ $relation = $record->getTable()->getRelation($name);
+ $coll = new IPF_ORM_Collection($relation->getTable()->getComponentName());
+ $coll->setReference($record, $relation);
+ $record[$name] = $coll;
+ $this->_initializedRelations[$record->getOid()][$name] = true;
+ }
+ return true;
+ }
+
+ public function registerCollection(IPF_ORM_Collection $coll)
+ {
+ $this->_collections[] = $coll;
+ }
+
+ public function getNullPointer()
+ {
+ return IPF_ORM_Null::getInstance();
+ }
+
+ public function getElement(array $data, $component)
+ {
+ $component = $this->_getClassNameToReturn($data, $component);
+ if ( ! isset($this->_tables[$component])) {
+ $this->_tables[$component] = IPF_ORM::getTable($component);
+ $this->_tables[$component]->setAttribute(IPF_ORM::ATTR_LOAD_REFERENCES, false);
+ }
+
+ $this->_tables[$component]->setData($data);
+ $record = $this->_tables[$component]->getRecord();
+
+ return $record;
+ }
+
+ public function flush()
+ {
+ // take snapshots from all initialized collections
+ foreach ($this->_collections as $key => $coll) {
+ $coll->takeSnapshot();
+ }
+ foreach ($this->_tables as $table) {
+ $table->setAttribute(IPF_ORM::ATTR_LOAD_REFERENCES, true);
+ }
+ $this->_initializedRelations = null;
+ $this->_collections = null;
+ $this->_tables = null;
+ }
+
+ protected function _getClassNameToReturn(array &$data, $component)
+ {
+ if ( ! isset($this->_tables[$component])) {
+ $this->_tables[$component] = IPF_ORM::getTable($component);
+ $this->_tables[$component]->setAttribute(IPF_ORM::ATTR_LOAD_REFERENCES, false);
+ }
+
+ if ( ! ($subclasses = $this->_tables[$component]->getOption('subclasses'))) {
+ return $component;
+ }
+
+ foreach ($subclasses as $subclass) {
+ $table = IPF_ORM::getTable($subclass);
+ $inheritanceMap = $table->getOption('inheritanceMap');
+ list($key, $value) = each($inheritanceMap);
+ if ( ! isset($data[$key]) || $data[$key] != $value) {
+ continue;
+ } else {
+ return $table->getComponentName();
+ }
+ }
+ return $component;
+ }
+}
--- /dev/null
+<?php
+
+class IPF_ORM_Import_Builder
+{
+ private $_baseClassPrefix = 'Base';
+ private $_baseClassesDirectory = IPF_ORM::BASE_CLASSES_DIRECTORY;
+
+ private static function varExport($var)
+ {
+ $export = var_export($var, true);
+ $export = preg_replace('#\s*\(\s*#', '(', $export);
+ $export = preg_replace('#[\s,]+\)#', ')', $export);
+ $export = preg_replace('#\s+#', ' ', $export);
+ return $export;
+ }
+
+ private static function varExportList($list)
+ {
+ $export = array();
+ foreach ($list as $var) {
+ $export[] = self::varExport($var);
+ }
+ return 'array('.implode(', ', $export).')';
+ }
+
+ private function buildTableDefinition(array $definition)
+ {
+ if (isset($definition['inheritance']['type']) && ($definition['inheritance']['type'] == 'simple' || $definition['inheritance']['type'] == 'column_aggregation')) {
+ return;
+ }
+
+ $ret = array(
+ ' public static function setTableDefinition(IPF_ORM_Table $table)',
+ ' {',
+ );
+
+ if (isset($definition['inheritance']['type']) && $definition['inheritance']['type'] == 'concrete')
+ $ret[] = ' parent::setTableDefinition($table);';
+
+ if (isset($definition['tableName']) && !empty($definition['tableName']))
+ $ret[] = " ".'$table->setTableName(\''. $definition['tableName'].'\');';
+
+ if (isset($definition['columns']) && is_array($definition['columns']) && !empty($definition['columns']))
+ $ret[] = $this->buildColumns($definition['columns']);
+
+ if (isset($definition['ordering']) && is_array($definition['ordering']) && !empty($definition['ordering']))
+ $ret[] = ' $table->setOrdering('.self::varExportList($definition['ordering']).');';
+
+ if (isset($definition['indexes']) && is_array($definition['indexes']) && !empty($definition['indexes']))
+ foreach ($definition['indexes'] as $indexName => $definitions)
+ $ret[] = " \$table->addIndex('" . $indexName . "', " . self::varExport($definitions) . ');';
+
+ if (isset($definition['attributes']) && is_array($definition['attributes']) && !empty($definition['attributes']))
+ $ret[] = $this->buildAttributes($definition['attributes']);
+
+ if (isset($definition['options']) && is_array($definition['options']) && !empty($definition['options']))
+ $ret[] = $this->buildOptions($definition['options']);
+
+ if (isset($definition['inheritance']['subclasses']) && !empty($definition['inheritance']['subclasses'])) {
+ $ret[] = " ".'$table->setSubClasses('. self::varExport($definition['inheritance']['subclasses']).');';
+ }
+
+ $ret[] = ' }';
+
+ return implode(PHP_EOL, $ret);
+ }
+
+ private function buildSetUp(array $definition)
+ {
+ $ret = array();
+
+ if (isset($definition['relations']) && is_array($definition['relations']) && !empty($definition['relations'])) {
+ foreach ($definition['relations'] as $name => $relation) {
+ $class = isset($relation['class']) ? $relation['class'] : $name;
+ $alias = (isset($relation['alias']) && $relation['alias'] !== $relation['class']) ? $relation['alias'] : '';
+
+ if (!isset($relation['type']))
+ $relation['type'] = IPF_ORM_Relation::ONE;
+
+ if ($relation['type'] === IPF_ORM_Relation::ONE ||
+ $relation['type'] === IPF_ORM_Relation::ONE_COMPOSITE) {
+ $r = " \$table->hasOne('$class', '$alias'";
+ } else {
+ $r = " \$table->hasMany('$class', '$alias'";
+ }
+
+ $a = array();
+
+ if (isset($relation['refClass'])) {
+ $a[] = '\'refClass\' => ' . self::varExport($relation['refClass']);
+ }
+
+ if (isset($relation['deferred']) && $relation['deferred']) {
+ $a[] = '\'default\' => ' . self::varExport($relation['deferred']);
+ }
+
+ if (isset($relation['local']) && $relation['local']) {
+ $a[] = '\'local\' => ' . self::varExport($relation['local']);
+ }
+
+ if (isset($relation['foreign']) && $relation['foreign']) {
+ $a[] = '\'foreign\' => ' . self::varExport($relation['foreign']);
+ }
+
+ if (isset($relation['onDelete']) && $relation['onDelete']) {
+ $a[] = '\'onDelete\' => ' . self::varExport($relation['onDelete']);
+ }
+
+ if (isset($relation['onUpdate']) && $relation['onUpdate']) {
+ $a[] = '\'onUpdate\' => ' . self::varExport($relation['onUpdate']);
+ }
+
+ if (isset($relation['equal']) && $relation['equal']) {
+ $a[] = '\'equal\' => ' . self::varExport($relation['equal']);
+ }
+
+ if (isset($relation['owningSide']) && $relation['owningSide']) {
+ $a[] = '\'owningSide\' => ' . self::varExport($relation['owningSide']);
+ }
+
+ if (isset($relation['exclude']) && $relation['exclude']) {
+ $a[] = '\'exclude\' => ' . self::varExport($relation['exclude']);
+ }
+
+ if (!empty($a))
+ $r .= ', array(' . implode(', ', $a) . ')';
+
+ $ret[] = $r.');';
+ }
+ }
+
+ if (isset($definition['templates']) && is_array($definition['templates']) && !empty($definition['templates'])) {
+ $this->buildTemplates($definition['templates'], $ret);
+ }
+
+ if (isset($definition['actAs']) && is_array($definition['actAs']) && !empty($definition['actAs'])) {
+ $this->buildActAs($definition['actAs'], $ret);
+ }
+
+ if (isset($definition['listeners']) && is_array($definition['listeners']) && !empty($definition['listeners'])) {
+ foreach ($definition['listeners'] as $listener) {
+ $ret[] = " \$table->listeners['$listener'] = new $listener;";
+ }
+ }
+
+ // If the body of the function has contents and we are using inheritance
+ // then we need call the parent::setUp() before the body of the function
+ // Class table inheritance is the only one we shouldn't call parent::setUp() for
+ if (count($ret) && isset($definition['inheritance']['type']) && $definition['inheritance']['type'] != 'class_table') {
+ array_unshift($ret, ' parent::setUp($table);');
+ }
+
+ // If we have some code for the function then lets define it and return it
+ if (count($ret)) {
+ array_unshift($ret,
+ ' public static function setUp(IPF_ORM_Table $table)',
+ ' {'
+ );
+ $ret[] = ' }';
+ }
+ return $ret;
+ }
+
+ private function buildColumns(array $columns)
+ {
+ $result = array();
+ foreach ($columns as $name => $column) {
+ $columnName = isset($column['name']) ? $column['name']:$name;
+ $build = " ".'$table->setColumn(\'' . $columnName . '\', \'' . $column['type'] . '\'';
+
+ if ($column['length']) {
+ if (is_numeric($column['length']))
+ $build .= ', ' . $column['length'];
+ else
+ $build .= ', array(' . $column['length'] . ')';
+ } else {
+ $build .= ', null';
+ }
+
+ $options = $column;
+
+ // Remove name, alltypes, ntype. They are not needed in options array
+ unset($options['name']);
+ unset($options['alltypes']);
+ unset($options['ntype']);
+
+ // Remove notnull => true if the column is primary
+ // Primary columns are implied to be notnull in IPF_ORM
+ if (isset($options['primary']) && $options['primary'] == true && (isset($options['notnull']) && $options['notnull'] == true)) {
+ unset($options['notnull']);
+ }
+
+ // Remove default if the value is 0 and the column is a primary key
+ // IPF_ORM defaults to 0 if it is a primary key
+ if (isset($options['primary']) && $options['primary'] == true && (isset($options['default']) && $options['default'] == 0)) {
+ unset($options['default']);
+ }
+
+ // These can be removed if they are empty. They all default to a false/0/null value anyways
+ $remove = array('fixed', 'primary', 'notnull', 'autoincrement', 'unsigned');
+ foreach ($remove as $key) {
+ if (isset($options[$key]) && empty($options[$key])) {
+ unset($options[$key]);
+ }
+ }
+
+ // Remove null and empty array values
+ foreach ($options as $key => $value) {
+ if (is_null($value) || (is_array($value) && empty($value))) {
+ unset($options[$key]);
+ }
+ }
+
+ if (is_array($options) && !empty($options)) {
+ $build .= ', ' . self::varExport($options);
+ }
+
+ $build .= ');';
+
+ $result[] = $build;
+ }
+
+ return implode(PHP_EOL, $result);
+ }
+
+ private function buildTemplates(array $templates, array &$build)
+ {
+ foreach ($templates as $name => $options) {
+ if (is_array($options) && !empty($options)) {
+ $build[] = " \$table->addTemplate('$name', " . self::varExport($options) . ");";
+ } elseif (isset($templates[0])) {
+ $build[] = " \$table->addTemplate('$options');";
+ } else {
+ $build[] = " \$table->addTemplate('$name');";
+ }
+ }
+ }
+
+ private function buildActAs($actAs, array &$build)
+ {
+ // rewrite special case of actAs: [Behavior] which gave [0] => Behavior
+ if (is_array($actAs) && isset($actAs[0]) && !is_array($actAs[0])) {
+ $actAs = array_flip($actAs);
+ }
+
+ // rewrite special case of actAs: Behavior
+ if (!is_array($actAs))
+ $actAs = array($actAs => '');
+
+ foreach ($actAs as $template => $options) {
+ // find class matching $name
+ if (class_exists('IPF_ORM_Template_'.$template, true))
+ $classname = 'IPF_ORM_Template_'.$template;
+ else
+ $classname = $template;
+
+ if (is_array($options))
+ $options = self::varExport($options);
+ else
+ $options = '';
+
+ $build[] = " \$table->addTemplate(new $classname($options));";
+ }
+ }
+
+ private function buildAttributes(array $attributes)
+ {
+ $build = PHP_EOL;
+ foreach ($attributes as $key => $value) {
+
+ if (is_bool($value)) {
+ $values = $value ? 'true':'false';
+ } else {
+ if (!is_array($value))
+ $value = array($value);
+
+ $values = '';
+ foreach ($value as $attr) {
+ $values .= "IPF_ORM::" . strtoupper($key) . "_" . strtoupper($attr) . ' ^ ';
+ }
+
+ // Trim last ^
+ $values = substr($values, 0, strlen($values) - 3);
+ }
+
+ $build .= " \$table->setAttribute(IPF_ORM::ATTR_" . strtoupper($key) . ", " . $values . ");" . PHP_EOL;
+ }
+
+ return $build;
+ }
+
+ private function buildOptions(array $options)
+ {
+ $build = '';
+ foreach ($options as $name => $value) {
+ $build .= " \$table->setOption('$name', " . self::varExport($value) . ");" . PHP_EOL;
+ }
+ return $build;
+ }
+
+ public function buildRecord(array $definition, $targetPath)
+ {
+ if (!isset($definition['className']))
+ throw new IPF_ORM_Exception('Missing class name.');
+
+ $this->writeBaseDefinition($definition, $targetPath);
+ $this->writeModelDefinition($definition, $targetPath);
+ }
+
+ private function writeBaseDefinition(array $definition, $targetPath)
+ {
+ $code = array(
+ '<?php',
+ '',
+ '/**',
+ ' * This class has been auto-generated by the IPF_ORM Framework.',
+ ' * Changes to this file may cause incorrect behavior',
+ ' * and will be lost if the code is regenerated.',
+ ' */',
+ '',
+ );
+
+ $code[] = 'abstract class '.$this->_baseClassPrefix.$definition['className'].' extends IPF_ORM_Record';
+ $code[] = '{';
+ $code[] = $this->buildTableDefinition($definition);
+ $code[] = '';
+ $code = array_merge($code, $this->buildSetUp($definition));
+ $code[] = '';
+ $code = array_merge($code, $this->buildShortcuts($definition));
+ $code[] = '}';
+
+ $fileName = $this->_baseClassPrefix . $definition['className'] . '.php';
+ $writePath = $targetPath . DIRECTORY_SEPARATOR . $this->_baseClassesDirectory;
+ IPF_Utils::makeDirectories($writePath);
+ $writePath .= DIRECTORY_SEPARATOR . $fileName;
+
+ if (file_put_contents($writePath, implode(PHP_EOL, $code)) === false)
+ throw new IPF_ORM_Exception("Couldn't write file " . $writePath);
+ }
+
+ private function buildShortcuts(array $definition)
+ {
+ return array(
+ ' public static function table()',
+ ' {',
+ ' return IPF_ORM::getTable(\''.$definition['className'].'\');',
+ ' }',
+ '',
+ ' public static function query($alias=\'\')',
+ ' {',
+ ' return IPF_ORM::getTable(\''.$definition['className'].'\')->createQuery($alias);',
+ ' }',
+ );
+ }
+
+ private function writeModelDefinition(array $definition, $targetPath)
+ {
+ $className = $definition['className'];
+ $adminClassName = $className.'Admin';
+
+ $writePath = $targetPath . DIRECTORY_SEPARATOR . $className . '.php';
+ if (file_exists($writePath))
+ return;
+
+ $code = array(
+ '<?php',
+ '',
+ sprintf('class %s extends %s%s', $className, $this->_baseClassPrefix, $className),
+ '{',
+ '}',
+ '',
+ '/*',
+ 'class '.$adminClassName.' extends IPF_Admin_Model',
+ '{',
+ '}',
+ '',
+ '// Add following line to your admin.php to enable admin interface for this model',
+ "new $adminClassName('$className'),",
+ '*/',
+ '',
+ );
+
+ IPF_Utils::makeDirectories($targetPath);
+ if (file_put_contents($writePath, implode(PHP_EOL, $code)) === false)
+ throw new IPF_ORM_Exception("Couldn't write file " . $writePath);
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Import_Schema
+{
+ protected $_relations = array();
+
+ protected $_validation = array(
+ 'root' => array(
+ 'abstract',
+ 'connection',
+ 'className',
+ 'tableName',
+ 'connection',
+ 'relations',
+ 'columns',
+ 'indexes',
+ 'attributes',
+ 'templates',
+ 'actAs',
+ 'options',
+ 'inheritance',
+ 'detect_relations',
+ 'listeners',
+ 'ordering',
+ ),
+ 'column' => array(
+ 'name',
+ 'format',
+ 'fixed',
+ 'primary',
+ 'autoincrement',
+ 'type',
+ 'length',
+ 'size',
+ 'default',
+ 'scale',
+ 'values',
+ 'comment',
+ 'protected',
+ 'zerofill',
+ 'owner',
+ 'exclude',
+ 'collate',
+ 'charset',
+ ),
+ 'relation' => array(
+ 'key',
+ 'class',
+ 'alias',
+ 'type',
+ 'refClass',
+ 'local',
+ 'exclude',
+ 'foreign',
+ 'foreignClass',
+ 'foreignAlias',
+ 'foreignType',
+ 'foreignExclude',
+ 'autoComplete',
+ 'onDelete',
+ 'onUpdate',
+ 'equal',
+ 'owningSide',
+ ),
+ 'inheritance' => array(
+ 'type',
+ 'extends',
+ 'keyField',
+ 'keyValue',
+ ),
+ );
+
+ protected $_validators = array();
+
+ public function getValidators()
+ {
+ if (empty($this->_validators)) {
+ $this->_validators = IPF_ORM_Utils::getValidators();
+ }
+ return $this->_validators;
+ }
+
+ public function importSchema($filename, $extraAllwedReferences)
+ {
+ $filename = (array)$filename;
+ $array = array();
+ foreach ($filename as $fn) {
+ $definitions = $this->parseSchema($fn);
+ foreach ($definitions as &$definition)
+ $definition['input_file'] = $fn;
+ $array = array_merge($array, $definitions);
+ }
+
+ $array = $this->_buildRelationships($array, $extraAllwedReferences);
+ $array = $this->_processInheritance($array);
+ return $array;
+ }
+
+ private function parseSchema($schema)
+ {
+ $defaults = array('abstract' => false,
+ 'className' => null,
+ 'tableName' => null,
+ 'connection' => null,
+ 'relations' => array(),
+ 'indexes' => array(),
+ 'attributes' => array(),
+ 'templates' => array(),
+ 'actAs' => array(),
+ 'options' => array(),
+ 'inheritance' => array(),
+ 'detect_relations' => false);
+
+ $array = Spyc::YAMLLoad($schema);
+
+ // Go through the schema and look for global values so we can assign them to each table/class
+ $globals = array();
+ $globalKeys = array('connection',
+ 'attributes',
+ 'templates',
+ 'actAs',
+ 'options',
+ 'inheritance',
+ 'detect_relations');
+
+ // Loop over and build up all the global values and remove them from the array
+ foreach ($array as $key => $value) {
+ if (in_array($key, $globalKeys)) {
+ unset($array[$key]);
+ $globals[$key] = $value;
+ }
+ }
+
+ // Apply the globals to each table if it does not have a custom value set already
+ foreach ($array as $className => $table) {
+ foreach ($globals as $key => $value) {
+ if (!isset($array[$className][$key])) {
+ $array[$className][$key] = $value;
+ }
+ }
+ }
+
+ $build = array();
+
+ foreach ($array as $className => $table) {
+ $this->_validateSchemaElement('root', array_keys($table), $className);
+
+ $columns = array();
+
+ $className = isset($table['className']) ? (string) $table['className']:(string) $className;
+
+ if (isset($table['inheritance']['keyField']) || isset($table['inheritance']['keyValue'])) {
+ $table['inheritance']['type'] = 'column_aggregation';
+ }
+
+ if (isset($table['tableName']) && $table['tableName']) {
+ $tableName = $table['tableName'];
+ } else {
+ if (isset($table['inheritance']['type']) && ($table['inheritance']['type'] == 'column_aggregation')) {
+ $tableName = null;
+ } else {
+ $tableName = IPF_ORM_Inflector::tableize($className);
+ }
+ }
+
+ $connection = isset($table['connection']) ? $table['connection']:'current';
+
+ $columns = isset($table['columns']) ? $table['columns']:array();
+
+ if ( ! empty($columns)) {
+ foreach ($columns as $columnName => $field) {
+
+ // Support short syntax: my_column: integer(4)
+ if ( ! is_array($field)) {
+ $original = $field;
+ $field = array();
+ $field['type'] = $original;
+ }
+
+ $colDesc = array();
+ if (isset($field['name'])) {
+ $colDesc['name'] = $field['name'];
+ } else {
+ $colDesc['name'] = $columnName;
+ }
+
+ $this->_validateSchemaElement('column', array_keys($field), $className . '->columns->' . $colDesc['name']);
+
+ // Support short type(length) syntax: my_column: { type: integer(4) }
+ $e = explode('(', $field['type']);
+ if (isset($e[0]) && isset($e[1])) {
+ $colDesc['type'] = $e[0];
+ $colDesc['length'] = substr($e[1], 0, strlen($e[1]) - 1);
+ } else {
+ $colDesc['type'] = isset($field['type']) ? (string) $field['type']:null;
+ $colDesc['length'] = isset($field['length']) ? (int) $field['length']:null;
+ $colDesc['length'] = isset($field['size']) ? (int) $field['size']:$colDesc['length'];
+ }
+
+ $colDesc['fixed'] = isset($field['fixed']) ? (int) $field['fixed']:null;
+ $colDesc['primary'] = isset($field['primary']) ? (bool) (isset($field['primary']) && $field['primary']):null;
+ $colDesc['default'] = isset($field['default']) ? $field['default']:null;
+ $colDesc['autoincrement'] = isset($field['autoincrement']) ? (bool) (isset($field['autoincrement']) && $field['autoincrement']):null;
+ $colDesc['values'] = isset($field['values']) ? (array) $field['values']:null;
+
+ // Include all the specified and valid validators in the colDesc
+ $validators = $this->getValidators();
+
+ foreach ($validators as $validator) {
+ if (isset($field[$validator])) {
+ $colDesc[$validator] = $field[$validator];
+ }
+ }
+
+ $columns[(string) $columnName] = $colDesc;
+ }
+ }
+
+ // Apply the default values
+ foreach ($defaults as $key => $defaultValue) {
+ if (isset($table[$key]) && ! isset($build[$className][$key])) {
+ $build[$className][$key] = $table[$key];
+ } else {
+ $build[$className][$key] = isset($build[$className][$key]) ? $build[$className][$key]:$defaultValue;
+ }
+ }
+
+ $build[$className]['className'] = $className;
+ $build[$className]['tableName'] = $tableName;
+ $build[$className]['columns'] = $columns;
+
+ // Make sure that anything else that is specified in the schema makes it to the final array
+ $build[$className] = IPF_ORM_Utils::arrayDeepMerge($table, $build[$className]);
+
+ // We need to keep track of the className for the connection
+ $build[$className]['connectionClassName'] = $build[$className]['className'];
+ }
+
+ return $build;
+ }
+
+ protected function _processInheritance($array)
+ {
+ // Apply default inheritance configuration
+ foreach ($array as $className => $definition) {
+ if ( ! empty($array[$className]['inheritance'])) {
+ $this->_validateSchemaElement('inheritance', array_keys($definition['inheritance']), $className . '->inheritance');
+
+ // Default inheritance to concrete inheritance
+ if ( ! isset($array[$className]['inheritance']['type'])) {
+ $array[$className]['inheritance']['type'] = 'concrete';
+ }
+
+ // Some magic for setting up the keyField and keyValue column aggregation options
+ // Adds keyField to the parent class automatically
+ if ($array[$className]['inheritance']['type'] == 'column_aggregation') {
+ // Set the keyField to 'type' by default
+ if ( ! isset($array[$className]['inheritance']['keyField'])) {
+ $array[$className]['inheritance']['keyField'] = 'type';
+ }
+
+ // Set the keyValue to the name of the child class if it does not exist
+ if ( ! isset($array[$className]['inheritance']['keyValue'])) {
+ $array[$className]['inheritance']['keyValue'] = $className;
+ }
+
+ // Add the keyType column to the parent if a definition does not already exist
+ if ( ! isset($array[$array[$className]['inheritance']['extends']]['columns'][$array[$className]['inheritance']['keyField']])) {
+ $array[$definition['inheritance']['extends']]['columns'][$array[$className]['inheritance']['keyField']] = array('name' => $array[$className]['inheritance']['keyField'], 'type' => 'string', 'length' => 255);
+ }
+ }
+ }
+ }
+
+ // Array of the array keys to move to the parent, and the value to default the child definition to
+ // after moving it. Will also populate the subclasses array for the inheritance parent
+ $moves = array('columns' => array());
+
+ foreach ($array as $className => $definition) {
+ if (!isset($definition['className']))
+ continue;
+ $parent = $this->_findBaseSuperClass($array, $definition['className']);
+ // Move any definitions on the schema to the parent
+ if (isset($definition['inheritance']['extends']) && isset($definition['inheritance']['type']) && ($definition['inheritance']['type'] == 'simple' || $definition['inheritance']['type'] == 'column_aggregation')) {
+ foreach ($moves as $move => $resetValue) {
+ $array[$parent][$move] = IPF_ORM_Utils::arrayDeepMerge($array[$parent][$move], $definition[$move]);
+ $array[$definition['className']][$move] = $resetValue;
+ }
+
+ // Populate the parents subclasses
+ if ($definition['inheritance']['type'] == 'column_aggregation') {
+ $array[$parent]['inheritance']['subclasses'][$definition['className']] = array($definition['inheritance']['keyField'] => $definition['inheritance']['keyValue']);
+ }
+ }
+ }
+
+ return $array;
+ }
+
+ protected function _findBaseSuperClass($array, $class)
+ {
+ if (isset($array[$class]['inheritance']['extends'])) {
+ return $this->_findBaseSuperClass($array, $array[$class]['inheritance']['extends']);
+ } else {
+ return $class;
+ }
+ }
+
+ protected function _buildRelationships($array, $extraAllwedReferences)
+ {
+ // Handle auto detecting relations by the names of columns
+ // User.contact_id will automatically create User hasOne Contact local => contact_id, foreign => id
+ foreach ($array as $className => $properties) {
+ if (isset($properties['columns']) && ! empty($properties['columns']) && isset($properties['detect_relations']) && $properties['detect_relations']) {
+ foreach ($properties['columns'] as $column) {
+ // Check if the column we are inflecting has a _id on the end of it before trying to inflect it and find
+ // the class name for the column
+ if (strpos($column['name'], '_id')) {
+ $columnClassName = IPF_ORM_Inflector::classify(str_replace('_id', '', $column['name']));
+ if (isset($array[$columnClassName]) && !isset($array[$className]['relations'][$columnClassName])) {
+ $array[$className]['relations'][$columnClassName] = array();
+
+ // Set the detected foreign key type and length to the same as the primary key
+ // of the related table
+ $type = isset($array[$columnClassName]['columns']['id']['type']) ? $array[$columnClassName]['columns']['id']['type']:'integer';
+ $length = isset($array[$columnClassName]['columns']['id']['length']) ? $array[$columnClassName]['columns']['id']['length']:8;
+ $array[$className]['columns'][$column['name']]['type'] = $type;
+ $array[$className]['columns'][$column['name']]['length'] = $length;
+ }
+ }
+ }
+ }
+ }
+
+ foreach ($array as $name => $properties) {
+ if (!isset($properties['relations'])) {
+ continue;
+ }
+
+ $className = $properties['className'];
+ $relations = $properties['relations'];
+
+ foreach ($relations as $alias => $relation) {
+ $class = isset($relation['class']) ? $relation['class'] : $alias;
+ if (!isset($array[$class]) && !in_array($class, $extraAllwedReferences)) {
+ print "Warning: Ignoring relation to unknown model '$class' in model '$className'. \n";
+ continue;
+ }
+ $relation['class'] = $class;
+ $relation['alias'] = isset($relation['alias']) ? $relation['alias'] : $alias;
+
+ // Attempt to guess the local and foreign
+ if (isset($relation['refClass'])) {
+ $relation['local'] = isset($relation['local']) ? $relation['local']:IPF_ORM_Inflector::tableize($name) . '_id';
+ $relation['foreign'] = isset($relation['foreign']) ? $relation['foreign']:IPF_ORM_Inflector::tableize($class) . '_id';
+ } else {
+ $relation['local'] = isset($relation['local']) ? $relation['local']:IPF_ORM_Inflector::tableize($relation['class']) . '_id';
+ $relation['foreign'] = isset($relation['foreign']) ? $relation['foreign']:'id';
+ }
+
+ if (isset($relation['refClass'])) {
+ $relation['type'] = 'many';
+ }
+
+ if (isset($relation['type']) && $relation['type']) {
+ $relation['type'] = $relation['type'] === 'one' ? IPF_ORM_Relation::ONE:IPF_ORM_Relation::MANY;
+ } else {
+ $relation['type'] = IPF_ORM_Relation::ONE;
+ }
+
+ if (isset($relation['foreignType']) && $relation['foreignType']) {
+ $relation['foreignType'] = $relation['foreignType'] === 'one' ? IPF_ORM_Relation::ONE:IPF_ORM_Relation::MANY;
+ }
+
+ $relation['key'] = $this->_buildUniqueRelationKey($relation);
+
+ $this->_validateSchemaElement('relation', array_keys($relation), $className . '->relation->' . $relation['alias']);
+
+ $this->_relations[$className][$alias] = $relation;
+ }
+ }
+
+ // Now we auto-complete opposite ends of relationships
+ $this->_autoCompleteOppositeRelations($extraAllwedReferences);
+
+ // Make sure we do not have any duplicate relations
+ $this->_fixDuplicateRelations();
+
+ //$array['relations'];
+ // Set the full array of relationships for each class to the final array
+ foreach ($this->_relations as $className => $relations) {
+ if (!in_array($className, $extraAllwedReferences))
+ $array[$className]['relations'] = $relations;
+ }
+
+ return $array;
+ }
+
+ protected function _autoCompleteOppositeRelations($extraAllwedReferences)
+ {
+ foreach ($this->_relations as $className => $relations) {
+ if (in_array($className, $extraAllwedReferences))
+ continue;
+
+ foreach ($relations as $alias => $relation) {
+ if ((isset($relation['equal']) && $relation['equal']) || (isset($relation['autoComplete']) && $relation['autoComplete'] === false)) {
+ continue;
+ }
+
+ $newRelation = array();
+ $newRelation['foreign'] = $relation['local'];
+ $newRelation['local'] = $relation['foreign'];
+ $newRelation['class'] = isset($relation['foreignClass']) ? $relation['foreignClass'] : $className;
+ $newRelation['alias'] = isset($relation['foreignAlias']) ? $relation['foreignAlias'] : $className;
+ $newRelation['exclude'] = isset($relation['foreignExclude']) ? $relation['foreignExclude'] : false;
+
+ // this is so that we know that this relation was autogenerated and
+ // that we do not need to include it if it is explicitly declared in the schema by the users.
+ $newRelation['autogenerated'] = true;
+
+ if (isset($relation['refClass'])) {
+ $newRelation['refClass'] = $relation['refClass'];
+ $newRelation['type'] = isset($relation['foreignType']) ? $relation['foreignType']:$relation['type'];
+ } else {
+ if(isset($relation['foreignType'])) {
+ $newRelation['type'] = $relation['foreignType'];
+ } else {
+ $newRelation['type'] = $relation['type'] === IPF_ORM_Relation::ONE ? IPF_ORM_Relation::MANY:IPF_ORM_Relation::ONE;
+ }
+ }
+
+ // Make sure it doesn't already exist
+ if ( ! isset($this->_relations[$relation['class']][$newRelation['alias']])) {
+ $newRelation['key'] = $this->_buildUniqueRelationKey($newRelation);
+ $this->_relations[$relation['class']][$newRelation['alias']] = $newRelation;
+ }
+ }
+ }
+ }
+
+ protected function _fixDuplicateRelations()
+ {
+ foreach($this->_relations as $className => $relations) {
+ // This is for checking for duplicates between alias-relations and a auto-generated relations to ensure the result set of unique relations
+ $existingRelations = array();
+ $uniqueRelations = array();
+ foreach ($relations as $relation) {
+ if ( ! in_array($relation['key'], $existingRelations)) {
+ $existingRelations[] = $relation['key'];
+ $uniqueRelations = array_merge($uniqueRelations, array($relation['alias'] => $relation));
+ } else {
+ // check to see if this relationship is not autogenerated, if it's not, then the user must have explicitly declared it
+ if ( ! isset($relation['autogenerated']) || $relation['autogenerated'] != true) {
+ $uniqueRelations = array_merge($uniqueRelations, array($relation['alias'] => $relation));
+ }
+ }
+ }
+
+ $this->_relations[$className] = $uniqueRelations;
+ }
+ }
+
+ protected function _buildUniqueRelationKey($relation)
+ {
+ return md5($relation['local'].$relation['foreign'].$relation['class'].(isset($relation['refClass']) ? $relation['refClass']:null));
+ }
+
+ protected function _validateSchemaElement($name, $element, $path)
+ {
+ $element = (array) $element;
+
+ $validation = $this->_validation[$name];
+
+ // Validators are a part of the column validation
+ // This should be fixed, made cleaner
+ if ($name == 'column') {
+ $validators = $this->getValidators();
+ $validation = array_merge($validation, $validators);
+ }
+
+ $validation = array_flip($validation);
+ foreach ($element as $key => $value) {
+ if ( ! isset($validation[$value])) {
+ throw new IPF_ORM_Exception(sprintf('Invalid schema element named "' . $value .
+ '" at path "' . $path . '"'));
+ }
+ }
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Inflector
+{
+ public static function pluralize($word)
+ {
+ $plural = array('/(quiz)$/i' => '\1zes',
+ '/^(ox)$/i' => '\1en',
+ '/([m|l])ouse$/i' => '\1ice',
+ '/(matr|vert|ind)ix|ex$/i' => '\1ices',
+ '/(x|ch|ss|sh)$/i' => '\1es',
+ '/([^aeiouy]|qu)ies$/i' => '\1y',
+ '/([^aeiouy]|qu)y$/i' => '\1ies',
+ '/(hive)$/i' => '\1s',
+ '/(?:([^f])fe|([lr])f)$/i' => '\1\2ves',
+ '/sis$/i' => 'ses',
+ '/([ti])um$/i' => '\1a',
+ '/(buffal|tomat)o$/i' => '\1oes',
+ '/(bu)s$/i' => '\1ses',
+ '/(alias|status)/i' => '\1es',
+ '/(octop|vir)us$/i' => '\1i',
+ '/(ax|test)is$/i' => '\1es',
+ '/s$/i' => 's',
+ '/$/' => 's');
+
+ $uncountable = array('equipment',
+ 'information',
+ 'rice',
+ 'money',
+ 'species',
+ 'series',
+ 'fish',
+ 'sheep');
+
+ $irregular = array('person' => 'people',
+ 'man' => 'men',
+ 'child' => 'children',
+ 'sex' => 'sexes',
+ 'move' => 'moves');
+
+ $lowercasedWord = strtolower($word);
+
+ foreach ($uncountable as $_uncountable) {
+ if (substr($lowercasedWord, (-1 * strlen($_uncountable))) == $_uncountable) {
+ return $word;
+ }
+ }
+
+ foreach ($irregular as $_plural=> $_singular) {
+ if (preg_match('/('.$_plural.')$/i', $word, $arr)) {
+ return preg_replace('/('.$_plural.')$/i', substr($arr[0],0,1) . substr($_singular,1), $word);
+ }
+ }
+
+ foreach ($plural as $rule => $replacement) {
+ if (preg_match($rule, $word)) {
+ return preg_replace($rule, $replacement, $word);
+ }
+ }
+
+ return false;
+ }
+
+ public static function singularize($word)
+ {
+ $singular = array('/(quiz)zes$/i' => '\\1',
+ '/(matr)ices$/i' => '\\1ix',
+ '/(vert|ind)ices$/i' => '\\1ex',
+ '/^(ox)en/i' => '\\1',
+ '/(alias|status)es$/i' => '\\1',
+ '/([octop|vir])i$/i' => '\\1us',
+ '/(cris|ax|test)es$/i' => '\\1is',
+ '/(shoe)s$/i' => '\\1',
+ '/(o)es$/i' => '\\1',
+ '/(bus)es$/i' => '\\1',
+ '/([m|l])ice$/i' => '\\1ouse',
+ '/(x|ch|ss|sh)es$/i' => '\\1',
+ '/(m)ovies$/i' => '\\1ovie',
+ '/(s)eries$/i' => '\\1eries',
+ '/([^aeiouy]|qu)ies$/i' => '\\1y',
+ '/([lr])ves$/i' => '\\1f',
+ '/(tive)s$/i' => '\\1',
+ '/(hive)s$/i' => '\\1',
+ '/([^f])ves$/i' => '\\1fe',
+ '/(^analy)ses$/i' => '\\1sis',
+ '/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => '\\1\\2sis',
+ '/([ti])a$/i' => '\\1um',
+ '/(n)ews$/i' => '\\1ews',
+ '/^(.{2,2})$/i' => '\\1',
+ '/s$/i' => '');
+
+ $uncountable = array('equipment',
+ 'information',
+ 'rice',
+ 'money',
+ 'species',
+ 'series',
+ 'fish',
+ 'sheep',
+ 'sms',
+ 'status',
+ 'access');
+
+ $irregular = array('person' => 'people',
+ 'man' => 'men',
+ 'child' => 'children',
+ 'sex' => 'sexes',
+ 'move' => 'moves');
+
+ $lowercasedWord = strtolower($word);
+ foreach ($uncountable as $_uncountable) {
+ if (substr($lowercasedWord, ( -1 * strlen($_uncountable))) == $_uncountable) {
+ return $word;
+ }
+ }
+
+ foreach ($irregular as $_singular => $_plural) {
+ if (preg_match('/('.$_plural.')$/i', $word, $arr)) {
+ return preg_replace('/('.$_plural.')$/i', substr($arr[0],0,1).substr($_singular,1), $word);
+ }
+ }
+
+ foreach ($singular as $rule => $replacement) {
+ if (preg_match($rule, $word)) {
+ return preg_replace($rule, $replacement, $word);
+ }
+ }
+
+ return $word;
+ }
+
+ public static function tableize($word)
+ {
+ // Would prefer this but it breaks unit tests. Forces the table underscore pattern
+ // return self::pluralize(self::underscore($name));
+ return strtolower(preg_replace('~(?<=\\w)([A-Z])~', '_$1', $word));
+ }
+
+ public static function classify($word)
+ {
+ return preg_replace_callback('~(_?)(_)([\w])~', array("IPF_ORM_Inflector", "classifyCallback"), ucfirst(strtolower($word)));
+ }
+
+ public static function classifyCallback($matches)
+ {
+ return $matches[1] . strtoupper($matches[3]);
+ }
+
+ public static function seemsUtf8($string)
+ {
+ for ($i = 0; $i < strlen($string); $i++) {
+ if (ord($string[$i]) < 0x80) continue; # 0bbbbbbb
+ elseif ((ord($string[$i]) & 0xE0) == 0xC0) $n=1; # 110bbbbb
+ elseif ((ord($string[$i]) & 0xF0) == 0xE0) $n=2; # 1110bbbb
+ elseif ((ord($string[$i]) & 0xF8) == 0xF0) $n=3; # 11110bbb
+ elseif ((ord($string[$i]) & 0xFC) == 0xF8) $n=4; # 111110bb
+ elseif ((ord($string[$i]) & 0xFE) == 0xFC) $n=5; # 1111110b
+ else return false; # Does not match any model
+ for ($j=0; $j<$n; $j++) { # n bytes matching 10bbbbbb follow ?
+ if ((++$i == strlen($string)) || ((ord($string[$i]) & 0xC0) != 0x80))
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public static function unaccent($string)
+ {
+ if (!preg_match('/[\x80-\xff]/', $string)) {
+ return $string;
+ }
+
+ if (self::seemsUtf8($string)) {
+ $chars = array(
+ // Decompositions for Latin-1 Supplement
+ chr(195).chr(128) => 'A', chr(195).chr(129) => 'A',
+ chr(195).chr(130) => 'A', chr(195).chr(131) => 'A',
+ chr(195).chr(132) => 'A', chr(195).chr(133) => 'A',
+ chr(195).chr(135) => 'C', chr(195).chr(136) => 'E',
+ chr(195).chr(137) => 'E', chr(195).chr(138) => 'E',
+ chr(195).chr(139) => 'E', chr(195).chr(140) => 'I',
+ chr(195).chr(141) => 'I', chr(195).chr(142) => 'I',
+ chr(195).chr(143) => 'I', chr(195).chr(145) => 'N',
+ chr(195).chr(146) => 'O', chr(195).chr(147) => 'O',
+ chr(195).chr(148) => 'O', chr(195).chr(149) => 'O',
+ chr(195).chr(150) => 'O', chr(195).chr(153) => 'U',
+ chr(195).chr(154) => 'U', chr(195).chr(155) => 'U',
+ chr(195).chr(156) => 'U', chr(195).chr(157) => 'Y',
+ chr(195).chr(159) => 's', chr(195).chr(160) => 'a',
+ chr(195).chr(161) => 'a', chr(195).chr(162) => 'a',
+ chr(195).chr(163) => 'a', chr(195).chr(164) => 'a',
+ chr(195).chr(165) => 'a', chr(195).chr(167) => 'c',
+ chr(195).chr(168) => 'e', chr(195).chr(169) => 'e',
+ chr(195).chr(170) => 'e', chr(195).chr(171) => 'e',
+ chr(195).chr(172) => 'i', chr(195).chr(173) => 'i',
+ chr(195).chr(174) => 'i', chr(195).chr(175) => 'i',
+ chr(195).chr(177) => 'n', chr(195).chr(178) => 'o',
+ chr(195).chr(179) => 'o', chr(195).chr(180) => 'o',
+ chr(195).chr(181) => 'o', chr(195).chr(182) => 'o',
+ chr(195).chr(182) => 'o', chr(195).chr(185) => 'u',
+ chr(195).chr(186) => 'u', chr(195).chr(187) => 'u',
+ chr(195).chr(188) => 'u', chr(195).chr(189) => 'y',
+ chr(195).chr(191) => 'y',
+ // Decompositions for Latin Extended-A
+ chr(196).chr(128) => 'A', chr(196).chr(129) => 'a',
+ chr(196).chr(130) => 'A', chr(196).chr(131) => 'a',
+ chr(196).chr(132) => 'A', chr(196).chr(133) => 'a',
+ chr(196).chr(134) => 'C', chr(196).chr(135) => 'c',
+ chr(196).chr(136) => 'C', chr(196).chr(137) => 'c',
+ chr(196).chr(138) => 'C', chr(196).chr(139) => 'c',
+ chr(196).chr(140) => 'C', chr(196).chr(141) => 'c',
+ chr(196).chr(142) => 'D', chr(196).chr(143) => 'd',
+ chr(196).chr(144) => 'D', chr(196).chr(145) => 'd',
+ chr(196).chr(146) => 'E', chr(196).chr(147) => 'e',
+ chr(196).chr(148) => 'E', chr(196).chr(149) => 'e',
+ chr(196).chr(150) => 'E', chr(196).chr(151) => 'e',
+ chr(196).chr(152) => 'E', chr(196).chr(153) => 'e',
+ chr(196).chr(154) => 'E', chr(196).chr(155) => 'e',
+ chr(196).chr(156) => 'G', chr(196).chr(157) => 'g',
+ chr(196).chr(158) => 'G', chr(196).chr(159) => 'g',
+ chr(196).chr(160) => 'G', chr(196).chr(161) => 'g',
+ chr(196).chr(162) => 'G', chr(196).chr(163) => 'g',
+ chr(196).chr(164) => 'H', chr(196).chr(165) => 'h',
+ chr(196).chr(166) => 'H', chr(196).chr(167) => 'h',
+ chr(196).chr(168) => 'I', chr(196).chr(169) => 'i',
+ chr(196).chr(170) => 'I', chr(196).chr(171) => 'i',
+ chr(196).chr(172) => 'I', chr(196).chr(173) => 'i',
+ chr(196).chr(174) => 'I', chr(196).chr(175) => 'i',
+ chr(196).chr(176) => 'I', chr(196).chr(177) => 'i',
+ chr(196).chr(178) => 'IJ',chr(196).chr(179) => 'ij',
+ chr(196).chr(180) => 'J', chr(196).chr(181) => 'j',
+ chr(196).chr(182) => 'K', chr(196).chr(183) => 'k',
+ chr(196).chr(184) => 'k', chr(196).chr(185) => 'L',
+ chr(196).chr(186) => 'l', chr(196).chr(187) => 'L',
+ chr(196).chr(188) => 'l', chr(196).chr(189) => 'L',
+ chr(196).chr(190) => 'l', chr(196).chr(191) => 'L',
+ chr(197).chr(128) => 'l', chr(197).chr(129) => 'L',
+ chr(197).chr(130) => 'l', chr(197).chr(131) => 'N',
+ chr(197).chr(132) => 'n', chr(197).chr(133) => 'N',
+ chr(197).chr(134) => 'n', chr(197).chr(135) => 'N',
+ chr(197).chr(136) => 'n', chr(197).chr(137) => 'N',
+ chr(197).chr(138) => 'n', chr(197).chr(139) => 'N',
+ chr(197).chr(140) => 'O', chr(197).chr(141) => 'o',
+ chr(197).chr(142) => 'O', chr(197).chr(143) => 'o',
+ chr(197).chr(144) => 'O', chr(197).chr(145) => 'o',
+ chr(197).chr(146) => 'OE',chr(197).chr(147) => 'oe',
+ chr(197).chr(148) => 'R', chr(197).chr(149) => 'r',
+ chr(197).chr(150) => 'R', chr(197).chr(151) => 'r',
+ chr(197).chr(152) => 'R', chr(197).chr(153) => 'r',
+ chr(197).chr(154) => 'S', chr(197).chr(155) => 's',
+ chr(197).chr(156) => 'S', chr(197).chr(157) => 's',
+ chr(197).chr(158) => 'S', chr(197).chr(159) => 's',
+ chr(197).chr(160) => 'S', chr(197).chr(161) => 's',
+ chr(197).chr(162) => 'T', chr(197).chr(163) => 't',
+ chr(197).chr(164) => 'T', chr(197).chr(165) => 't',
+ chr(197).chr(166) => 'T', chr(197).chr(167) => 't',
+ chr(197).chr(168) => 'U', chr(197).chr(169) => 'u',
+ chr(197).chr(170) => 'U', chr(197).chr(171) => 'u',
+ chr(197).chr(172) => 'U', chr(197).chr(173) => 'u',
+ chr(197).chr(174) => 'U', chr(197).chr(175) => 'u',
+ chr(197).chr(176) => 'U', chr(197).chr(177) => 'u',
+ chr(197).chr(178) => 'U', chr(197).chr(179) => 'u',
+ chr(197).chr(180) => 'W', chr(197).chr(181) => 'w',
+ chr(197).chr(182) => 'Y', chr(197).chr(183) => 'y',
+ chr(197).chr(184) => 'Y', chr(197).chr(185) => 'Z',
+ chr(197).chr(186) => 'z', chr(197).chr(187) => 'Z',
+ chr(197).chr(188) => 'z', chr(197).chr(189) => 'Z',
+ chr(197).chr(190) => 'z', chr(197).chr(191) => 's',
+ // Euro Sign
+ chr(226).chr(130).chr(172) => 'E',
+ // GBP (Pound) Sign
+ chr(194).chr(163) => '');
+
+ $string = strtr($string, $chars);
+ } else {
+ // Assume ISO-8859-1 if not UTF-8
+ $chars['in'] = chr(128).chr(131).chr(138).chr(142).chr(154).chr(158)
+ .chr(159).chr(162).chr(165).chr(181).chr(192).chr(193).chr(194)
+ .chr(195).chr(196).chr(197).chr(199).chr(200).chr(201).chr(202)
+ .chr(203).chr(204).chr(205).chr(206).chr(207).chr(209).chr(210)
+ .chr(211).chr(212).chr(213).chr(214).chr(216).chr(217).chr(218)
+ .chr(219).chr(220).chr(221).chr(224).chr(225).chr(226).chr(227)
+ .chr(228).chr(229).chr(231).chr(232).chr(233).chr(234).chr(235)
+ .chr(236).chr(237).chr(238).chr(239).chr(241).chr(242).chr(243)
+ .chr(244).chr(245).chr(246).chr(248).chr(249).chr(250).chr(251)
+ .chr(252).chr(253).chr(255);
+
+ $chars['out'] = "EfSZszYcYuAAAAAACEEEEIIIINOOOOOOUUUUYaaaaaaceeeeiiiinoooooouuuuyy";
+
+ $string = strtr($string, $chars['in'], $chars['out']);
+ $doubleChars['in'] = array(chr(140), chr(156), chr(198), chr(208), chr(222), chr(223), chr(230), chr(240), chr(254));
+ $doubleChars['out'] = array('OE', 'oe', 'AE', 'DH', 'TH', 'ss', 'ae', 'dh', 'th');
+ $string = str_replace($doubleChars['in'], $doubleChars['out'], $string);
+ }
+
+ return $string;
+ }
+
+ public static function translit($st)
+ {
+ $st = strtr($st, array(
+ 'а' => 'a', 'б' => 'b', 'в' => 'v', 'г' => 'g', 'д' => 'd',
+ 'е' => 'e', 'ё' => 'e', 'ж' => 'zh', 'з' => 'z', 'и' => 'i',
+ 'й' => 'y', 'к' => 'k', 'л' => 'l', 'м' => 'm', 'н' => 'n',
+ 'о' => 'o', 'п' => 'p', 'р' => 'r', 'с' => 's', 'т' => 't',
+ 'у' => 'u', 'ф' => 'f', 'х' => 'h', 'ц' => 'ts', 'ч' => 'ch',
+ 'ш' => 'sh', 'щ' => 'shch', 'ъ' => '\'', 'ы' => 'i', 'ь' => '',
+ 'э' => 'e', 'ю' => 'yu', 'я' => 'ya', 'ї' => 'i', 'є' => 'ie',
+
+ 'А' => 'A', 'Б' => 'B', 'В' => 'V', 'Г' => 'G', 'Д' => 'D',
+ 'Е' => 'E', 'Ё' => 'E', 'Ж' => 'Zh', 'З' => 'Z', 'И' => 'I',
+ 'Й' => 'Y', 'К' => 'K', 'Л' => 'L', 'М' => 'M', 'Н' => 'N',
+ 'О' => 'O', 'П' => 'P', 'Р' => 'R', 'С' => 'S', 'Т' => 'T',
+ 'У' => 'U', 'Ф' => 'F', 'Х' => 'H', 'Ц' => 'Ts', 'Ч' => 'Ch',
+ 'Ш' => 'Sh', 'Щ' => 'Shch', 'Ъ' => '\'', 'Ы' => 'I', 'Ь' => '',
+ 'Э' => 'E', 'Ю' => 'Yu', 'Я' => 'Ya', 'Ї' => 'Yi', 'Є' => 'Ye',
+ ));
+ return $st;
+ }
+
+ public static function urlize($text)
+ {
+ // Remove all non url friendly characters with the unaccent function
+ $text = self::unaccent($text);
+
+ // Transliterate cyrillic
+ $text = self::translit($text);
+
+ // Remove all none word characters
+ $text = preg_replace('/\W/', ' ', $text);
+
+ // More stripping. Replace spaces with dashes
+ $text = strtolower(preg_replace('/[^A-Z^a-z^0-9^\/]+/', '-',
+ preg_replace('/([a-z\d])([A-Z])/', '\1_\2',
+ preg_replace('/([A-Z]+)([A-Z][a-z])/', '\1_\2',
+ preg_replace('/::/', '/', $text)))));
+
+ return trim($text, '-');
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Manager extends IPF_ORM_Configurable
+{
+ protected $_connection = null;
+ protected $_queryRegistry;
+ public $dbListeners = array();
+
+ private function __construct()
+ {
+ $this->attributes = array(
+ IPF_ORM::ATTR_LOAD_REFERENCES => true,
+ IPF_ORM::ATTR_PORTABILITY => IPF_ORM::PORTABILITY_ALL,
+ IPF_ORM::ATTR_EXPORT => IPF_ORM::EXPORT_ALL,
+ IPF_ORM::ATTR_DECIMAL_PLACES => 2,
+ IPF_ORM::ATTR_DEFAULT_PARAM_NAMESPACE => 'ipf',
+ );
+ }
+
+ public static function getInstance()
+ {
+ static $instance;
+ if ( ! isset($instance)) {
+ $instance = new self();
+ }
+ return $instance;
+ }
+
+ public function getQueryRegistry()
+ {
+ if ( ! isset($this->_queryRegistry)) {
+ $this->_queryRegistry = new IPF_ORM_Query_Registry;
+ }
+ return $this->_queryRegistry;
+ }
+
+ public function setQueryRegistry(IPF_ORM_Query_Registry $registry)
+ {
+ $this->_queryRegistry = $registry;
+
+ return $this;
+ }
+
+ public static function connection()
+ {
+ return IPF_ORM_Manager::getInstance()->getCurrentConnection();
+ }
+
+ public function getCurrentConnection()
+ {
+ if (!$this->_connection) {
+ $pdo = \PFF\Container::databaseConnection();
+ $this->_connection = new IPF_ORM_Connection_Mysql($this, $pdo);
+ }
+ return $this->_connection;
+ }
+
+ public function __toString()
+ {
+ return "<pre>\nIPF_ORM_Manager\n</pre>";
+ }
+}
+
--- /dev/null
+<?php
+
+final class IPF_ORM_Null
+{
+ private static $instance = null;
+ public static function getInstance()
+ {
+ if (!self::$instance)
+ self::$instance = new IPF_ORM_Null;
+ return self::$instance;
+ }
+
+ private function __construct()
+ {
+ }
+
+ private function __clone()
+ {
+ }
+
+ public static function isNull($obj)
+ {
+ return $obj === self::$instance;
+ }
+
+ public function exists()
+ {
+ return false;
+ }
+
+ public function __toString()
+ {
+ return '';
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Pager
+{
+ protected $_query;
+ protected $_countQuery;
+ protected $_countQueryParams;
+ protected $_numResults;
+ protected $_maxPerPage;
+ protected $_page;
+ protected $_lastPage;
+ protected $_executed;
+
+ public function __construct($query, $page, $maxPerPage = 0)
+ {
+ $this->_setExecuted(false);
+
+ $this->_setQuery($query);
+ $this->_setPage($page);
+
+ $this->setMaxPerPage($maxPerPage);
+ }
+
+ protected function _initialize($params = array())
+ {
+ // retrieve the number of items found
+ $count = $this->getCountQuery()->count($this->getCountQueryParams($params));
+
+ $this->_setNumResults($count);
+ $this->_setExecuted(true); // _adjustOffset relies of _executed equals true = getNumResults()
+
+ $this->_adjustOffset();
+ }
+
+ protected function _adjustOffset()
+ {
+ // Define new total of pages
+ $this->_setLastPage(
+ max(1, ceil($this->getNumResults() / $this->getMaxPerPage()))
+ );
+ $offset = ($this->getPage() - 1) * $this->getMaxPerPage();
+
+ // Assign new offset and limit to IPF_ORM_Query object
+ $p = $this->getQuery();
+ $p->offset($offset);
+ $p->limit($this->getMaxPerPage());
+ }
+
+ public function getExecuted()
+ {
+ return $this->_executed;
+ }
+
+ protected function _setExecuted($executed)
+ {
+ $this->_executed = $executed;
+ }
+
+ public function getRange($rangeStyle, $options = array())
+ {
+ $class = 'IPF_ORM_Pager_Range_' . ucfirst($rangeStyle);
+ return new $class($options, $this);
+ }
+
+ public function getNumResults()
+ {
+ if ($this->getExecuted()) {
+ return $this->_numResults;
+ }
+ throw new IPF_ORM_Exception(
+ 'Cannot retrieve the number of results of a not yet executed Pager query'
+ );
+ }
+
+ protected function _setNumResults($nb)
+ {
+ $this->_numResults = $nb;
+ }
+
+ public function getFirstPage()
+ {
+ return 1;
+ }
+
+ public function getLastPage()
+ {
+ if ($this->getExecuted()) {
+ return $this->_lastPage;
+ }
+
+ throw new IPF_ORM_Exception(
+ 'Cannot retrieve the last page number of a not yet executed Pager query'
+ );
+ }
+
+ protected function _setLastPage($page)
+ {
+ $this->_lastPage = $page;
+
+ if ($this->getPage() > $page) {
+ $this->_setPage($page);
+ }
+ }
+
+ public function getPage()
+ {
+ return $this->_page;
+ }
+
+ public function getNextPage()
+ {
+ if ($this->getExecuted()) {
+ return min($this->getPage() + 1, $this->getLastPage());
+ }
+
+ throw new IPF_ORM_Exception(
+ 'Cannot retrieve the last page number of a not yet executed Pager query'
+ );
+ }
+
+ public function getPreviousPage()
+ {
+ if ($this->getExecuted()) {
+ return max($this->getPage() - 1, $this->getFirstPage());
+ }
+
+ throw new IPF_ORM_Exception(
+ 'Cannot retrieve the previous page number of a not yet executed Pager query'
+ );
+ }
+
+ public function getFirstIndice()
+ {
+ return ($this->getPage() - 1) * $this->getMaxPerPage() + 1;
+ }
+
+ public function getLastIndice()
+ {
+ return min($this->getNumResults(), ($this->getPage() * $this->getMaxPerPage()));
+ }
+
+ public function haveToPaginate()
+ {
+ if ($this->getExecuted()) {
+ return $this->getNumResults() > $this->getMaxPerPage();
+ }
+
+ throw new IPF_ORM_Exception(
+ 'Cannot know if it is necessary to paginate a not yet executed Pager query'
+ );
+ }
+
+ public function setPage($page)
+ {
+ $this->_setPage($page);
+ $this->_setExecuted(false);
+ }
+
+ private function _setPage($page)
+ {
+ $page = intval($page);
+ $this->_page = ($page <= 0) ? 1 : $page;
+ }
+
+ public function getMaxPerPage()
+ {
+ return $this->_maxPerPage;
+ }
+
+ public function setMaxPerPage($max)
+ {
+ if ($max > 0) {
+ $this->_maxPerPage = $max;
+ } else if ($max == 0) {
+ $this->_maxPerPage = 25;
+ } else {
+ $this->_maxPerPage = abs($max);
+ }
+
+ $this->_setExecuted(false);
+ }
+
+ public function getResultsInPage()
+ {
+ $page = $this->getPage();
+
+ if ($page != $this->getLastPage()) {
+ return $this->getMaxPerPage();
+ }
+
+ $offset = ($this->getPage() - 1) * $this->getMaxPerPage();
+
+ return abs($this->getNumResults() - $offset);
+ }
+
+ public function getQuery()
+ {
+ return $this->_query;
+ }
+
+ protected function _setQuery($query)
+ {
+ if (is_string($query)) {
+ $query = IPF_ORM_Query::create()->parseQuery($query);
+ }
+
+ $this->_query = $query;
+ }
+
+ public function getCountQuery()
+ {
+ return ($this->_countQuery !== null) ? $this->_countQuery : $this->_query;
+ }
+
+ public function setCountQuery($query, $params = null)
+ {
+ if (is_string($query)) {
+ $query = IPF_ORM_Query::create()->parseQuery($query);
+ }
+
+ $this->_countQuery = $query;
+
+ $this->setCountQueryParams($params);
+
+ $this->_setExecuted(false);
+ }
+
+ public function getCountQueryParams($defaultParams = array())
+ {
+ return ($this->_countQueryParams !== null) ? $this->_countQueryParams : $defaultParams;
+ }
+
+ public function setCountQueryParams($params = array(), $append = false)
+ {
+ if ($append && is_array($this->_countQueryParams)) {
+ $this->_countQueryParams = array_merge($this->_countQueryParams, $params);
+ } else {
+ if ($params !== null && !is_array($params)) {
+ $params = array($params);
+ }
+
+ $this->_countQueryParams = $params;
+ }
+
+ $this->_setExecuted(false);
+ }
+
+ public function execute($params = array(), $hydrationMode = IPF_ORM::FETCH_RECORD)
+ {
+ if (!$this->getExecuted()) {
+ $this->_initialize($params);
+ }
+ return $this->getQuery()->execute($params, $hydrationMode);
+ }
+
+ public function __debugInfo()
+ {
+ return array(
+ 'page' => $this->getPage(),
+ 'size' => $this->getMaxPerPage(),
+ 'query' => $this->getQuery(),
+ );
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Pager_Layout
+{
+ private $_pager;
+ private $_pagerRange;
+ private $_template;
+ private $_selectedTemplate;
+ private $_separatorTemplate;
+ private $_urlMask;
+ private $_maskReplacements = array();
+
+ public function __construct($pager, $pagerRange, $urlMask)
+ {
+ $this->_setPager($pager);
+ $this->_setPagerRange($pagerRange);
+ $this->_setUrlMask($urlMask);
+
+ $this->setTemplate('[<a href="{%url}">{%page}</a>]');
+ $this->setSelectedTemplate('');
+ $this->setSeparatorTemplate('');
+ }
+
+ public function getPager()
+ {
+ return $this->_pager;
+ }
+
+ protected function _setPager($pager)
+ {
+ $this->_pager = $pager;
+ }
+
+ public function execute($params = array(), $hydrationMode = IPF_ORM::FETCH_RECORD)
+ {
+ return $this->getPager()->execute($params, $hydrationMode);
+ }
+
+ public function getPagerRange()
+ {
+ return $this->_pagerRange;
+ }
+
+ protected function _setPagerRange($pagerRange)
+ {
+ $this->_pagerRange = $pagerRange;
+ $this->getPagerRange()->setPager($this->getPager());
+ }
+
+ public function getUrlMask()
+ {
+ return $this->_urlMask;
+ }
+
+ protected function _setUrlMask($urlMask)
+ {
+ $this->_urlMask = $urlMask;
+ }
+
+ public function getTemplate()
+ {
+ return $this->_template;
+ }
+
+ public function setTemplate($template)
+ {
+ $this->_template = $template;
+ }
+
+ public function getSelectedTemplate()
+ {
+ return $this->_selectedTemplate;
+ }
+
+ public function setSelectedTemplate($selectedTemplate)
+ {
+ $this->_selectedTemplate = $selectedTemplate;
+ }
+
+ public function getSeparatorTemplate()
+ {
+ return $this->_separatorTemplate;
+ }
+
+ public function setSeparatorTemplate($separatorTemplate)
+ {
+ $this->_separatorTemplate = $separatorTemplate;
+ }
+
+ public function addMaskReplacement($oldMask, $newMask, $asValue = false)
+ {
+ if (($oldMask = trim($oldMask)) != 'page_number') {
+ $this->_maskReplacements[$oldMask] = array(
+ 'newMask' => $newMask,
+ 'asValue' => ($asValue === false) ? false : true
+ );
+ }
+ }
+
+ public function removeMaskReplacement($oldMask)
+ {
+ if (isset($this->_maskReplacements[$oldMask])) {
+ $this->_maskReplacements[$oldMask] = null;
+ unset($this->_maskReplacements[$oldMask]);
+ }
+ }
+
+ public function cleanMaskReplacements()
+ {
+ $this->_maskReplacements = null;
+ $this->_maskReplacements = array();
+ }
+
+ public function display($options = array(), $return = true)
+ {
+ $range = $this->getPagerRange()->rangeAroundPage();
+ $str = '';
+
+ // For each page in range
+ for ($i = 0, $l = count($range); $i < $l; $i++) {
+ // Define some optional mask values
+ $options['page_number'] = $range[$i];
+
+ $str .= $this->processPage($options);
+
+ // Apply separator between pages
+ if ($i < $l - 1) {
+ $str .= $this->getSeparatorTemplate();
+ }
+ }
+
+ // Possible wish to return value instead of print it on screen
+ if ($return) {
+ return $str;
+ }
+
+ echo $str;
+ }
+
+ public function processPage($options = array())
+ {
+ // Check if at least basic options are defined
+ if (!isset($options['page_number'])) {
+ throw new IPF_ORM_Exception(
+ 'Cannot process template of the given page. ' .
+ 'Missing at least one of needed parameters: \'page\' or \'page_number\''
+ );
+
+ // Should never reach here
+ return '';
+ }
+
+ // Assign "page" options index if not defined yet
+ if (!isset($this->_maskReplacements['page']) && !isset($options['page'])) {
+ $options['page'] = $options['page_number'];
+ }
+
+ return $this->_parseTemplate($options);
+ }
+
+ public function __toString()
+ {
+ return $this->display(array(), true);
+ }
+
+ protected function _parseTemplate($options = array())
+ {
+ $str = $this->_parseUrlTemplate($options);
+ $replacements = $this->_parseReplacementsTemplate($options);
+
+ return strtr($str, $replacements);
+ }
+
+ protected function _parseUrlTemplate($options = array())
+ {
+ $str = '';
+
+ // If given page is the current active one
+ if ($options['page_number'] == $this->getPager()->getPage()) {
+ $str = $this->_parseMaskReplacements($this->getSelectedTemplate());
+ }
+
+ // Possible attempt where Selected == Template
+ if ($str == '') {
+ $str = $this->_parseMaskReplacements($this->getTemplate());
+ }
+
+ return $str;
+ }
+
+ protected function _parseReplacementsTemplate($options = array())
+ {
+ // Defining "url" options index to allow {%url} mask
+ $options['url'] = $this->_parseUrl($options);
+
+ $replacements = array();
+
+ foreach ($options as $k => $v) {
+ $replacements['{%'.$k.'}'] = $v;
+ }
+
+ return $replacements;
+ }
+
+ protected function _parseUrl($options = array())
+ {
+ $str = $this->_parseMaskReplacements($this->getUrlMask());
+
+ $replacements = array();
+
+ foreach ($options as $k => $v) {
+ $replacements['{%'.$k.'}'] = $v;
+ }
+
+ return strtr($str, $replacements);
+ }
+
+ protected function _parseMaskReplacements($str)
+ {
+ $replacements = array();
+
+ foreach ($this->_maskReplacements as $k => $v) {
+ $replacements['{%'.$k.'}'] = ($v['asValue'] === true) ? $v['newMask'] : '{%'.$v['newMask'].'}';
+ }
+
+ return strtr($str, $replacements);
+ }
+
+ public function __debugInfo()
+ {
+ return array(
+ 'pager' => $this->getPager()
+ );
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Pager_LayoutArrows extends IPF_ORM_Pager_Layout
+{
+ public function display($options = array(), $return = true)
+ {
+ $pager = $this->getPager();
+ $str = '';
+
+ $range = $this->getPagerRange()->rangeAroundPage();
+
+ if ($pager->getFirstPage() != $pager->getLastPage())
+ {
+ $this->removeMaskReplacement('page');
+
+ if ($range[0] > 1)
+ {
+ $options['page_number'] = 1;
+ $str .= $this->processPage($options);
+
+ if ($range[0] > 2)
+ {
+ $options['page_number'] = 2;
+ $str .= $this->processPage($options);
+ }
+
+ if ($range[0]>3)
+ $str .= ' ... ';
+ }
+
+ // Pages listing
+ $str .= parent::display($options, true);
+
+ $range = $this->getPagerRange()->rangeAroundPage();
+ $last_range = $range[count($range)-1];
+
+ if ($last_range < $pager->getLastPage())
+ {
+ if ($last_range < $pager->getLastPage() - 2)
+ {
+ $str .= ' ... ';
+ }
+
+ if ($last_range < $pager->getLastPage() - 1)
+ {
+ $options['page_number'] = $pager->getLastPage() - 1;
+ $str .= $this->processPage($options);
+ }
+
+ if ($last_range < $pager->getLastPage())
+ {
+ $options['page_number'] = $pager->getLastPage();
+ $str .= $this->processPage($options);
+ }
+ }
+ }
+
+ if ($return)
+ return $str;
+
+ echo $str;
+ }
+}
+
--- /dev/null
+<?php
+
+abstract class IPF_ORM_Pager_Range
+{
+ protected $_options;
+ private $pager;
+
+ final public function __construct($options = array(), $pager = null)
+ {
+ $this->_setOptions($options);
+
+ if ($pager !== null) {
+ $this->setPager($pager);
+ }
+ }
+
+ public function getPager()
+ {
+ return $this->pager;
+ }
+
+ public function setPager($pager)
+ {
+ $this->pager = $pager;
+
+ // Lazy-load initialization. It only should be called when all
+ // needed information data is ready (this can only happens when we have
+ // options stored and a IPF_ORM_Pager assocated)
+ $this->_initialize();
+ }
+
+ public function getOptions()
+ {
+ return $this->_options;
+ }
+
+ public function getOption($option)
+ {
+ if (isset($this->_options[$option])) {
+ return $this->_options[$option];
+ }
+
+ throw new IPF_ORM_Exception(
+ 'Cannot access unexistent option \'' . $option . '\' in IPF_ORM_Pager_Range class'
+ );
+ }
+
+ protected function _setOptions($options)
+ {
+ $this->_options = $options;
+ }
+
+ public function isInRange($page)
+ {
+ return (array_search($page, $this->rangeAroundPage()) !== false);
+ }
+
+ abstract protected function _initialize();
+
+ abstract public function rangeAroundPage();
+}
--- /dev/null
+<?php
+
+class IPF_ORM_Pager_Range_Jumping extends IPF_ORM_Pager_Range
+{
+ private $_chunkLength;
+
+ protected function _initialize()
+ {
+ if (isset($this->_options['chunk'])) {
+ $this->_setChunkLength($this->_options['chunk']);
+ } else {
+ throw new IPF_ORM_Exception('Missing parameter \'chunk\' that must be define in options.');
+ }
+ }
+
+ public function getChunkLength()
+ {
+ return $this->_chunkLength;
+ }
+
+ protected function _setChunkLength($chunkLength)
+ {
+ $this->_chunkLength = $chunkLength;
+ }
+
+ public function rangeAroundPage()
+ {
+ $pager = $this->getPager();
+
+ if ($pager->getExecuted()) {
+ $page = $pager->getPage();
+
+ // Define initial assignments for StartPage and EndPage
+ $startPage = $page - ($page - 1) % $this->getChunkLength();
+ $endPage = ($startPage + $this->getChunkLength()) - 1;
+
+ // Check for EndPage out-range
+ if ($endPage > $pager->getLastPage()) {
+ $endPage = $pager->getLastPage();
+ }
+
+ // No need to check for out-range in start, it will never happens
+
+ return range($startPage, $endPage);
+ }
+
+ throw new IPF_ORM_Exception(
+ 'Cannot retrieve the range around the page of a not yet executed Pager query'
+ );
+ }
+}
--- /dev/null
+<?php
+
+class IPF_ORM_Pager_Range_Sliding extends IPF_ORM_Pager_Range
+{
+ private $_chunkLength;
+
+ protected function _initialize()
+ {
+ if (isset($this->_options['chunk'])) {
+ $this->_setChunkLength($this->_options['chunk']);
+ } else {
+ throw new IPF_ORM_Exception('Missing parameter \'chunk\' that must be defined in options.');
+ }
+ }
+
+ public function getChunkLength()
+ {
+ return $this->_chunkLength;
+ }
+
+ protected function _setChunkLength($chunkLength)
+ {
+ $chunkLength = (int) $chunkLength;
+ if (!$chunkLength) {
+ $chunkLength = 1;
+ } else {
+ $this->_chunkLength = $chunkLength;
+ }
+ }
+
+ public function rangeAroundPage()
+ {
+ $pager = $this->getPager();
+
+ if ($pager->getExecuted()) {
+ $page = $pager->getPage();
+ $pages = $pager->getLastPage();
+
+ $chunk = $this->getChunkLength();
+
+ if ($chunk > $pages) {
+ $chunk = $pages;
+ }
+
+ $chunkStart = $page - (floor($chunk / 2));
+ $chunkEnd = $page + (ceil($chunk / 2)-1);
+
+ if ($chunkStart < 1) {
+ $adjust = 1 - $chunkStart;
+ $chunkStart = 1;
+ $chunkEnd = $chunkEnd + $adjust;
+ }
+
+ if ($chunkEnd > $pages) {
+ $adjust = $chunkEnd - $pages;
+ $chunkStart = $chunkStart - $adjust;
+ $chunkEnd = $pages;
+ }
+ return range($chunkStart, $chunkEnd);
+ }
+
+ throw new IPF_ORM_Exception(
+ 'Cannot retrieve the range around the page of a not yet executed Pager query'
+ );
+ }
+}
--- /dev/null
+<?php
+
+class IPF_ORM_Query extends IPF_ORM_Query_Abstract implements Countable, Serializable
+{
+ protected static $_keywords = array('ALL',
+ 'AND',
+ 'ANY',
+ 'AS',
+ 'ASC',
+ 'AVG',
+ 'BETWEEN',
+ 'BIT_LENGTH',
+ 'BY',
+ 'CHARACTER_LENGTH',
+ 'CHAR_LENGTH',
+ 'CURRENT_DATE',
+ 'CURRENT_TIME',
+ 'CURRENT_TIMESTAMP',
+ 'DELETE',
+ 'DESC',
+ 'DISTINCT',
+ 'EMPTY',
+ 'EXISTS',
+ 'FALSE',
+ 'FETCH',
+ 'FROM',
+ 'GROUP',
+ 'HAVING',
+ 'IN',
+ 'INDEXBY',
+ 'INNER',
+ 'IS',
+ 'JOIN',
+ 'LEFT',
+ 'LIKE',
+ 'LOWER',
+ 'MEMBER',
+ 'MOD',
+ 'NEW',
+ 'NOT',
+ 'NULL',
+ 'OBJECT',
+ 'OF',
+ 'OR',
+ 'ORDER',
+ 'OUTER',
+ 'POSITION',
+ 'SELECT',
+ 'SOME',
+ 'TRIM',
+ 'TRUE',
+ 'UNKNOWN',
+ 'UPDATE',
+ 'WHERE');
+
+ protected $_subqueryAliases = array();
+ protected $_aggregateAliasMap = array();
+ protected $_pendingAggregates = array();
+ protected $_isSubquery;
+ protected $_neededTables = array();
+ protected $_pendingSubqueries = array();
+ protected $_pendingFields = array();
+ protected $_parsers = array();
+ protected $_pendingJoinConditions = array();
+ protected $_expressionMap = array();
+ protected $_sql;
+
+ public static function create($conn = null)
+ {
+ return new IPF_ORM_Query($conn);
+ }
+
+ public function reset()
+ {
+ $this->_pendingJoinConditions = array();
+ $this->_pendingSubqueries = array();
+ $this->_pendingFields = array();
+ $this->_neededTables = array();
+ $this->_expressionMap = array();
+ $this->_subqueryAliases = array();
+ }
+
+ public function createSubquery()
+ {
+ $class = get_class($this);
+ $obj = new $class();
+
+ // copy the aliases to the subquery
+ $obj->copyAliases($this);
+
+ // this prevents the 'id' being selected, re ticket #307
+ $obj->isSubquery(true);
+
+ return $obj;
+ }
+
+ protected function _addPendingJoinCondition($componentAlias, $joinCondition)
+ {
+ $this->_pendingJoins[$componentAlias] = $joinCondition;
+ }
+
+ public function addEnumParam($key, $table = null, $column = null)
+ {
+ $array = (isset($table) || isset($column)) ? array($table, $column) : array();
+
+ if ($key === '?') {
+ $this->_enumParams[] = $array;
+ } else {
+ $this->_enumParams[$key] = $array;
+ }
+ }
+
+ public function getEnumParams()
+ {
+ return $this->_enumParams;
+ }
+
+ public function getDql()
+ {
+ $q = '';
+ $q .= ( ! empty($this->_dqlParts['select']))? 'SELECT ' . implode(', ', $this->_dqlParts['select']) : '';
+ $q .= ( ! empty($this->_dqlParts['from']))? ' FROM ' . implode(' ', $this->_dqlParts['from']) : '';
+ $q .= ( ! empty($this->_dqlParts['where']))? ' WHERE ' . implode(' AND ', $this->_dqlParts['where']) : '';
+ $q .= ( ! empty($this->_dqlParts['groupby']))? ' GROUP BY ' . implode(', ', $this->_dqlParts['groupby']) : '';
+ $q .= ( ! empty($this->_dqlParts['having']))? ' HAVING ' . implode(' AND ', $this->_dqlParts['having']) : '';
+ $q .= ( ! empty($this->_dqlParts['orderby']))? ' ORDER BY ' . implode(', ', $this->_dqlParts['orderby']) : '';
+ $q .= ( ! empty($this->_dqlParts['limit']))? ' LIMIT ' . implode(' ', $this->_dqlParts['limit']) : '';
+ $q .= ( ! empty($this->_dqlParts['offset']))? ' OFFSET ' . implode(' ', $this->_dqlParts['offset']) : '';
+
+ return $q;
+ }
+
+
+ public function fetchArray($params = array()) {
+ return $this->execute($params, IPF_ORM::HYDRATE_ARRAY);
+ }
+
+ public function fetchOne($params = array(), $hydrationMode = null)
+ {
+ $collection = $this->execute($params, $hydrationMode);
+
+ if (count($collection) === 0) {
+ return false;
+ }
+
+ if ($collection instanceof IPF_ORM_Collection) {
+ return $collection->getFirst();
+ } else if (is_array($collection)) {
+ return array_shift($collection);
+ }
+
+ return false;
+ }
+
+ public function isSubquery($bool = null)
+ {
+ if ($bool === null) {
+ return $this->_isSubquery;
+ }
+
+ $this->_isSubquery = (bool) $bool;
+ return $this;
+ }
+
+ public function getAggregateAlias($dqlAlias)
+ {
+ return $this->getSqlAggregateAlias($dqlAlias);
+ }
+
+ public function getSqlAggregateAlias($dqlAlias)
+ {
+ if (isset($this->_aggregateAliasMap[$dqlAlias])) {
+ // mark the expression as used
+ $this->_expressionMap[$dqlAlias][1] = true;
+
+ return $this->_aggregateAliasMap[$dqlAlias];
+ } else if ( ! empty($this->_pendingAggregates)) {
+ $this->processPendingAggregates();
+
+ return $this->getSqlAggregateAlias($dqlAlias);
+ } else {
+ throw new IPF_ORM_Exception('Unknown aggregate alias: ' . $dqlAlias);
+ }
+ }
+
+ public function getDqlPart($queryPart)
+ {
+ if ( ! isset($this->_dqlParts[$queryPart])) {
+ throw new IPF_ORM_Exception('Unknown query part ' . $queryPart);
+ }
+
+ return $this->_dqlParts[$queryPart];
+ }
+
+ public function contains($dql)
+ {
+ return stripos($this->getDql(), $dql) === false ? false : true;
+ }
+
+ public function processPendingFields($componentAlias)
+ {
+ $tableAlias = $this->getTableAlias($componentAlias);
+ $table = $this->_queryComponents[$componentAlias]['table'];
+
+ if ( ! isset($this->_pendingFields[$componentAlias])) {
+ if ($this->_hydrator->getHydrationMode() != IPF_ORM::HYDRATE_NONE) {
+ if ( ! $this->_isSubquery && $componentAlias == $this->getRootAlias()) {
+ throw new IPF_ORM_Exception("The root class of the query (alias $componentAlias) "
+ . " must have at least one field selected.");
+ }
+ }
+ return;
+ }
+
+ // At this point we know the component is FETCHED (either it's the base class of
+ // the query (FROM xyz) or its a "fetch join").
+
+ // Check that the parent join (if there is one), is a "fetch join", too.
+ if (isset($this->_queryComponents[$componentAlias]['parent'])) {
+ $parentAlias = $this->_queryComponents[$componentAlias]['parent'];
+ if (is_string($parentAlias) && ! isset($this->_pendingFields[$parentAlias])
+ && $this->_hydrator->getHydrationMode() != IPF_ORM::HYDRATE_NONE) {
+ throw new IPF_ORM_Exception("The left side of the join between "
+ . "the aliases '$parentAlias' and '$componentAlias' must have at least"
+ . " the primary key field(s) selected.");
+ }
+ }
+
+ $fields = $this->_pendingFields[$componentAlias];
+
+ // check for wildcards
+ if (in_array('*', $fields)) {
+ $fields = $table->getFieldNames();
+ } else {
+ // only auto-add the primary key fields if this query object is not
+ // a subquery of another query object
+ if ( ! $this->_isSubquery || $this->_hydrator->getHydrationMode() === IPF_ORM::HYDRATE_NONE) {
+ $fields = array_unique(array_merge((array) $table->getIdentifier(), $fields));
+ }
+ }
+
+ $sql = array();
+ foreach ($fields as $fieldName) {
+ $columnName = $table->getColumnName($fieldName);
+ $sql[] = $this->_conn->quoteIdentifier($tableAlias . '.' . $columnName)
+ . ' AS '
+ . $this->_conn->quoteIdentifier($tableAlias . '__' . $columnName);
+ }
+
+ $this->_neededTables[] = $tableAlias;
+
+ return implode(', ', $sql);
+ }
+
+ public function parseSelectField($field)
+ {
+ $terms = explode('.', $field);
+
+ if (isset($terms[1])) {
+ $componentAlias = $terms[0];
+ $field = $terms[1];
+ } else {
+ reset($this->_queryComponents);
+ $componentAlias = key($this->_queryComponents);
+ $fields = $terms[0];
+ }
+
+ $tableAlias = $this->getTableAlias($componentAlias);
+ $table = $this->_queryComponents[$componentAlias]['table'];
+
+
+ // check for wildcards
+ if ($field === '*') {
+ $sql = array();
+
+ foreach ($table->getColumnNames() as $field) {
+ $sql[] = $this->parseSelectField($componentAlias . '.' . $field);
+ }
+
+ return implode(', ', $sql);
+ } else {
+ $name = $table->getColumnName($field);
+
+ $this->_neededTables[] = $tableAlias;
+
+ return $this->_conn->quoteIdentifier($tableAlias . '.' . $name)
+ . ' AS '
+ . $this->_conn->quoteIdentifier($tableAlias . '__' . $name);
+ }
+ }
+
+ public function getExpressionOwner($expr)
+ {
+ if (strtoupper(substr(trim($expr, '( '), 0, 6)) !== 'SELECT') {
+ preg_match_all("/[a-z_][a-z0-9_]*\.[a-z_][a-z0-9_]*[\.[a-z0-9]+]*/i", $expr, $matches);
+
+ $match = current($matches);
+
+ if (isset($match[0])) {
+ $terms = explode('.', $match[0]);
+
+ return $terms[0];
+ }
+ }
+ return $this->getRootAlias();
+
+ }
+
+ public function parseSelect($dql)
+ {
+ $refs = $this->_tokenizer->sqlExplode($dql, ',');
+
+ $pos = strpos(trim($refs[0]), ' ');
+ $first = substr($refs[0], 0, $pos);
+
+ // check for DISTINCT keyword
+ if ($first === 'DISTINCT') {
+ $this->_sqlParts['distinct'] = true;
+
+ $refs[0] = substr($refs[0], ++$pos);
+ }
+
+ $parsedComponents = array();
+
+ foreach ($refs as $reference) {
+ $reference = trim($reference);
+
+ if (empty($reference)) {
+ continue;
+ }
+
+ $terms = $this->_tokenizer->sqlExplode($reference, ' ');
+
+ $pos = strpos($terms[0], '(');
+
+ if (count($terms) > 1 || $pos !== false) {
+ $expression = array_shift($terms);
+ $alias = array_pop($terms);
+
+ if ( ! $alias) {
+ $alias = substr($expression, 0, $pos);
+ }
+
+ $componentAlias = $this->getExpressionOwner($expression);
+ $expression = $this->parseClause($expression);
+
+ $tableAlias = $this->getTableAlias($componentAlias);
+
+ $index = count($this->_aggregateAliasMap);
+
+ $sqlAlias = $this->_conn->quoteIdentifier($tableAlias . '__' . $index);
+
+ $this->_sqlParts['select'][] = $expression . ' AS ' . $sqlAlias;
+
+ $this->_aggregateAliasMap[$alias] = $sqlAlias;
+ $this->_expressionMap[$alias][0] = $expression;
+
+ $this->_queryComponents[$componentAlias]['agg'][$index] = $alias;
+
+ $this->_neededTables[] = $tableAlias;
+ } else {
+ $e = explode('.', $terms[0]);
+
+ if (isset($e[1])) {
+ $componentAlias = $e[0];
+ $field = $e[1];
+ } else {
+ reset($this->_queryComponents);
+ $componentAlias = key($this->_queryComponents);
+ $field = $e[0];
+ }
+
+ $this->_pendingFields[$componentAlias][] = $field;
+ }
+ }
+ }
+
+ public function parseClause($clause)
+ {
+ $clause = trim($clause);
+
+ if (is_numeric($clause)) {
+ return $clause;
+ }
+
+ $terms = $this->_tokenizer->clauseExplode($clause, array(' ', '+', '-', '*', '/', '<', '>', '=', '>=', '<='));
+
+ $str = '';
+ foreach ($terms as $term) {
+ $pos = strpos($term[0], '(');
+
+ if ($pos !== false) {
+ $name = substr($term[0], 0, $pos);
+ $term[0] = $this->parseFunctionExpression($term[0]);
+ } else {
+ if (substr($term[0], 0, 1) !== "'" && substr($term[0], -1) !== "'") {
+
+ if (strpos($term[0], '.') !== false) {
+ if ( ! is_numeric($term[0])) {
+ $e = explode('.', $term[0]);
+
+ $field = array_pop($e);
+
+ if ($this->getType() === IPF_ORM_Query::SELECT) {
+ $componentAlias = implode('.', $e);
+
+ if (empty($componentAlias)) {
+ $componentAlias = $this->getRootAlias();
+ }
+
+ $this->load($componentAlias);
+
+ // check the existence of the component alias
+ if ( ! isset($this->_queryComponents[$componentAlias])) {
+ throw new IPF_ORM_Exception('Unknown component alias ' . $componentAlias);
+ }
+
+ $table = $this->_queryComponents[$componentAlias]['table'];
+
+ $def = $table->getDefinitionOf($field);
+
+ // get the actual field name from alias
+ $field = $table->getColumnName($field);
+
+ // check column existence
+ if ( ! $def) {
+ throw new IPF_ORM_Exception('Unknown column ' . $field);
+ }
+
+ if (isset($def['owner'])) {
+ $componentAlias = $componentAlias . '.' . $def['owner'];
+ }
+
+ $tableAlias = $this->getTableAlias($componentAlias);
+
+ // build sql expression
+ $term[0] = $this->_conn->quoteIdentifier($tableAlias)
+ . '.'
+ . $this->_conn->quoteIdentifier($field);
+ } else {
+ // build sql expression
+ $field = $this->getRoot()->getColumnName($field);
+ $term[0] = $this->_conn->quoteIdentifier($field);
+ }
+ }
+ } else {
+ if ( ! empty($term[0]) &&
+ ! in_array(strtoupper($term[0]), self::$_keywords) &&
+ ! is_numeric($term[0])) {
+
+ $componentAlias = $this->getRootAlias();
+
+ $found = false;
+
+ if ($componentAlias !== false &&
+ $componentAlias !== null) {
+ $table = $this->_queryComponents[$componentAlias]['table'];
+
+ // check column existence
+ if ($table->hasField($term[0])) {
+ $found = true;
+
+ $def = $table->getDefinitionOf($term[0]);
+
+ // get the actual column name from field name
+ $term[0] = $table->getColumnName($term[0]);
+
+
+ if (isset($def['owner'])) {
+ $componentAlias = $componentAlias . '.' . $def['owner'];
+ }
+
+ $tableAlias = $this->getTableAlias($componentAlias);
+
+ if ($this->getType() === IPF_ORM_Query::SELECT) {
+ // build sql expression
+ $term[0] = $this->_conn->quoteIdentifier($tableAlias)
+ . '.'
+ . $this->_conn->quoteIdentifier($term[0]);
+ } else {
+ // build sql expression
+ $term[0] = $this->_conn->quoteIdentifier($term[0]);
+ }
+ } else {
+ $found = false;
+ }
+ }
+
+ if ( ! $found) {
+ $term[0] = $this->getSqlAggregateAlias($term[0]);
+ }
+ }
+ }
+ }
+ }
+
+ $str .= $term[0] . $term[1];
+ }
+ return $str;
+ }
+
+ public function parseIdentifierReference($expr)
+ {
+ }
+
+ public function parseFunctionExpression($expr)
+ {
+ $pos = strpos($expr, '(');
+
+ $name = substr($expr, 0, $pos);
+
+ if ($name === '') {
+ return $this->parseSubquery($expr);
+ }
+
+ $argStr = substr($expr, ($pos + 1), -1);
+
+ $args = array();
+ // parse args
+
+ foreach ($this->_tokenizer->sqlExplode($argStr, ',') as $arg) {
+ $args[] = $this->parseClause($arg);
+ }
+
+ // convert DQL function to its RDBMS specific equivalent
+ try {
+ $expr = call_user_func_array(array($this->_conn->expression, $name), $args);
+ } catch (IPF_ORM_Exception $e) {
+ throw new IPF_ORM_Exception('Unknown function ' . $name . '.');
+ }
+
+ return $expr;
+ }
+
+ public function parseSubquery($subquery)
+ {
+ $trimmed = trim($this->_tokenizer->bracketTrim($subquery));
+
+ // check for possible subqueries
+ if (substr($trimmed, 0, 4) == 'FROM' || substr($trimmed, 0, 6) == 'SELECT') {
+ // parse subquery
+ $trimmed = $this->createSubquery()->parseDqlQuery($trimmed)->getQuery();
+ } else {
+ // parse normal clause
+ $trimmed = $this->parseClause($trimmed);
+ }
+
+ return '(' . $trimmed . ')';
+ }
+
+ public function processPendingSubqueries()
+ {
+ foreach ($this->_pendingSubqueries as $value) {
+ list($dql, $alias) = $value;
+
+ $subquery = $this->createSubquery();
+
+ $sql = $subquery->parseDqlQuery($dql, false)->getQuery();
+
+ reset($this->_queryComponents);
+ $componentAlias = key($this->_queryComponents);
+ $tableAlias = $this->getTableAlias($componentAlias);
+
+ $sqlAlias = $tableAlias . '__' . count($this->_aggregateAliasMap);
+
+ $this->_sqlParts['select'][] = '(' . $sql . ') AS ' . $this->_conn->quoteIdentifier($sqlAlias);
+
+ $this->_aggregateAliasMap[$alias] = $sqlAlias;
+ $this->_queryComponents[$componentAlias]['agg'][] = $alias;
+ }
+ $this->_pendingSubqueries = array();
+ }
+
+ public function processPendingAggregates()
+ {
+ // iterate trhough all aggregates
+ foreach ($this->_pendingAggregates as $aggregate) {
+ list ($expression, $components, $alias) = $aggregate;
+
+ $tableAliases = array();
+
+ // iterate through the component references within the aggregate function
+ if ( ! empty ($components)) {
+ foreach ($components as $component) {
+
+ if (is_numeric($component)) {
+ continue;
+ }
+
+ $e = explode('.', $component);
+
+ $field = array_pop($e);
+ $componentAlias = implode('.', $e);
+
+ // check the existence of the component alias
+ if ( ! isset($this->_queryComponents[$componentAlias])) {
+ throw new IPF_ORM_Exception('Unknown component alias ' . $componentAlias);
+ }
+
+ $table = $this->_queryComponents[$componentAlias]['table'];
+
+ $field = $table->getColumnName($field);
+
+ // check column existence
+ if ( ! $table->hasColumn($field)) {
+ throw new IPF_ORM_Exception('Unknown column ' . $field);
+ }
+
+ $sqlTableAlias = $this->getSqlTableAlias($componentAlias);
+
+ $tableAliases[$sqlTableAlias] = true;
+
+ // build sql expression
+
+ $identifier = $this->_conn->quoteIdentifier($sqlTableAlias . '.' . $field);
+ $expression = str_replace($component, $identifier, $expression);
+ }
+ }
+
+ if (count($tableAliases) !== 1) {
+ $componentAlias = reset($this->_tableAliasMap);
+ $tableAlias = key($this->_tableAliasMap);
+ }
+
+ $index = count($this->_aggregateAliasMap);
+ $sqlAlias = $this->_conn->quoteIdentifier($tableAlias . '__' . $index);
+
+ $this->_sqlParts['select'][] = $expression . ' AS ' . $sqlAlias;
+
+ $this->_aggregateAliasMap[$alias] = $sqlAlias;
+ $this->_expressionMap[$alias][0] = $expression;
+
+ $this->_queryComponents[$componentAlias]['agg'][$index] = $alias;
+
+ $this->_neededTables[] = $tableAlias;
+ }
+ // reset the state
+ $this->_pendingAggregates = array();
+ }
+
+ protected function _buildSqlQueryBase()
+ {
+ switch ($this->_type) {
+ case self::DELETE:
+ $q = 'DELETE FROM ';
+ break;
+ case self::UPDATE:
+ $q = 'UPDATE ';
+ break;
+ case self::SELECT:
+ $distinct = ($this->_sqlParts['distinct']) ? 'DISTINCT ' : '';
+ $q = 'SELECT ' . $distinct . implode(', ', $this->_sqlParts['select']) . ' FROM ';
+ break;
+ }
+ return $q;
+ }
+
+ protected function _buildSqlFromPart()
+ {
+ $q = '';
+ foreach ($this->_sqlParts['from'] as $k => $part) {
+ if ($k === 0) {
+ $q .= $part;
+ continue;
+ }
+
+ // preserve LEFT JOINs only if needed
+ // Check if it's JOIN, if not add a comma separator instead of space
+ if (!preg_match('/\bJOIN\b/i', $part) && !isset($this->_pendingJoinConditions[$k])) {
+ $q .= ', ' . $part;
+ } else {
+ $e = explode(' ', $part);
+
+ if (substr($part, 0, 9) === 'LEFT JOIN') {
+ $aliases = array_merge($this->_subqueryAliases,
+ array_keys($this->_neededTables));
+
+ if ( ! in_array($e[3], $aliases) &&
+ ! in_array($e[2], $aliases) &&
+
+ ! empty($this->_pendingFields)) {
+ continue;
+ }
+
+ }
+
+ if (isset($this->_pendingJoinConditions[$k])) {
+ $parser = new IPF_ORM_JoinCondition($this, $this->_tokenizer);
+
+ if (strpos($part, ' ON ') !== false) {
+ $part .= ' AND ';
+ } else {
+ $part .= ' ON ';
+ }
+ $part .= $parser->parse($this->_pendingJoinConditions[$k]);
+
+ unset($this->_pendingJoinConditions[$k]);
+ }
+
+ $tableAlias = trim($e[3], '"');
+ $componentAlias = $this->getComponentAlias($tableAlias);
+
+ $string = $this->getInheritanceCondition($componentAlias);
+
+ if ($string) {
+ $q .= ' ' . $part . ' AND ' . $string;
+ } else {
+ $q .= ' ' . $part;
+ }
+ }
+
+ $this->_sqlParts['from'][$k] = $part;
+ }
+ return $q;
+ }
+
+ public function getSqlQuery($params = array())
+ {
+ if ($this->_state !== self::STATE_DIRTY) {
+ return $this->_sql;
+ }
+
+ // reset the state
+ if ( ! $this->isSubquery()) {
+ $this->_queryComponents = array();
+ $this->_pendingAggregates = array();
+ $this->_aggregateAliasMap = array();
+ }
+ $this->reset();
+
+ // invoke the preQuery hook
+ $this->_preQuery();
+
+ // process the DQL parts => generate the SQL parts.
+ // this will also populate the $_queryComponents.
+ foreach ($this->_dqlParts as $queryPartName => $queryParts) {
+ $this->_processDqlQueryPart($queryPartName, $queryParts);
+ }
+ $this->_state = self::STATE_CLEAN;
+
+ $params = $this->convertEnums($params);
+
+ // Proceed with the generated SQL
+
+ if (empty($this->_sqlParts['from'])) {
+ return false;
+ }
+
+ $map = reset($this->_queryComponents);
+ $table = $map['table'];
+ $rootAlias = key($this->_queryComponents);
+
+ $sql = array();
+ if ( ! empty($this->_pendingFields)) {
+ foreach ($this->_queryComponents as $alias => $map) {
+ $fieldSql = $this->processPendingFields($alias);
+ if ( ! empty($fieldSql)) {
+ $sql[] = $fieldSql;
+ }
+ }
+ }
+ if ( ! empty($sql)) {
+ array_unshift($this->_sqlParts['select'], implode(', ', $sql));
+ }
+
+ $this->_pendingFields = array();
+
+ // build the basic query
+ $q = $this->_buildSqlQueryBase();
+ $q .= $this->_buildSqlFromPart();
+
+ if ( ! empty($this->_sqlParts['set'])) {
+ $q .= ' SET ' . implode(', ', $this->_sqlParts['set']);
+ }
+
+ $string = $this->getInheritanceCondition($this->getRootAlias());
+
+ // apply inheritance to WHERE part
+ if ( ! empty($string)) {
+ if (substr($string, 0, 1) === '(' && substr($string, -1) === ')') {
+ $this->_sqlParts['where'][] = $string;
+ } else {
+ $this->_sqlParts['where'][] = '(' . $string . ')';
+ }
+ }
+
+ $q .= ( ! empty($this->_sqlParts['where']))? ' WHERE ' . implode(' AND ', $this->_sqlParts['where']) : '';
+ $q .= ( ! empty($this->_sqlParts['groupby']))? ' GROUP BY ' . implode(', ', $this->_sqlParts['groupby']) : '';
+ $q .= ( ! empty($this->_sqlParts['having']))? ' HAVING ' . implode(' AND ', $this->_sqlParts['having']): '';
+ $q .= ( ! empty($this->_sqlParts['orderby']))? ' ORDER BY ' . implode(', ', $this->_sqlParts['orderby']) : '';
+
+ $q = $this->_conn->modifyLimitQuery($q, $this->_sqlParts['limit'], $this->_sqlParts['offset']);
+
+ // return to the previous state
+ if ( ! empty($string)) {
+ array_pop($this->_sqlParts['where']);
+ }
+ $this->_sql = $q;
+
+ return $q;
+ }
+
+ public function parseDqlQuery($query, $clear = true)
+ {
+ if ($clear) {
+ $this->clear();
+ }
+
+ $query = trim($query);
+ $query = str_replace("\r", "\n", str_replace("\r\n", "\n", $query));
+ $query = str_replace("\n", ' ', $query);
+
+ $parts = $this->_tokenizer->tokenizeQuery($query);
+
+ foreach ($parts as $partName => $subParts) {
+ $subParts = trim($subParts);
+ $partName = strtolower($partName);
+ switch ($partName) {
+ case 'create':
+ $this->_type = self::CREATE;
+ break;
+ case 'insert':
+ $this->_type = self::INSERT;
+ break;
+ case 'delete':
+ $this->_type = self::DELETE;
+ break;
+ case 'select':
+ $this->_type = self::SELECT;
+ $this->_addDqlQueryPart($partName, $subParts);
+ break;
+ case 'update':
+ $this->_type = self::UPDATE;
+ $partName = 'from';
+ case 'from':
+ $this->_addDqlQueryPart($partName, $subParts);
+ break;
+ case 'set':
+ $this->_addDqlQueryPart($partName, $subParts, true);
+ break;
+ case 'group':
+ case 'order':
+ $partName .= 'by';
+ case 'where':
+ case 'having':
+ case 'limit':
+ case 'offset':
+ $this->_addDqlQueryPart($partName, $subParts);
+ break;
+ }
+ }
+
+ return $this;
+ }
+
+ public function load($path, $loadFields = true)
+ {
+ if (isset($this->_queryComponents[$path])) {
+ return $this->_queryComponents[$path];
+ }
+
+ $e = $this->_tokenizer->quoteExplode($path, ' INDEXBY ');
+
+ $mapWith = null;
+ if (count($e) > 1) {
+ $mapWith = trim($e[1]);
+
+ $path = $e[0];
+ }
+
+ // parse custom join conditions
+ $e = explode(' ON ', $path);
+
+ $joinCondition = '';
+
+ if (count($e) > 1) {
+ $joinCondition = $e[1];
+ $overrideJoin = true;
+ $path = $e[0];
+ } else {
+ $e = explode(' WITH ', $path);
+
+ if (count($e) > 1) {
+ $joinCondition = $e[1];
+ $path = $e[0];
+ }
+ $overrideJoin = false;
+ }
+
+ $tmp = explode(' ', $path);
+ $componentAlias = $originalAlias = (count($tmp) > 1) ? end($tmp) : null;
+
+ $e = preg_split("/[.:]/", $tmp[0], -1);
+
+ $fullPath = $tmp[0];
+ $prevPath = '';
+ $fullLength = strlen($fullPath);
+
+ if (isset($this->_queryComponents[$e[0]])) {
+ $table = $this->_queryComponents[$e[0]]['table'];
+ $componentAlias = $e[0];
+
+ $prevPath = $parent = array_shift($e);
+ }
+
+ foreach ($e as $key => $name) {
+ // get length of the previous path
+ $length = strlen($prevPath);
+
+ // build the current component path
+ $prevPath = ($prevPath) ? $prevPath . '.' . $name : $name;
+
+ $delimeter = substr($fullPath, $length, 1);
+
+ // if an alias is not given use the current path as an alias identifier
+ if (strlen($prevPath) === $fullLength && isset($originalAlias)) {
+ $componentAlias = $originalAlias;
+ } else {
+ $componentAlias = $prevPath;
+ }
+
+ // if the current alias already exists, skip it
+ if (isset($this->_queryComponents[$componentAlias])) {
+ throw new IPF_ORM_Exception("Duplicate alias '$componentAlias' in query.");
+ }
+
+ if ( ! isset($table)) {
+ // process the root of the path
+
+ $table = $this->loadRoot($name, $componentAlias);
+ } else {
+ $join = ($delimeter == ':') ? 'INNER JOIN ' : 'LEFT JOIN ';
+
+ $relation = $table->getRelation($name);
+ $localTable = $table;
+
+ $table = $relation->getTable();
+ $this->_queryComponents[$componentAlias] = array('table' => $table,
+ 'parent' => $parent,
+ 'relation' => $relation,
+ 'map' => null);
+
+ $localAlias = $this->getTableAlias($parent, $table->getTableName());
+ $foreignAlias = $this->getTableAlias($componentAlias, $relation->getTable()->getTableName());
+
+ $foreignSql = $this->_conn->quoteIdentifier($relation->getTable()->getTableName())
+ . ' '
+ . $this->_conn->quoteIdentifier($foreignAlias);
+
+ $map = $relation->getTable()->inheritanceMap;
+
+ if ( ! $loadFields || ! empty($map) || $joinCondition) {
+ $this->_subqueryAliases[] = $foreignAlias;
+ }
+
+ if ($relation instanceof IPF_ORM_Relation_Association) {
+ $asf = $relation->getAssociationTable();
+
+ $assocTableName = $asf->getTableName();
+
+ if ( ! $loadFields || ! empty($map) || $joinCondition) {
+ $this->_subqueryAliases[] = $assocTableName;
+ }
+
+ $assocPath = $prevPath . '.' . $asf->getComponentName();
+
+ $this->_queryComponents[$assocPath] = array('parent' => $prevPath, 'relation' => $relation, 'table' => $asf);
+
+ $assocAlias = $this->getTableAlias($assocPath, $asf->getTableName());
+
+ $queryPart = $join . $assocTableName . ' ' . $assocAlias;
+
+ $queryPart .= ' ON ' . $localAlias
+ . '.'
+ . $localTable->getColumnName($localTable->getIdentifier()) // what about composite keys?
+ . ' = '
+ . $assocAlias . '.' . $relation->getLocal();
+
+ if ($relation->isEqual()) {
+ // equal nest relation needs additional condition
+ $queryPart .= ' OR ' . $localAlias
+ . '.'
+ . $table->getColumnName($table->getIdentifier())
+ . ' = '
+ . $assocAlias . '.' . $relation->getForeign();
+ }
+
+ $this->_sqlParts['from'][] = $queryPart;
+
+ $queryPart = $join . $foreignSql;
+
+ if ( ! $overrideJoin) {
+ $queryPart .= $this->buildAssociativeRelationSql($relation, $assocAlias, $foreignAlias, $localAlias);
+ }
+ } else {
+ $queryPart = $this->buildSimpleRelationSql($relation, $foreignAlias, $localAlias, $overrideJoin, $join);
+ }
+
+ $queryPart .= $this->buildInheritanceJoinSql($table->getComponentName(), $componentAlias);
+
+ $this->_sqlParts['from'][$componentAlias] = $queryPart;
+ if ( ! empty($joinCondition)) {
+ $this->_pendingJoinConditions[$componentAlias] = $joinCondition;
+ }
+ }
+ if ($loadFields) {
+
+ $restoreState = false;
+ // load fields if necessary
+ if ($loadFields && empty($this->_dqlParts['select'])) {
+ $this->_pendingFields[$componentAlias] = array('*');
+ }
+ }
+ $parent = $prevPath;
+ }
+
+ $table = $this->_queryComponents[$componentAlias]['table'];
+
+ return $this->buildIndexBy($componentAlias, $mapWith);
+ }
+
+ protected function buildSimpleRelationSql(IPF_ORM_Relation $relation, $foreignAlias, $localAlias, $overrideJoin, $join)
+ {
+ $queryPart = $join . $this->_conn->quoteIdentifier($relation->getTable()->getTableName())
+ . ' '
+ . $this->_conn->quoteIdentifier($foreignAlias);
+
+ if ( ! $overrideJoin) {
+ $queryPart .= ' ON '
+ . $this->_conn->quoteIdentifier($localAlias . '.' . $relation->getLocal())
+ . ' = '
+ . $this->_conn->quoteIdentifier($foreignAlias . '.' . $relation->getForeign());
+ }
+
+ return $queryPart;
+ }
+
+ protected function buildIndexBy($componentAlias, $mapWith = null)
+ {
+ $table = $this->_queryComponents[$componentAlias]['table'];
+
+ $indexBy = null;
+
+ if (isset($mapWith)) {
+ $terms = explode('.', $mapWith);
+
+ if (isset($terms[1])) {
+ $indexBy = $terms[1];
+ }
+ } elseif ($table->getBoundQueryPart('indexBy') !== null) {
+ $indexBy = $table->getBoundQueryPart('indexBy');
+ }
+
+ if ($indexBy !== null) {
+ if ( ! $table->hasColumn($table->getColumnName($indexBy))) {
+ throw new IPF_ORM_Exception("Couldn't use key mapping. Column " . $indexBy . " does not exist.");
+ }
+
+ $this->_queryComponents[$componentAlias]['map'] = $indexBy;
+ }
+
+ return $this->_queryComponents[$componentAlias];
+ }
+
+
+ protected function buildAssociativeRelationSql(IPF_ORM_Relation $relation, $assocAlias, $foreignAlias, $localAlias)
+ {
+ $table = $relation->getTable();
+
+ $queryPart = ' ON ';
+
+ if ($relation->isEqual()) {
+ $queryPart .= '(';
+ }
+
+ $localIdentifier = $table->getColumnName($table->getIdentifier());
+
+ $queryPart .= $this->_conn->quoteIdentifier($foreignAlias . '.' . $localIdentifier)
+ . ' = '
+ . $this->_conn->quoteIdentifier($assocAlias . '.' . $relation->getForeign());
+
+ if ($relation->isEqual()) {
+ $queryPart .= ' OR '
+ . $this->_conn->quoteIdentifier($foreignAlias . '.' . $localIdentifier)
+ . ' = '
+ . $this->_conn->quoteIdentifier($assocAlias . '.' . $relation->getLocal())
+ . ') AND '
+ . $this->_conn->quoteIdentifier($foreignAlias . '.' . $localIdentifier)
+ . ' != '
+ . $this->_conn->quoteIdentifier($localAlias . '.' . $localIdentifier);
+ }
+
+ return $queryPart;
+ }
+
+ public function loadRoot($name, $componentAlias)
+ {
+ // get the connection for the component
+ $manager = IPF_ORM_Manager::getInstance();
+
+ $table = $this->_conn->getTable($name);
+ $tableName = $table->getTableName();
+
+ // get the short alias for this table
+ $tableAlias = $this->getTableAlias($componentAlias, $tableName);
+ // quote table name
+ $queryPart = $this->_conn->quoteIdentifier($tableName);
+
+ if ($this->_type === self::SELECT) {
+ $queryPart .= ' ' . $this->_conn->quoteIdentifier($tableAlias);
+ }
+
+ $this->_tableAliasMap[$tableAlias] = $componentAlias;
+
+ $queryPart .= $this->buildInheritanceJoinSql($name, $componentAlias);
+
+ $this->_sqlParts['from'][] = $queryPart;
+
+ $this->_queryComponents[$componentAlias] = array('table' => $table, 'map' => null);
+
+ return $table;
+ }
+
+ public function buildInheritanceJoinSql($name, $componentAlias)
+ {
+ // get the connection for the component
+ $manager = IPF_ORM_Manager::getInstance();
+
+ $table = $this->_conn->getTable($name);
+ $tableName = $table->getTableName();
+
+ // get the short alias for this table
+ $tableAlias = $this->getTableAlias($componentAlias, $tableName);
+
+ return '';
+ }
+
+ public function getCountQuery()
+ {
+ // triggers dql parsing/processing
+ $this->getQuery(); // this is ugly
+
+ // initialize temporary variables
+ $where = $this->_sqlParts['where'];
+ $having = $this->_sqlParts['having'];
+ $groupby = $this->_sqlParts['groupby'];
+ $map = reset($this->_queryComponents);
+ $componentAlias = key($this->_queryComponents);
+ $tableAlias = $this->_conn->quoteIdentifier($this->getTableAlias($componentAlias));
+ $table = $map['table'];
+
+ $idColumnNames = array();
+ foreach ($table->getIdentifierColumnNames() as $column) {
+ $idColumnNames[] = $tableAlias . '.' . $this->_conn->quoteIdentifier($column);
+ }
+
+ // build the query base
+ $q = 'SELECT COUNT(DISTINCT ' . implode(' || ', $idColumnNames) . ') AS num_results';
+
+ foreach ($this->_sqlParts['select'] as $field) {
+ if (strpos($field, '(') !== false) {
+ $q .= ', ' . $field;
+ }
+ }
+
+ $q .= ' FROM ' . $this->_buildSqlFromPart();
+
+ // append column aggregation inheritance (if needed)
+ $string = $this->getInheritanceCondition($this->getRootAlias());
+
+ if ( ! empty($string)) {
+ $where[] = $string;
+ }
+
+ // append conditions
+ $q .= ( ! empty($where)) ? ' WHERE ' . implode(' AND ', $where) : '';
+
+ if ( ! empty($groupby)) {
+ // Maintain existing groupby
+ $q .= ' GROUP BY ' . implode(', ', $groupby);
+ } else {
+ // Default groupby to primary identifier. Database defaults to this internally
+ // This is required for situations where the user has aggregate functions in the select part
+ // Without the groupby it fails
+ $q .= ' GROUP BY ' . implode(', ', $idColumnNames);
+ }
+
+ $q .= ( ! empty($having)) ? ' HAVING ' . implode(' AND ', $having): '';
+
+ return $q;
+ }
+
+ public function count($params = array())
+ {
+ $q = $this->getCountQuery();
+
+ if ( ! is_array($params)) {
+ $params = array($params);
+ }
+
+ $params = array_merge($this->_params['join'], $this->_params['where'], $this->_params['having'], $params);
+
+ $params = $this->convertEnums($params);
+
+ $results = $this->getConnection()->fetchAll($q, $params);
+
+ if (count($results) > 1) {
+ $count = count($results);
+ } else {
+ if (isset($results[0])) {
+ $results[0] = array_change_key_case($results[0], CASE_LOWER);
+ $count = $results[0]['num_results'];
+ } else {
+ $count = 0;
+ }
+ }
+
+ return (int) $count;
+ }
+
+ public function query($query, $params = array(), $hydrationMode = null)
+ {
+ $this->parseDqlQuery($query);
+ return $this->execute($params, $hydrationMode);
+ }
+
+ public function copy(IPF_ORM_Query $query = null)
+ {
+ if ( ! $query) {
+ $query = $this;
+ }
+
+ $new = clone $query;
+
+ return $new;
+ }
+
+ public function __clone()
+ {
+ $this->_parsers = array();
+ }
+
+ public function free()
+ {
+ $this->reset();
+ $this->_parsers = array();
+ $this->_dqlParts = array();
+ $this->_enumParams = array();
+ }
+
+ public function serialize()
+ {
+ $vars = get_object_vars($this);
+ }
+
+ public function unserialize($serialized)
+ {
+ }
+}
--- /dev/null
+<?php
+
+abstract class IPF_ORM_Query_Abstract
+{
+ const SELECT = 0;
+ const DELETE = 1;
+ const UPDATE = 2;
+ const INSERT = 3;
+ const CREATE = 4;
+
+ const STATE_CLEAN = 1;
+ const STATE_DIRTY = 2;
+ const STATE_DIRECT = 3;
+ const STATE_LOCKED = 4;
+
+ protected $_tableAliasMap = array();
+ protected $_view;
+ protected $_state = IPF_ORM_Query::STATE_CLEAN;
+ protected $_params = array('join' => array(),
+ 'where' => array(),
+ 'set' => array(),
+ 'having' => array());
+
+ protected $_resultCache;
+ protected $_expireResultCache = false;
+ protected $_resultCacheTTL;
+
+ protected $_queryCache;
+ protected $_expireQueryCache = false;
+ protected $_queryCacheTTL;
+
+ protected $_conn;
+
+ protected $_sqlParts = array(
+ 'select' => array(),
+ 'distinct' => false,
+ 'forUpdate' => false,
+ 'from' => array(),
+ 'set' => array(),
+ 'join' => array(),
+ 'where' => array(),
+ 'groupby' => array(),
+ 'having' => array(),
+ 'orderby' => array(),
+ 'limit' => false,
+ 'offset' => false,
+ );
+
+ protected $_dqlParts = array(
+ 'from' => array(),
+ 'select' => array(),
+ 'forUpdate' => false,
+ 'set' => array(),
+ 'join' => array(),
+ 'where' => array(),
+ 'groupby' => array(),
+ 'having' => array(),
+ 'orderby' => array(),
+ 'limit' => array(),
+ 'offset' => array(),
+ );
+
+ protected $_queryComponents = array();
+ protected $_type = self::SELECT;
+ protected $_hydrator;
+ protected $_tokenizer;
+ protected $_parser;
+ protected $_tableAliasSeeds = array();
+ protected $_options = array(
+ 'fetchMode' => IPF_ORM::FETCH_RECORD
+ );
+ protected $_enumParams = array();
+
+ protected $_pendingSetParams = array();
+ protected $_components;
+ protected $_preQueried = false;
+
+ public function __construct(IPF_ORM_Connection $connection = null,
+ IPF_ORM_Hydrator_Abstract $hydrator = null)
+ {
+ if ($connection === null) {
+ $connection = IPF_ORM_Manager::connection();
+ }
+ if ($hydrator === null) {
+ $hydrator = new IPF_ORM_Hydrator();
+ }
+ $this->_conn = $connection;
+ $this->_hydrator = $hydrator;
+ $this->_tokenizer = new IPF_ORM_Query_Tokenizer();
+ $this->_resultCacheTTL = $this->_conn->getAttribute(IPF_ORM::ATTR_RESULT_CACHE_LIFESPAN);
+ $this->_queryCacheTTL = $this->_conn->getAttribute(IPF_ORM::ATTR_QUERY_CACHE_LIFESPAN);
+ }
+
+ public function setOption($name, $value)
+ {
+ if ( ! isset($this->_options[$name])) {
+ throw new IPF_ORM_Exception('Unknown option ' . $name);
+ }
+ $this->_options[$name] = $value;
+ }
+
+ public function hasTableAlias($sqlTableAlias)
+ {
+ return $this->hasSqlTableAlias($sqlTableAlias);
+ }
+
+ public function hasSqlTableAlias($sqlTableAlias)
+ {
+ return (isset($this->_tableAliasMap[$sqlTableAlias]));
+ }
+
+ public function getTableAliases()
+ {
+ return $this->getTableAliasMap();
+ }
+
+ public function getTableAliasMap()
+ {
+ return $this->_tableAliasMap;
+ }
+
+ public function getQueryPart($part)
+ {
+ return $this->getSqlQueryPart($part);
+ }
+
+ public function getSqlQueryPart($part)
+ {
+ if ( ! isset($this->_sqlParts[$part])) {
+ throw new IPF_ORM_Exception('Unknown SQL query part ' . $part);
+ }
+ return $this->_sqlParts[$part];
+ }
+
+ public function setQueryPart($name, $part)
+ {
+ return $this->setSqlQueryPart($name, $part);
+ }
+
+ public function setSqlQueryPart($name, $part)
+ {
+ if ( ! isset($this->_sqlParts[$name])) {
+ throw new IPF_ORM_Exception('Unknown query part ' . $name);
+ }
+
+ if ($name !== 'limit' && $name !== 'offset') {
+ if (is_array($part)) {
+ $this->_sqlParts[$name] = $part;
+ } else {
+ $this->_sqlParts[$name] = array($part);
+ }
+ } else {
+ $this->_sqlParts[$name] = $part;
+ }
+
+ return $this;
+ }
+
+ public function addQueryPart($name, $part)
+ {
+ return $this->addSqlQueryPart($name, $part);
+ }
+
+ public function addSqlQueryPart($name, $part)
+ {
+ if ( ! isset($this->_sqlParts[$name])) {
+ throw new IPF_ORM_Exception('Unknown query part ' . $name);
+ }
+ if (is_array($part)) {
+ $this->_sqlParts[$name] = array_merge($this->_sqlParts[$name], $part);
+ } else {
+ $this->_sqlParts[$name][] = $part;
+ }
+ return $this;
+ }
+
+ public function removeQueryPart($name)
+ {
+ return $this->removeSqlQueryPart($name);
+ }
+
+ public function removeSqlQueryPart($name)
+ {
+ if ( ! isset($this->_sqlParts[$name])) {
+ throw new IPF_ORM_Exception('Unknown query part ' . $name);
+ }
+
+ if ($name == 'limit' || $name == 'offset') {
+ $this->_sqlParts[$name] = false;
+ } else {
+ $this->_sqlParts[$name] = array();
+ }
+
+ return $this;
+ }
+
+ public function removeDqlQueryPart($name)
+ {
+ if ( ! isset($this->_dqlParts[$name])) {
+ throw new IPF_ORM_Exception('Unknown query part ' . $name);
+ }
+
+ if ($name == 'limit' || $name == 'offset') {
+ $this->_dqlParts[$name] = false;
+ } else {
+ $this->_dqlParts[$name] = array();
+ }
+
+ return $this;
+ }
+
+ public function getParams($params = array())
+ {
+ return array_merge($this->_params['join'], $this->_params['set'], $this->_params['where'], $this->_params['having'], $params);
+ }
+
+ public function setParams(array $params = array()) {
+ $this->_params = $params;
+ }
+
+ public function setView(IPF_ORM_View $view)
+ {
+ $this->_view = $view;
+ }
+
+ public function getView()
+ {
+ return $this->_view;
+ }
+
+ public function convertEnums($params)
+ {
+ $table = $this->getRoot();
+
+ // $position tracks the position of the parameter, to ensure we're converting
+ // the right parameter value when simple ? placeholders are used.
+ // This only works because SET is only allowed in update statements and it's
+ // the first place where parameters can occur.. see issue #935
+ $position = 0;
+ foreach ($this->_pendingSetParams as $fieldName => $value) {
+ $e = explode('.', $fieldName);
+ $fieldName = isset($e[1]) ? $e[1]:$e[0];
+ if ($table->getTypeOf($fieldName) == 'enum') {
+ $value = $value === '?' ? $position : $value;
+ $this->addEnumParam($value, $table, $fieldName);
+ }
+ ++$position;
+ }
+ $this->_pendingSetParams = array();
+
+ foreach ($this->_enumParams as $key => $values) {
+ if (isset($params[$key])) {
+ if ( ! empty($values)) {
+ $params[$key] = $values[0]->enumIndex($values[1], $params[$key]);
+ }
+ }
+ }
+
+ return $params;
+ }
+
+ public function getInheritanceCondition($componentAlias)
+ {
+ $map = $this->_queryComponents[$componentAlias]['table']->inheritanceMap;
+
+ // No inheritance map so lets just return
+ if (empty($map)) {
+ return;
+ }
+
+ $tableAlias = $this->getSqlTableAlias($componentAlias);
+
+ if ($this->_type !== IPF_ORM_Query::SELECT) {
+ $tableAlias = '';
+ } else {
+ $tableAlias .= '.';
+ }
+
+ $field = key($map);
+ $value = current($map);
+ $identifier = $this->_conn->quoteIdentifier($tableAlias . $field);
+
+ return $identifier . ' = ' . $this->_conn->quote($value);;
+ }
+
+ public function getTableAlias($componentAlias, $tableName = null)
+ {
+ return $this->getSqlTableAlias($componentAlias, $tableName);
+ }
+
+ public function getSqlTableAlias($componentAlias, $tableName = null)
+ {
+ $alias = array_search($componentAlias, $this->_tableAliasMap);
+
+ if ($alias !== false) {
+ return $alias;
+ }
+
+ if ($tableName === null) {
+ throw new IPF_ORM_Exception("Couldn't get short alias for " . $componentAlias);
+ }
+
+ return $this->generateTableAlias($componentAlias, $tableName);
+ }
+
+ public function generateNewTableAlias($oldAlias)
+ {
+ return $this->generateNewSqlTableAlias($oldAlias);
+ }
+
+ public function generateNewSqlTableAlias($oldAlias)
+ {
+ if (isset($this->_tableAliasMap[$oldAlias])) {
+ // generate a new alias
+ $name = substr($oldAlias, 0, 1);
+ $i = ((int) substr($oldAlias, 1));
+
+ if ($i == 0) {
+ $i = 1;
+ }
+
+ $newIndex = ($this->_tableAliasSeeds[$name] + $i);
+
+ return $name . $newIndex;
+ }
+
+ return $oldAlias;
+ }
+
+ public function getTableAliasSeed($sqlTableAlias)
+ {
+ return $this->getSqlTableAliasSeed($sqlTableAlias);
+ }
+
+ public function getSqlTableAliasSeed($sqlTableAlias)
+ {
+ if ( ! isset($this->_tableAliasSeeds[$sqlTableAlias])) {
+ return 0;
+ }
+ return $this->_tableAliasSeeds[$sqlTableAlias];
+ }
+
+ public function hasAliasDeclaration($componentAlias)
+ {
+ return isset($this->_queryComponents[$componentAlias]);
+ }
+
+ public function getAliasDeclaration($componentAlias)
+ {
+ return $this->getQueryComponent($componentAlias);
+ }
+
+ public function getQueryComponent($componentAlias)
+ {
+ if ( ! isset($this->_queryComponents[$componentAlias])) {
+ throw new IPF_ORM_Exception('Unknown component alias ' . $componentAlias);
+ }
+
+ return $this->_queryComponents[$componentAlias];
+ }
+
+ public function copyAliases(IPF_ORM_Query_Abstract $query)
+ {
+ $this->_tableAliasMap = $query->_tableAliasMap;
+ $this->_queryComponents = $query->_queryComponents;
+ $this->_tableAliasSeeds = $query->_tableAliasSeeds;
+ return $this;
+ }
+
+ public function getRootAlias()
+ {
+ if ( ! $this->_queryComponents) {
+ $this->getSql();
+ }
+ reset($this->_queryComponents);
+
+ return key($this->_queryComponents);
+ }
+
+ public function getRootDeclaration()
+ {
+ $map = reset($this->_queryComponents);
+ return $map;
+ }
+
+ public function getRoot()
+ {
+ $map = reset($this->_queryComponents);
+
+ if ( ! isset($map['table'])) {
+ throw new IPF_ORM_Exception('Root component not initialized.');
+ }
+
+ return $map['table'];
+ }
+
+ public function generateTableAlias($componentAlias, $tableName)
+ {
+ return $this->generateSqlTableAlias($componentAlias, $tableName);
+ }
+
+ public function generateSqlTableAlias($componentAlias, $tableName)
+ {
+ preg_match('/([^_])/', $tableName, $matches);
+ $char = strtolower($matches[0]);
+
+ $alias = $char;
+
+ if ( ! isset($this->_tableAliasSeeds[$alias])) {
+ $this->_tableAliasSeeds[$alias] = 1;
+ }
+
+ while (isset($this->_tableAliasMap[$alias])) {
+ if ( ! isset($this->_tableAliasSeeds[$alias])) {
+ $this->_tableAliasSeeds[$alias] = 1;
+ }
+ $alias = $char . ++$this->_tableAliasSeeds[$alias];
+ }
+
+ $this->_tableAliasMap[$alias] = $componentAlias;
+
+ return $alias;
+ }
+
+ public function getComponentAlias($sqlTableAlias)
+ {
+ if ( ! isset($this->_tableAliasMap[$sqlTableAlias])) {
+ throw new IPF_ORM_Exception('Unknown table alias ' . $sqlTableAlias);
+ }
+ return $this->_tableAliasMap[$sqlTableAlias];
+ }
+
+ protected function _execute($params)
+ {
+ $params = $this->_conn->convertBooleans($params);
+
+ if ( ! $this->_view) {
+ if ($this->_queryCache || $this->_conn->getAttribute(IPF_ORM::ATTR_QUERY_CACHE)) {
+ $queryCacheDriver = $this->getQueryCacheDriver();
+ // calculate hash for dql query
+ $dql = $this->getDql();
+ $hash = md5($dql . 'IPF_ORM_QUERY_CACHE_SALT');
+ $cached = $queryCacheDriver->fetch($hash);
+ if ($cached) {
+ $query = $this->_constructQueryFromCache($cached);
+ } else {
+ $query = $this->getSqlQuery($params);
+ $serializedQuery = $this->getCachedForm($query);
+ $queryCacheDriver->save($hash, $serializedQuery, $this->getQueryCacheLifeSpan());
+ }
+ } else {
+ $query = $this->getSqlQuery($params);
+ }
+ $params = $this->convertEnums($params);
+ } else {
+ $query = $this->_view->getSelectSql();
+ }
+
+ if ($this->_type !== self::SELECT) {
+ return $this->_conn->exec($query, $params);
+ }
+
+ $stmt = $this->_conn->execute($query, $params);
+ return $stmt;
+ }
+
+ public function execute($params = array(), $hydrationMode = null)
+ {
+ $this->_preQuery();
+
+ if ($hydrationMode !== null) {
+ $this->_hydrator->setHydrationMode($hydrationMode);
+ }
+
+ $params = $this->getParams($params);
+
+ if ($this->_resultCache && $this->_type == self::SELECT) {
+ $cacheDriver = $this->getResultCacheDriver();
+
+ $dql = $this->getDql();
+ // calculate hash for dql query
+ $hash = md5($dql . var_export($params, true));
+
+ $cached = ($this->_expireResultCache) ? false : $cacheDriver->fetch($hash);
+
+ if ($cached === false) {
+ // cache miss
+ $stmt = $this->_execute($params);
+ $this->_hydrator->setQueryComponents($this->_queryComponents);
+ $result = $this->_hydrator->hydrateResultSet($stmt, $this->_tableAliasMap);
+
+ $cached = $this->getCachedForm($result);
+ $cacheDriver->save($hash, $cached, $this->getResultCacheLifeSpan());
+ } else {
+ $result = $this->_constructQueryFromCache($cached);
+ }
+ } else {
+ $stmt = $this->_execute($params);
+
+ if (is_integer($stmt)) {
+ $result = $stmt;
+ } else {
+ $this->_hydrator->setQueryComponents($this->_queryComponents);
+ $result = $this->_hydrator->hydrateResultSet($stmt, $this->_tableAliasMap);
+ }
+ }
+
+ return $result;
+ }
+
+ protected function _preQuery()
+ {
+ // Invoke preQuery() hook on IPF_ORM_Query for child classes which implement this hook
+ $this->preQuery();
+ }
+
+ public function preQuery()
+ {
+ }
+
+ protected function _constructQueryFromCache($cached)
+ {
+ $cached = unserialize($cached);
+ $this->_tableAliasMap = $cached[2];
+ $customComponent = $cached[0];
+
+ $queryComponents = array();
+ $cachedComponents = $cached[1];
+ foreach ($cachedComponents as $alias => $components) {
+ $e = explode('.', $components[0]);
+ if (count($e) === 1) {
+ $queryComponents[$alias]['table'] = $this->_conn->getTable($e[0]);
+ } else {
+ $queryComponents[$alias]['parent'] = $e[0];
+ $queryComponents[$alias]['relation'] = $queryComponents[$e[0]]['table']->getRelation($e[1]);
+ $queryComponents[$alias]['table'] = $queryComponents[$alias]['relation']->getTable();
+ }
+ if (isset($components[1])) {
+ $queryComponents[$alias]['agg'] = $components[1];
+ }
+ if (isset($components[2])) {
+ $queryComponents[$alias]['map'] = $components[2];
+ }
+ }
+ $this->_queryComponents = $queryComponents;
+
+ return $customComponent;
+ }
+
+ public function getCachedForm($customComponent = null)
+ {
+ $componentInfo = array();
+
+ foreach ($this->getQueryComponents() as $alias => $components) {
+ if ( ! isset($components['parent'])) {
+ $componentInfo[$alias][] = $components['table']->getComponentName();
+ } else {
+ $componentInfo[$alias][] = $components['parent'] . '.' . $components['relation']->getAlias();
+ }
+ if (isset($components['agg'])) {
+ $componentInfo[$alias][] = $components['agg'];
+ }
+ if (isset($components['map'])) {
+ $componentInfo[$alias][] = $components['map'];
+ }
+ }
+
+ return serialize(array($customComponent, $componentInfo, $this->getTableAliasMap()));
+ }
+
+ public function addSelect($select)
+ {
+ return $this->_addDqlQueryPart('select', $select, true);
+ }
+
+ public function addTableAlias($tableAlias, $componentAlias)
+ {
+ return $this->addSqlTableAlias($tableAlias, $componentAlias);
+ }
+
+ public function addSqlTableAlias($sqlTableAlias, $componentAlias)
+ {
+ $this->_tableAliasMap[$sqlTableAlias] = $componentAlias;
+ return $this;
+ }
+
+ public function addFrom($from)
+ {
+ return $this->_addDqlQueryPart('from', $from, true);
+ }
+
+ public function addWhere($where, $params = array())
+ {
+ if (is_array($params)) {
+ $this->_params['where'] = array_merge($this->_params['where'], $params);
+ } else {
+ $this->_params['where'][] = $params;
+ }
+ return $this->_addDqlQueryPart('where', $where, true);
+ }
+
+ public function whereIn($expr, $params = array(), $not = false)
+ {
+ $params = (array) $params;
+
+ // if there's no params, return (else we'll get a WHERE IN (), invalid SQL)
+ if (!count($params))
+ return $this;
+
+ $a = array();
+ foreach ($params as $k => $value) {
+ if ($value instanceof IPF_ORM_Expression) {
+ $value = $value->getSql();
+ unset($params[$k]);
+ } else {
+ $value = '?';
+ }
+ $a[] = $value;
+ }
+
+ $this->_params['where'] = array_merge($this->_params['where'], $params);
+
+ $where = $expr . ($not === true ? ' NOT ':'') . ' IN (' . implode(', ', $a) . ')';
+
+ return $this->_addDqlQueryPart('where', $where, true);
+ }
+
+ public function whereNotIn($expr, $params = array())
+ {
+ return $this->whereIn($expr, $params, true);
+ }
+
+ public function addGroupBy($groupby)
+ {
+ return $this->_addDqlQueryPart('groupby', $groupby, true);
+ }
+
+ public function addHaving($having, $params = array())
+ {
+ if (is_array($params)) {
+ $this->_params['having'] = array_merge($this->_params['having'], $params);
+ } else {
+ $this->_params['having'][] = $params;
+ }
+ return $this->_addDqlQueryPart('having', $having, true);
+ }
+
+ public function addOrderBy($orderby)
+ {
+ return $this->_addDqlQueryPart('orderby', $orderby, true);
+ }
+
+ public function select($select)
+ {
+ return $this->_addDqlQueryPart('select', $select);
+ }
+
+ public function distinct($flag = true)
+ {
+ $this->_sqlParts['distinct'] = (bool) $flag;
+ return $this;
+ }
+
+ public function forUpdate($flag = true)
+ {
+ $this->_sqlParts['forUpdate'] = (bool) $flag;
+ return $this;
+ }
+
+ public function delete()
+ {
+ $this->_type = self::DELETE;
+ return $this;
+ }
+
+ public function update($update)
+ {
+ $this->_type = self::UPDATE;
+ return $this->_addDqlQueryPart('from', $update);
+ }
+
+ public function set($key, $value, $params = null)
+ {
+ if (is_array($key)) {
+ foreach ($key as $k => $v) {
+ $this->set($k, '?', array($v));
+ }
+ return $this;
+ } else {
+ if ($params !== null) {
+ if (is_array($params)) {
+ $this->_params['set'] = array_merge($this->_params['set'], $params);
+ } else {
+ $this->_params['set'][] = $params;
+ }
+ }
+
+ $this->_pendingSetParams[$key] = $value;
+
+ return $this->_addDqlQueryPart('set', $key . ' = ' . $value, true);
+ }
+ }
+
+ public function from($from)
+ {
+ return $this->_addDqlQueryPart('from', $from);
+ }
+
+ public function innerJoin($join, $params = array())
+ {
+ if (is_array($params)) {
+ $this->_params['join'] = array_merge($this->_params['join'], $params);
+ } else {
+ $this->_params['join'][] = $params;
+ }
+
+ return $this->_addDqlQueryPart('from', 'INNER JOIN ' . $join, true);
+ }
+
+ public function leftJoin($join, $params = array())
+ {
+ if (is_array($params)) {
+ $this->_params['join'] = array_merge($this->_params['join'], $params);
+ } else {
+ $this->_params['join'][] = $params;
+ }
+
+ return $this->_addDqlQueryPart('from', 'LEFT JOIN ' . $join, true);
+ }
+
+ public function groupBy($groupby)
+ {
+ return $this->_addDqlQueryPart('groupby', $groupby);
+ }
+
+ public function where($where, $params = array())
+ {
+ $this->_params['where'] = array();
+ if (is_array($params)) {
+ $this->_params['where'] = $params;
+ } else {
+ $this->_params['where'][] = $params;
+ }
+
+ return $this->_addDqlQueryPart('where', $where);
+ }
+
+ public function having($having, $params = array())
+ {
+ $this->_params['having'] = array();
+ if (is_array($params)) {
+ $this->_params['having'] = $params;
+ } else {
+ $this->_params['having'][] = $params;
+ }
+
+ return $this->_addDqlQueryPart('having', $having);
+ }
+
+ public function orderBy($orderby)
+ {
+ return $this->_addDqlQueryPart('orderby', $orderby);
+ }
+
+ public function limit($limit)
+ {
+ return $this->_addDqlQueryPart('limit', $limit);
+ }
+
+ public function offset($offset)
+ {
+ return $this->_addDqlQueryPart('offset', $offset);
+ }
+
+ public function getSql($params = array())
+ {
+ return $this->getSqlQuery($params);
+ }
+
+ protected function clear()
+ {
+ $this->_sqlParts = array(
+ 'select' => array(),
+ 'distinct' => false,
+ 'forUpdate' => false,
+ 'from' => array(),
+ 'set' => array(),
+ 'join' => array(),
+ 'where' => array(),
+ 'groupby' => array(),
+ 'having' => array(),
+ 'orderby' => array(),
+ 'limit' => false,
+ 'offset' => false,
+ );
+ }
+
+ public function setHydrationMode($hydrationMode)
+ {
+ $this->_hydrator->setHydrationMode($hydrationMode);
+ return $this;
+ }
+
+ public function getAliasMap()
+ {
+ return $this->_queryComponents;
+ }
+
+ public function getQueryComponents()
+ {
+ return $this->_queryComponents;
+ }
+
+ public function getParts()
+ {
+ return $this->getSqlParts();
+ }
+
+ public function getSqlParts()
+ {
+ return $this->_sqlParts;
+ }
+
+ public function getType()
+ {
+ return $this->_type;
+ }
+
+ public function useCache($driver = true, $timeToLive = null)
+ {
+ return $this->useResultCache($driver, $timeToLive);
+ }
+
+ public function useResultCache($driver = true, $timeToLive = null)
+ {
+ if ($driver !== null && $driver !== true && ! ($driver instanceOf IPF_ORM_Cache_Interface)) {
+ $msg = 'First argument should be instance of IPF_ORM_Cache_Interface or null.';
+ throw new IPF_ORM_Exception($msg);
+ }
+ $this->_resultCache = $driver;
+
+ return $this->setResultCacheLifeSpan($timeToLive);
+ }
+
+ public function useQueryCache(IPF_ORM_Cache_Interface $driver, $timeToLive = null)
+ {
+ $this->_queryCache = $driver;
+ return $this->setQueryCacheLifeSpan($timeToLive);
+ }
+
+ public function expireCache($expire = true)
+ {
+ return $this->expireResultCache($expire);
+ }
+
+ public function expireResultCache($expire = true)
+ {
+ $this->_expireResultCache = true;
+ return $this;
+ }
+
+ public function expireQueryCache($expire = true)
+ {
+ $this->_expireQueryCache = true;
+ return $this;
+ }
+
+ public function setCacheLifeSpan($timeToLive)
+ {
+ return $this->setResultCacheLifeSpan($timeToLive);
+ }
+
+ public function setResultCacheLifeSpan($timeToLive)
+ {
+ if ($timeToLive !== null) {
+ $timeToLive = (int) $timeToLive;
+ }
+ $this->_resultCacheTTL = $timeToLive;
+
+ return $this;
+ }
+
+ public function getResultCacheLifeSpan()
+ {
+ return $this->_resultCacheTTL;
+ }
+
+ public function setQueryCacheLifeSpan($timeToLive)
+ {
+ if ($timeToLive !== null) {
+ $timeToLive = (int) $timeToLive;
+ }
+ $this->_queryCacheTTL = $timeToLive;
+
+ return $this;
+ }
+
+ public function getQueryCacheLifeSpan()
+ {
+ return $this->_queryCacheTTL;
+ }
+
+ public function getCacheDriver()
+ {
+ return $this->getResultCacheDriver();
+ }
+
+ public function getResultCacheDriver()
+ {
+ if ($this->_resultCache instanceof IPF_ORM_Cache_Interface) {
+ return $this->_resultCache;
+ } else {
+ return $this->_conn->getResultCacheDriver();
+ }
+ }
+
+ public function getQueryCacheDriver()
+ {
+ if ($this->_queryCache instanceof IPF_ORM_Cache_Interface) {
+ return $this->_queryCache;
+ } else {
+ return $this->_conn->getQueryCacheDriver();
+ }
+ }
+
+ public function getConnection()
+ {
+ return $this->_conn;
+ }
+
+ protected function _addDqlQueryPart($queryPartName, $queryPart, $append = false)
+ {
+ if ($append) {
+ $this->_dqlParts[$queryPartName][] = $queryPart;
+ } else {
+ $this->_dqlParts[$queryPartName] = array($queryPart);
+ }
+
+ $this->_state = IPF_ORM_Query::STATE_DIRTY;
+ return $this;
+ }
+
+ protected function _processDqlQueryPart($queryPartName, $queryParts)
+ {
+ $this->removeSqlQueryPart($queryPartName);
+
+ if (is_array($queryParts) && ! empty($queryParts)) {
+ foreach ($queryParts as $queryPart) {
+ $parser = $this->_getParser($queryPartName);
+ $sql = $parser->parse($queryPart);
+ if (isset($sql)) {
+ if ($queryPartName == 'limit' || $queryPartName == 'offset') {
+ $this->setSqlQueryPart($queryPartName, $sql);
+ } else {
+ $this->addSqlQueryPart($queryPartName, $sql);
+ }
+ }
+ }
+ }
+ }
+
+ protected function _getParser($name)
+ {
+ if ( ! isset($this->_parsers[$name])) {
+ $class = 'IPF_ORM_Query_' . ucwords(strtolower($name));
+
+ //IPF_ORM::autoload($class);
+
+ if ( ! class_exists($class)) {
+ throw new IPF_ORM_Exception('Unknown parser ' . $name);
+ }
+
+ $this->_parsers[$name] = new $class($this, $this->_tokenizer);
+ }
+
+ return $this->_parsers[$name];
+ }
+
+ abstract public function getSqlQuery($params = array());
+
+ abstract public function parseDqlQuery($query);
+
+ public function parseQuery($query)
+ {
+ return $this->parseDqlQuery($query);
+ }
+
+ public function getQuery($params = array())
+ {
+ return $this->getSqlQuery($params);
+ }
+
+ public function __debugInfo()
+ {
+ return array('sql' => $this->getQuery());
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Query_Check
+{
+ protected $table;
+
+ protected $sql;
+
+ protected $_tokenizer;
+
+ public function __construct($table)
+ {
+ if ( ! ($table instanceof IPF_ORM_Table)) {
+ $table = IPF_ORM_Manager::connection()->getTable($table);
+ }
+ $this->table = $table;
+ $this->_tokenizer = new IPF_ORM_Query_Tokenizer();
+ }
+
+ public function getTable()
+ {
+ return $this->table;
+ }
+
+ public function parse($dql)
+ {
+ $this->sql = $this->parseClause($dql);
+ }
+
+ public function parseClause($dql)
+ {
+ $parts = $this->_tokenizer->sqlExplode($dql, ' AND ');
+
+ if (count($parts) > 1) {
+ $ret = array();
+ foreach ($parts as $part) {
+ $ret[] = $this->parseSingle($part);
+ }
+
+ $r = implode(' AND ', $ret);
+ } else {
+ $parts = $this->_tokenizer->quoteExplode($dql, ' OR ');
+ if (count($parts) > 1) {
+ $ret = array();
+ foreach ($parts as $part) {
+ $ret[] = $this->parseClause($part);
+ }
+
+ $r = implode(' OR ', $ret);
+ } else {
+ $ret = $this->parseSingle($dql);
+ return $ret;
+ }
+ }
+ return '(' . $r . ')';
+ }
+
+ public function parseSingle($part)
+ {
+ $e = explode(' ', $part);
+
+ $e[0] = $this->parseFunction($e[0]);
+
+ switch ($e[1]) {
+ case '>':
+ case '<':
+ case '=':
+ case '!=':
+ case '<>':
+
+ break;
+ default:
+ throw new IPF_ORM_Exception('Unknown operator ' . $e[1]);
+ }
+
+ return implode(' ', $e);
+ }
+ public function parseFunction($dql)
+ {
+ if (($pos = strpos($dql, '(')) !== false) {
+ $func = substr($dql, 0, $pos);
+ $value = substr($dql, ($pos + 1), -1);
+
+ $expr = $this->table->getConnection()->expression;
+
+ if ( ! method_exists($expr, $func)) {
+ throw new IPF_ORM_Exception('Unknown function ' . $func);
+ }
+
+ $func = $expr->$func($value);
+ }
+ return $func;
+ }
+
+ public function getSql()
+ {
+ return $this->sql;
+ }
+}
--- /dev/null
+<?php
+
+abstract class IPF_ORM_Query_Condition extends IPF_ORM_Query_Part
+{
+ public function parse($str)
+ {
+ $tmp = trim($str);
+
+ $parts = $this->_tokenizer->bracketExplode($str, array(' \&\& ', ' AND '), '(', ')');
+
+ if (count($parts) > 1) {
+ $ret = array();
+ foreach ($parts as $part) {
+ $part = $this->_tokenizer->bracketTrim($part, '(', ')');
+ $ret[] = $this->parse($part);
+ }
+ $r = implode(' AND ', $ret);
+ } else {
+
+ $parts = $this->_tokenizer->bracketExplode($str, array(' \|\| ', ' OR '), '(', ')');
+ if (count($parts) > 1) {
+ $ret = array();
+ foreach ($parts as $part) {
+ $part = $this->_tokenizer->bracketTrim($part, '(', ')');
+ $ret[] = $this->parse($part);
+ }
+ $r = implode(' OR ', $ret);
+ } else {
+ // Fix for #710
+ if (substr($parts[0],0,1) == '(' && substr($parts[0], -1) == ')') {
+ return $this->parse(substr($parts[0], 1, -1));
+ } else {
+ // Processing NOT here
+ if (strtoupper(substr($parts[0], 0, 4)) === 'NOT ') {
+ $r = 'NOT ('.$this->parse(substr($parts[0], 4)).')';
+ } else {
+ return $this->load($parts[0]);
+ }
+ }
+ }
+ }
+
+ return '(' . $r . ')';
+ }
+
+ private function parseBoolean($value)
+ {
+ // parse booleans
+ if ($value == 'true') {
+ $value = 1;
+ } elseif ($value == 'false') {
+ $value = 0;
+ }
+ return $value;
+ }
+
+ public function parseLiteralValue($value)
+ {
+ // check that value isn't a string
+ if (strpos($value, '\'') === false) {
+ // parse booleans
+ $value = $this->parseBoolean($value);
+
+ $a = explode('.', $value);
+
+ if (count($a) > 1) {
+ // either a float or a component..
+
+ if ( ! is_numeric($a[0])) {
+ // a component found
+ $field = array_pop($a);
+ $reference = implode('.', $a);
+ $value = $this->query->getTableAlias($reference). '.' . $field;
+ }
+ }
+ } else {
+ // string literal found
+ }
+ return $value;
+ }
+}
--- /dev/null
+<?php
+
+class IPF_ORM_Query_Filter implements IPF_ORM_Query_Filter_Interface
+{
+ public function preQuery(IPF_ORM_Query $query)
+ {
+ }
+
+ public function postQuery(IPF_ORM_Query $query)
+ {
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+class IPF_ORM_Query_Filter_Chain
+{
+ protected $_filters = array();
+
+ public function add(IPF_ORM_Query_Filter $filter)
+ {
+ $this->_filters[] = $filter;
+ }
+
+ public function get($key)
+ {
+ if ( ! isset($this->_filters[$key])) {
+ throw new IPF_ORM_Exception('Unknown filter ' . $key);
+ }
+ return $this->_filters[$key];
+ }
+
+ public function set($key, IPF_ORM_Query_Filter $listener)
+ {
+ $this->_filters[$key] = $listener;
+ }
+
+ public function preQuery(IPF_ORM_Query $query)
+ {
+ foreach ($this->_filters as $filter) {
+ $filter->preQuery($query);
+ }
+ }
+
+ public function postQuery(IPF_ORM_Query $query)
+ {
+ foreach ($this->_filters as $filter) {
+ $filter->postQuery($query);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+interface IPF_ORM_Query_Filter_Interface
+{
+ public function preQuery(IPF_ORM_Query $query);
+ public function postQuery(IPF_ORM_Query $query);
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+class IPF_ORM_Query_From extends IPF_ORM_Query_Part
+{
+ public function parse($str, $return = false)
+ {
+ $str = trim($str);
+ $parts = $this->_tokenizer->bracketExplode($str, 'JOIN');
+
+ $from = $return ? array() : null;
+
+ $operator = false;
+
+ switch (trim($parts[0])) {
+ case 'INNER':
+ $operator = ':';
+ case 'LEFT':
+ array_shift($parts);
+ break;
+ }
+
+ $last = '';
+
+ foreach ($parts as $k => $part) {
+ $part = trim($part);
+
+ if (empty($part)) {
+ continue;
+ }
+
+ $e = explode(' ', $part);
+
+ if (end($e) == 'INNER' || end($e) == 'LEFT') {
+ $last = array_pop($e);
+ }
+ $part = implode(' ', $e);
+
+ foreach ($this->_tokenizer->bracketExplode($part, ',') as $reference) {
+ $reference = trim($reference);
+ $e = explode(' ', $reference);
+ $e2 = explode('.', $e[0]);
+
+ if ($operator) {
+ $e[0] = array_shift($e2) . $operator . implode('.', $e2);
+ }
+
+ if ($return) {
+ $from[] = $e;
+ } else {
+ $table = $this->query->load(implode(' ', $e));
+ }
+ }
+
+ $operator = ($last == 'INNER') ? ':' : '.';
+ }
+ return $from;
+ }
+}
--- /dev/null
+<?php
+
+class IPF_ORM_Query_Groupby extends IPF_ORM_Query_Part
+{
+ public function parse($str, $append = false)
+ {
+ $r = array();
+ foreach (explode(',', $str) as $reference) {
+ $reference = trim($reference);
+
+ $r[] = $this->query->parseClause($reference);
+ }
+ return implode(', ', $r);
+ }
+}
--- /dev/null
+<?php
+
+class IPF_ORM_Query_Having extends IPF_ORM_Query_Condition
+{
+ private function parseAggregateFunction($func)
+ {
+ $pos = strpos($func, '(');
+
+ if ($pos !== false) {
+ $funcs = array();
+
+ $name = substr($func, 0, $pos);
+ $func = substr($func, ($pos + 1), -1);
+ $params = $this->_tokenizer->bracketExplode($func, ',', '(', ')');
+
+ foreach ($params as $k => $param) {
+ $params[$k] = $this->parseAggregateFunction($param);
+ }
+
+ $funcs = $name . '(' . implode(', ', $params) . ')';
+
+ return $funcs;
+
+ } else {
+ if ( ! is_numeric($func)) {
+ $a = explode('.', $func);
+
+ if (count($a) > 1) {
+ $field = array_pop($a);
+ $reference = implode('.', $a);
+ $map = $this->query->load($reference, false);
+ $field = $map['table']->getColumnName($field);
+ $func = $this->query->getTableAlias($reference) . '.' . $field;
+ } else {
+ $field = end($a);
+ $func = $this->query->getAggregateAlias($field);
+ }
+ return $func;
+ } else {
+ return $func;
+ }
+ }
+ }
+
+ final public function load($having)
+ {
+ $tokens = $this->_tokenizer->bracketExplode($having, ' ', '(', ')');
+ $part = $this->parseAggregateFunction(array_shift($tokens));
+ $operator = array_shift($tokens);
+ $value = implode(' ', $tokens);
+ $part .= ' ' . $operator . ' ' . $value;
+ // check the RHS for aggregate functions
+ if (strpos($value, '(') !== false) {
+ $value = $this->parseAggregateFunction($value);
+ }
+ return $part;
+ }
+}
--- /dev/null
+<?php
+
+class IPF_ORM_Query_JoinCondition extends IPF_ORM_Query_Condition
+{
+ public function load($condition)
+ {
+ $condition = trim($condition);
+
+ $e = $this->_tokenizer->sqlExplode($condition);
+
+ if (count($e) > 2) {
+ $expr = new IPF_ORM_Expression($e[0], $this->query->getConnection());
+ $e[0] = $expr->getSql();
+
+ $operator = $e[1];
+
+ if (substr(trim($e[2]), 0, 1) != '(') {
+ $expr = new IPF_ORM_Expression($e[2], $this->query->getConnection());
+ $e[2] = $expr->getSql();
+ }
+
+ // We need to check for agg functions here
+ $hasLeftAggExpression = preg_match('/(.*)\(([^\)]*)\)([\)]*)/', $e[0], $leftMatches);
+
+ if ($hasLeftAggExpression) {
+ $e[0] = $leftMatches[2];
+ }
+
+ $hasRightAggExpression = preg_match('/(.*)\(([^\)]*)\)([\)]*)/', $e[2], $rightMatches);
+
+ if ($hasRightAggExpression) {
+ $e[2] = $rightMatches[2];
+ }
+
+ $a = explode('.', $e[0]);
+ $field = array_pop($a);
+ $reference = implode('.', $a);
+ $value = $e[2];
+
+ $conn = $this->query->getConnection();
+ $alias = $this->query->getTableAlias($reference);
+ $map = $this->query->getAliasDeclaration($reference);
+ $table = $map['table'];
+ // check if value is enumerated value
+ $enumIndex = $table->enumIndex($field, trim($value, "'"));
+
+ if (false !== $enumIndex && $conn->getAttribute(IPF_ORM::ATTR_USE_NATIVE_ENUM)) {
+ $enumIndex = $conn->quote($enumIndex, 'text');
+ }
+
+ // FIX: Issues with "(" XXX ")"
+ if ($hasRightAggExpression) {
+ $value = '(' . $value . ')';
+ }
+
+ if (substr($value, 0, 1) == '(') {
+ // trim brackets
+ $trimmed = $this->_tokenizer->bracketTrim($value);
+
+ if (substr($trimmed, 0, 4) == 'FROM' || substr($trimmed, 0, 6) == 'SELECT') {
+ // subquery found
+ $q = $this->query->createSubquery();
+
+ // Change due to bug "(" XXX ")"
+ //$value = '(' . $q->parseQuery($trimmed)->getQuery() . ')';
+ $value = $q->parseQuery($trimmed)->getQuery();
+ } elseif (substr($trimmed, 0, 4) == 'SQL:') {
+ // Change due to bug "(" XXX ")"
+ //$value = '(' . substr($trimmed, 4) . ')';
+ $value = substr($trimmed, 4);
+ } else {
+ // simple in expression found
+ $e = $this->_tokenizer->sqlExplode($trimmed, ',');
+
+ $value = array();
+ foreach ($e as $part) {
+ $index = $table->enumIndex($field, trim($part, "'"));
+
+ if (false !== $index && $conn->getAttribute(IPF_ORM::ATTR_USE_NATIVE_ENUM)) {
+ $index = $conn->quote($index, 'text');
+ }
+
+ if ($index !== false) {
+ $value[] = $index;
+ } else {
+ $value[] = $this->parseLiteralValue($part);
+ }
+ }
+
+ // Change due to bug "(" XXX ")"
+ //$value = '(' . implode(', ', $value) . ')';
+ $value = implode(', ', $value);
+ }
+ } else {
+ if ($enumIndex !== false) {
+ $value = $enumIndex;
+ } else {
+ $value = $this->parseLiteralValue($value);
+ }
+ }
+
+ switch ($operator) {
+ case '<':
+ case '>':
+ case '=':
+ case '!=':
+ if ($enumIndex !== false) {
+ $value = $enumIndex;
+ }
+ default:
+ $leftExpr = (($hasLeftAggExpression) ? $leftMatches[1] . '(' : '')
+ . $alias . '.' . $field
+ . (($hasLeftAggExpression) ? $leftMatches[3] . ')' : '') ;
+
+ $rightExpr = (($hasRightAggExpression) ? $rightMatches[1] . '(' : '')
+ . $value
+ . (($hasRightAggExpression) ? $rightMatches[3] . ')' : '') ;
+
+ $condition = $leftExpr . ' ' . $operator . ' ' . $rightExpr;
+ }
+
+ }
+
+ return $condition;
+ }
+}
--- /dev/null
+<?php
+
+class IPF_ORM_Query_Limit extends IPF_ORM_Query_Part
+{
+ public function parse($limit)
+ {
+ return (int) $limit;
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+class IPF_ORM_Query_Offset extends IPF_ORM_Query_Part
+{
+ public function parse($offset)
+ {
+ return (int) $offset;
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+class IPF_ORM_Query_Orderby extends IPF_ORM_Query_Part
+{
+ public function parse($str, $append = false)
+ {
+ $ret = array();
+
+ foreach (explode(',', trim($str)) as $r) {
+ $r = $this->query->parseClause($r);
+
+ $ret[] = $r;
+ }
+ return $ret;
+ }
+}
--- /dev/null
+<?php
+
+class IPF_ORM_Query_Parser
+{
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+abstract class IPF_ORM_Query_Part
+{
+ protected $query;
+
+ protected $_tokenizer;
+
+ public function __construct($query, IPF_ORM_Query_Tokenizer $tokenizer = null)
+ {
+ $this->query = $query;
+ if ( ! $tokenizer) {
+ $tokenizer = new IPF_ORM_Query_Tokenizer();
+ }
+ $this->_tokenizer = $tokenizer;
+ }
+
+ public function getQuery()
+ {
+ return $this->query;
+ }
+}
--- /dev/null
+<?php
+
+class IPF_ORM_Query_Registry
+{
+ protected $_queries = array();
+
+ public function add($key, $query)
+ {
+ if (strpos($key, '/') === false) {
+ $this->_queries[$key] = $query;
+ } else {
+ // namespace found
+ $e = explode('/', $key);
+
+ $this->_queries[$e[0]][$e[1]] = $query;
+ }
+ }
+
+ public function get($key, $namespace = null)
+ {
+ if (isset($namespace)) {
+ if ( ! isset($this->_queries[$namespace][$key])) {
+ throw new IPF_ORM_Exception('A query with the name ' . $namespace . '/' . $key . ' does not exist.');
+ }
+ $query = $this->_queries[$namespace][$key];
+ } else {
+ if ( ! isset($this->_queries[$key])) {
+ throw new IPF_ORM_Exception('A query with the name ' . $key . ' does not exist.');
+ }
+ $query = $this->_queries[$key];
+ }
+
+ if ( ! ($query instanceof IPF_ORM_Query)) {
+ $query = IPF_ORM_Query::create()->parseQuery($query);
+ }
+
+ return $query;
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+class IPF_ORM_Query_Select extends IPF_ORM_Query_Part
+{
+ public function parse($dql)
+ {
+ $this->query->parseSelect($dql);
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+class IPF_ORM_Query_Set extends IPF_ORM_Query_Part
+{
+ public function parse($dql)
+ {
+ $terms = $this->_tokenizer->sqlExplode($dql, ' ');
+ foreach ($terms as $term) {
+ preg_match_all("/[a-z0-9_]+\.[a-z0-9_]+[\.[a-z0-9]+]*/i", $term, $m);
+
+ if (isset($m[0])) {
+ foreach ($m[0] as $part) {
+ $e = explode('.', trim($part));
+ $field = array_pop($e);
+
+ $reference = implode('.', $e);
+
+ $alias = $this->query->getTableAlias($reference);
+ $map = $this->query->getAliasDeclaration($reference);
+
+ $dql = str_replace($part, $map['table']->getColumnName($field), $dql);
+ }
+ }
+ }
+ return $dql;
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+class IPF_ORM_Query_Tokenizer
+{
+ public function tokenizeQuery($query)
+ {
+ $parts = array();
+ $tokens = $this->sqlExplode($query, ' ');
+
+ foreach ($tokens as $index => $token) {
+ $token = trim($token);
+ switch (strtolower($token)) {
+ case 'delete':
+ case 'update':
+ case 'select':
+ case 'set':
+ case 'from':
+ case 'where':
+ case 'limit':
+ case 'offset':
+ case 'having':
+ $p = $token;
+ //$parts[$token] = array();
+ $parts[$token] = '';
+ break;
+ case 'order':
+ case 'group':
+ $i = ($index + 1);
+ if (isset($tokens[$i]) && strtolower($tokens[$i]) === 'by') {
+ $p = $token;
+ $parts[$token] = '';
+ //$parts[$token] = array();
+ } else {
+ $parts[$p] .= "$token ";
+ //$parts[$p][] = $token;
+ }
+ break;
+ case 'by':
+ continue;
+ default:
+ if ( ! isset($p)) {
+ throw new IPF_ORM_Exception(
+ "Couldn't tokenize query. Encountered invalid token: '$token'.");
+ }
+
+ $parts[$p] .= "$token ";
+ //$parts[$p][] = $token;
+ }
+ }
+ return $parts;
+ }
+
+ public function bracketTrim($str, $e1 = '(', $e2 = ')')
+ {
+ if (substr($str, 0, 1) === $e1 && substr($str, -1) === $e2) {
+ return substr($str, 1, -1);
+ } else {
+ return $str;
+ }
+ }
+
+ public function bracketExplode($str, $d = ' ', $e1 = '(', $e2 = ')')
+ {
+ if (is_array($d)) {
+ $a = preg_split('#('.implode('|', $d).')#i', $str);
+ $d = stripslashes($d[0]);
+ } else {
+ $a = explode($d, $str);
+ }
+
+ $i = 0;
+ $term = array();
+ foreach($a as $key=>$val) {
+ if (empty($term[$i])) {
+ $term[$i] = trim($val);
+ $s1 = substr_count($term[$i], $e1);
+ $s2 = substr_count($term[$i], $e2);
+
+ if ($s1 == $s2) {
+ $i++;
+ }
+ } else {
+ $term[$i] .= $d . trim($val);
+ $c1 = substr_count($term[$i], $e1);
+ $c2 = substr_count($term[$i], $e2);
+
+ if ($c1 == $c2) {
+ $i++;
+ }
+ }
+ }
+ return $term;
+ }
+
+ public function quoteExplode($str, $d = ' ')
+ {
+ if (is_array($d)) {
+ $a = preg_split('/('.implode('|', $d).')/', $str);
+ $d = stripslashes($d[0]);
+ } else {
+ $a = explode($d, $str);
+ }
+
+ $i = 0;
+ $term = array();
+ foreach ($a as $key => $val) {
+ if (empty($term[$i])) {
+ $term[$i] = trim($val);
+
+ if ( ! (substr_count($term[$i], "'") & 1)) {
+ $i++;
+ }
+ } else {
+ $term[$i] .= $d . trim($val);
+
+ if ( ! (substr_count($term[$i], "'") & 1)) {
+ $i++;
+ }
+ }
+ }
+ return $term;
+ }
+
+ public function sqlExplode($str, $d = ' ', $e1 = '(', $e2 = ')')
+ {
+ if ($d == ' ') {
+ $d = array(' ', '\s');
+ }
+ if (is_array($d)) {
+ $d = array_map('preg_quote', $d);
+
+ if (in_array(' ', $d)) {
+ $d[] = '\s';
+ }
+
+ $split = '#(' . implode('|', $d) . ')#';
+
+ $str = preg_split($split, $str);
+ $d = stripslashes($d[0]);
+ } else {
+ $str = explode($d, $str);
+ }
+
+ $i = 0;
+ $term = array();
+
+ foreach ($str as $key => $val) {
+ if (empty($term[$i])) {
+ $term[$i] = trim($val);
+
+ $s1 = substr_count($term[$i], $e1);
+ $s2 = substr_count($term[$i], $e2);
+
+ if (strpos($term[$i], '(') !== false) {
+ if ($s1 == $s2) {
+ $i++;
+ }
+ } else {
+ if ( ! (substr_count($term[$i], "'") & 1) &&
+ ! (substr_count($term[$i], "\"") & 1)) {
+ $i++;
+ }
+ }
+ } else {
+ $term[$i] .= $d . trim($val);
+ $c1 = substr_count($term[$i], $e1);
+ $c2 = substr_count($term[$i], $e2);
+
+ if (strpos($term[$i], '(') !== false) {
+ if ($c1 == $c2) {
+ $i++;
+ }
+ } else {
+ if ( ! (substr_count($term[$i], "'") & 1) &&
+ ! (substr_count($term[$i], "\"") & 1)) {
+ $i++;
+ }
+ }
+ }
+ }
+ return $term;
+ }
+
+ public function clauseExplode($str, array $d, $e1 = '(', $e2 = ')')
+ {
+ if (is_array($d)) {
+ $d = array_map('preg_quote', $d);
+
+ if (in_array(' ', $d)) {
+ $d[] = '\s';
+ }
+
+ $split = '#(' . implode('|', $d) . ')#';
+
+ $str = preg_split($split, $str, -1, PREG_SPLIT_DELIM_CAPTURE);
+ }
+
+ $i = 0;
+ $term = array();
+
+ foreach ($str as $key => $val) {
+ if ($key & 1) {
+ if (isset($term[($i - 1)]) && ! is_array($term[($i - 1)])) {
+ $term[($i - 1)] = array($term[($i - 1)], $val);
+ }
+ continue;
+ }
+ if (empty($term[$i])) {
+ $term[$i] = $val;
+ } else {
+ $term[$i] .= $str[($key - 1)] . $val;
+ }
+
+ $c1 = substr_count($term[$i], $e1);
+ $c2 = substr_count($term[$i], $e2);
+
+ if (strpos($term[$i], '(') !== false) {
+ if ($c1 == $c2) {
+ $i++;
+ }
+ } else {
+ if ( ! (substr_count($term[$i], "'") & 1) &&
+ ! (substr_count($term[$i], "\"") & 1)) {
+ $i++;
+ }
+ }
+ }
+
+ if (isset($term[$i - 1])) {
+ $term[$i - 1] = array($term[$i - 1], '');
+ }
+
+ return $term;
+ }
+}
--- /dev/null
+<?php
+
+class IPF_ORM_Query_Where extends IPF_ORM_Query_Condition
+{
+ public function load($where)
+ {
+ $where = $this->_tokenizer->bracketTrim(trim($where));
+ $conn = $this->query->getConnection();
+ $terms = $this->_tokenizer->sqlExplode($where);
+
+ if (count($terms) > 1) {
+ if (substr($where, 0, 6) == 'EXISTS') {
+ return $this->parseExists($where, true);
+ } elseif (substr($where, 0, 10) == 'NOT EXISTS') {
+ return $this->parseExists($where, false);
+ }
+ }
+
+ if (count($terms) < 3) {
+ $terms = $this->_tokenizer->sqlExplode($where, array('=', '<', '<>', '>', '!='));
+ }
+
+ if (count($terms) > 1) {
+ $first = array_shift($terms);
+ $value = array_pop($terms);
+ $operator = trim(substr($where, strlen($first), -strlen($value)));
+ $table = null;
+ $field = null;
+
+ if (strpos($first, "'") === false && strpos($first, '(') === false) {
+ // normal field reference found
+ $a = explode('.', $first);
+
+ $field = array_pop($a);
+ $reference = implode('.', $a);
+
+ if (empty($reference)) {
+ $map = $this->query->getRootDeclaration();
+
+ $alias = $this->query->getTableAlias($this->query->getRootAlias());
+ $table = $map['table'];
+ } else {
+ $map = $this->query->load($reference, false);
+
+ $alias = $this->query->getTableAlias($reference);
+ $table = $map['table'];
+ }
+ }
+ $first = $this->query->parseClause($first);
+
+ $sql = $first . ' ' . $operator . ' ' . $this->parseValue($value, $table, $field);
+
+ return $sql;
+ } else {
+ return $where;
+ }
+ }
+
+ public function parseValue($value, IPF_ORM_Table $table = null, $field = null)
+ {
+ $conn = $this->query->getConnection();
+
+ if (substr($value, 0, 1) == '(') {
+ // trim brackets
+ $trimmed = $this->_tokenizer->bracketTrim($value);
+
+ if (substr($trimmed, 0, 4) == 'FROM' ||
+ substr($trimmed, 0, 6) == 'SELECT') {
+
+ // subquery found
+ $q = new IPF_ORM_Query();
+ $value = '(' . $this->query->createSubquery()->parseQuery($trimmed, false)->getQuery() . ')';
+
+ } elseif (substr($trimmed, 0, 4) == 'SQL:') {
+ $value = '(' . substr($trimmed, 4) . ')';
+ } else {
+ // simple in expression found
+ $e = $this->_tokenizer->sqlExplode($trimmed, ',');
+
+ $value = array();
+
+ $index = false;
+
+ foreach ($e as $part) {
+ if (isset($table) && isset($field)) {
+ $index = $table->enumIndex($field, trim($part, "'"));
+
+ if (false !== $index && $conn->getAttribute(IPF_ORM::ATTR_USE_NATIVE_ENUM)) {
+ $index = $conn->quote($index, 'text');
+ }
+ }
+
+ if ($index !== false) {
+ $value[] = $index;
+ } else {
+ $value[] = $this->parseLiteralValue($part);
+ }
+ }
+
+ $value = '(' . implode(', ', $value) . ')';
+ }
+ } else if (substr($value, 0, 1) == ':' || $value === '?') {
+ // placeholder found
+ if (isset($table) && isset($field) && $table->getTypeOf($field) == 'enum') {
+ $this->query->addEnumParam($value, $table, $field);
+ } else {
+ $this->query->addEnumParam($value, null, null);
+ }
+ } else {
+ $enumIndex = false;
+ if (isset($table) && isset($field)) {
+ // check if value is enumerated value
+ $enumIndex = $table->enumIndex($field, trim($value, "'"));
+
+ if (false !== $enumIndex && $conn->getAttribute(IPF_ORM::ATTR_USE_NATIVE_ENUM)) {
+ $enumIndex = $conn->quote($enumIndex, 'text');
+ }
+ }
+
+ if ($enumIndex !== false) {
+ $value = $enumIndex;
+ } else {
+ $value = $this->parseLiteralValue($value);
+ }
+ }
+ return $value;
+ }
+
+ public function parseExists($where, $negation)
+ {
+ $operator = ($negation) ? 'EXISTS' : 'NOT EXISTS';
+
+ $pos = strpos($where, '(');
+
+ if ($pos == false) {
+ throw new IPF_ORM_Exception('Unknown expression, expected a subquery with () -marks');
+ }
+
+ $sub = $this->_tokenizer->bracketTrim(substr($where, $pos));
+
+ return $operator . ' (' . $this->query->createSubquery()->parseQuery($sub, false)->getQuery() . ')';
+ }
+}
--- /dev/null
+<?php
+
+class IPF_ORM_RawSql extends IPF_ORM_Query_Abstract
+{
+ private $fields = array();
+
+ public function parseDqlQueryPart($queryPartName, $queryPart, $append=false)
+ {
+ if ($queryPartName == 'select') {
+ $this->_parseSelectFields($queryPart, $append);
+ return $this;
+ }
+
+ if (!isset($this->parts[$queryPartName])) {
+ $this->_sqlParts[$queryPartName] = array();
+ }
+
+ if (!$append) {
+ $this->_sqlParts[$queryPartName] = array($queryPart);
+ } else {
+ $this->_sqlParts[$queryPartName][] = $queryPart;
+ }
+ return $this;
+ }
+
+ protected function _addDqlQueryPart($queryPartName, $queryPart, $append=false)
+ {
+ return $this->parseDqlQueryPart($queryPartName, $queryPart, $append);
+ }
+
+ private function _parseSelectFields($queryPart, $append=false)
+ {
+ if ($append)
+ $this->fields[] = $queryPart;
+ else
+ $this->fields = array($queryPart);
+
+ $this->_sqlParts['select'] = array();
+ }
+
+ public function parseDqlQuery($query)
+ {
+ $this->_parseSelectFields($query);
+ $this->clear();
+
+ $tokens = $this->_tokenizer->sqlExplode($query, ' ');
+
+ $parts = array();
+ foreach ($tokens as $key => $part) {
+ $partLowerCase = strtolower($part);
+ switch ($partLowerCase) {
+ case 'select':
+ case 'from':
+ case 'where':
+ case 'limit':
+ case 'offset':
+ case 'having':
+ $type = $partLowerCase;
+ if ( ! isset($parts[$partLowerCase])) {
+ $parts[$partLowerCase] = array();
+ }
+ break;
+ case 'order':
+ case 'group':
+ $i = $key + 1;
+ if (isset($tokens[$i]) && strtolower($tokens[$i]) === 'by') {
+ $type = $partLowerCase . 'by';
+ $parts[$type] = array();
+ } else {
+ //not a keyword so we add it to the previous type
+ $parts[$type][] = $part;
+ }
+ break;
+ case 'by':
+ continue;
+ default:
+ //not a keyword so we add it to the previous type.
+ if ( ! isset($parts[$type][0])) {
+ $parts[$type][0] = $part;
+ } else {
+ // why does this add to index 0 and not append to the
+ // array. If it had done that one could have used
+ // parseQueryPart.
+ $parts[$type][0] .= ' '.$part;
+ }
+ }
+ }
+
+ $this->_sqlParts = $parts;
+ $this->_sqlParts['select'] = array();
+
+ return $this;
+ }
+
+ public function getSqlQuery($params=array())
+ {
+ $select = array();
+
+ foreach ($this->fields as $field) {
+ if (preg_match('/^{([^}{]+)\.([^}{]+)}$/U', $field, $e)) {
+ // try to auto-add component
+ if (!$this->hasSqlTableAlias($e[1])) {
+ try {
+ $this->addComponent($e[1], ucwords($e[1]));
+ } catch (IPF_ORM_Exception $exception) {
+ throw new IPF_ORM_Exception('The associated component for table alias ' . $e[1] . ' couldn\'t be found.');
+ }
+ }
+
+ $componentAlias = $this->getComponentAlias($e[1]);
+
+ if ($e[2] == '*') {
+ foreach ($this->_queryComponents[$componentAlias]['table']->getColumnNames() as $name) {
+ $field = $e[1] . '.' . $name;
+
+ $select[$componentAlias][$field] = $field . ' AS ' . $e[1] . '__' . $name;
+ }
+ } else {
+ $field = $e[1] . '.' . $e[2];
+ $select[$componentAlias][$field] = $field . ' AS ' . $e[1] . '__' . $e[2];
+ }
+ } else {
+ $select['__raw__'][] = $field;
+ }
+ }
+
+ // force-add all primary key fields
+
+ foreach ($this->getTableAliasMap() as $tableAlias => $componentAlias) {
+ $map = $this->_queryComponents[$componentAlias];
+
+ foreach ((array) $map['table']->getIdentifierColumnNames() as $key) {
+ $field = $tableAlias . '.' . $key;
+
+ if ( ! isset($this->_sqlParts['select'][$field])) {
+ $select[$componentAlias][$field] = $field . ' AS ' . $tableAlias . '__' . $key;
+ }
+ }
+ }
+
+ // first add the fields of the root component
+ reset($this->_queryComponents);
+ $componentAlias = key($this->_queryComponents);
+
+ $q = 'SELECT ' . implode(', ', $select[$componentAlias]);
+ unset($select[$componentAlias]);
+
+ foreach ($select as $component => $fields) {
+ if ( ! empty($fields)) {
+ $q .= ', ' . implode(', ', $fields);
+ }
+ }
+
+ $string = $this->getInheritanceCondition($this->getRootAlias());
+ if ( ! empty($string)) {
+ $this->_sqlParts['where'][] = $string;
+ }
+ $copy = $this->_sqlParts;
+ unset($copy['select']);
+
+ $q .= ( ! empty($this->_sqlParts['from']))? ' FROM ' . implode(' ', $this->_sqlParts['from']) : '';
+ $q .= ( ! empty($this->_sqlParts['where']))? ' WHERE ' . implode(' AND ', $this->_sqlParts['where']) : '';
+ $q .= ( ! empty($this->_sqlParts['groupby']))? ' GROUP BY ' . implode(', ', $this->_sqlParts['groupby']) : '';
+ $q .= ( ! empty($this->_sqlParts['having']))? ' HAVING ' . implode(' AND ', $this->_sqlParts['having']) : '';
+ $q .= ( ! empty($this->_sqlParts['orderby']))? ' ORDER BY ' . implode(', ', $this->_sqlParts['orderby']) : '';
+ $q .= ( ! empty($this->_sqlParts['limit']))? ' LIMIT ' . implode(' ', $this->_sqlParts['limit']) : '';
+ $q .= ( ! empty($this->_sqlParts['offset']))? ' OFFSET ' . implode(' ', $this->_sqlParts['offset']) : '';
+
+ if ( ! empty($string)) {
+ array_pop($this->_sqlParts['where']);
+ }
+ return $q;
+ }
+
+ public function getFields()
+ {
+ return $this->fields;
+ }
+
+ public function addComponent($tableAlias, $path)
+ {
+ $tmp = explode(' ', $path);
+ $originalAlias = (count($tmp) > 1) ? end($tmp) : null;
+
+ $e = explode('.', $tmp[0]);
+
+ $fullPath = $tmp[0];
+ $fullLength = strlen($fullPath);
+
+ $table = null;
+
+ $currPath = '';
+
+ if (isset($this->_queryComponents[$e[0]])) {
+ $table = $this->_queryComponents[$e[0]]['table'];
+ $currPath = $parent = array_shift($e);
+ }
+
+ foreach ($e as $k => $component) {
+ // get length of the previous path
+ $length = strlen($currPath);
+
+ // build the current component path
+ $currPath = ($currPath) ? $currPath . '.' . $component : $component;
+
+ $delimeter = substr($fullPath, $length, 1);
+
+ // if an alias is not given use the current path as an alias identifier
+ if (strlen($currPath) === $fullLength && isset($originalAlias)) {
+ $componentAlias = $originalAlias;
+ } else {
+ $componentAlias = $currPath;
+ }
+
+ if (!isset($table)) {
+ $table = IPF_ORM_Manager::connection()->getTable($component);
+ $this->_queryComponents[$componentAlias] = array('table' => $table);
+ } else {
+ $relation = $table->getRelation($component);
+
+ $this->_queryComponents[$componentAlias] = array('table' => $relation->getTable(),
+ 'parent' => $parent,
+ 'relation' => $relation);
+ }
+ $this->addSqlTableAlias($tableAlias, $componentAlias);
+
+ $parent = $currPath;
+ }
+
+ return $this;
+ }
+}
+
--- /dev/null
+<?php
+
+abstract class IPF_ORM_Record extends IPF_ORM_Record_Abstract implements Countable, IteratorAggregate, Serializable
+{
+ const STATE_DIRTY = 1;
+ const STATE_TDIRTY = 2;
+ const STATE_CLEAN = 3;
+ const STATE_PROXY = 4;
+ const STATE_TCLEAN = 5;
+ const STATE_LOCKED = 6;
+
+ protected $_id = array();
+ protected $_data = array();
+ protected $_values = array();
+ protected $_state;
+ protected $_modified = array();
+ protected $_errors = array();
+ protected $_references = array();
+ protected $_pendingDeletes = array();
+ protected $_custom = array();
+ private static $_index = 1;
+ private $_oid;
+
+ public function __construct($table = null, $isNewEntry = false)
+ {
+ if (isset($table) && $table instanceof IPF_ORM_Table) {
+ $this->_table = $table;
+ $exists = ( ! $isNewEntry);
+ } else {
+ // get the table of this class
+ $class = get_class($this);
+ $this->_table = IPF_ORM::getTable($class);
+ $exists = false;
+ }
+
+ // Check if the current connection has the records table in its registry
+ // If not this record is only used for creating table definition and setting up
+ // relations.
+ if ( ! $this->_table->getConnection()->hasTable($this->_table->getComponentName())) {
+ return;
+ }
+
+ $this->_oid = self::$_index;
+
+ self::$_index++;
+
+ // get the data array
+ $this->_data = $this->_table->getData();
+
+ // get the column count
+ $count = count($this->_data);
+
+ $this->_values = $this->cleanData($this->_data);
+
+ $this->prepareIdentifiers($exists);
+
+ if ( ! $exists) {
+ if ($count > count($this->_values)) {
+ $this->_state = IPF_ORM_Record::STATE_TDIRTY;
+ } else {
+ $this->_state = IPF_ORM_Record::STATE_TCLEAN;
+ }
+
+ // set the default values for this record
+ $this->assignDefaultValues();
+ } else {
+ $this->_state = IPF_ORM_Record::STATE_CLEAN;
+
+ if ($count < $this->_table->getColumnCount()) {
+ $this->_state = IPF_ORM_Record::STATE_PROXY;
+ }
+ }
+
+ $repository = $this->_table->getRepository();
+ $repository->add($this);
+
+ $this->construct();
+ }
+
+ public static function _index()
+ {
+ return self::$_index;
+ }
+
+ public function construct(){}
+
+ public function getOid()
+ {
+ return $this->_oid;
+ }
+
+ public function oid()
+ {
+ return $this->_oid;
+ }
+
+ public function isValid()
+ {
+ // Clear the stack from any previous errors.
+ $this->_errors = array();
+
+ // Run validation process
+ $validator = new IPF_ORM_Validator();
+ $validator->validateRecord($this);
+ $this->validate();
+ if ($this->_state == self::STATE_TDIRTY || $this->_state == self::STATE_TCLEAN) {
+ $this->validateOnInsert();
+ } else {
+ $this->validateOnUpdate();
+ }
+
+ return count($this->_errors) === 0;
+ }
+
+ public function addError($invalidFieldName, $errorCode = 'general')
+ {
+ $this->_errors[$invalidFieldName][] = $errorCode;
+ }
+
+ public function getErrors()
+ {
+ return $this->_errors;
+ }
+
+ protected function validate(){}
+
+ protected function validateOnUpdate(){}
+
+ protected function validateOnInsert(){}
+
+ public function preSerialize($event){}
+
+ public function postSerialize($event){}
+
+ public function preUnserialize($event){}
+
+ public function postUnserialize($event){}
+
+ public function preSave($event){}
+
+ public function postSave($event){}
+
+ public function preDelete($event){}
+
+ public function postDelete($event){}
+
+ public function preUpdate($event){}
+
+ public function postUpdate($event){}
+
+ public function preInsert($event){}
+
+ public function postInsert($event){}
+
+ public function assignDefaultValues($overwrite = false)
+ {
+ if ( ! $this->_table->hasDefaultValues()) {
+ return false;
+ }
+ foreach ($this->_data as $column => $value) {
+ $default = $this->_table->getDefaultValueOf($column);
+
+ if ($default === null) {
+ continue;
+ }
+
+ if (IPF_ORM_Null::isNull($value) || $overwrite) {
+ $this->_data[$column] = $default;
+ $this->_modified[] = $column;
+ $this->_state = IPF_ORM_Record::STATE_TDIRTY;
+ }
+ }
+ }
+
+ public function cleanData(&$data)
+ {
+ $tmp = $data;
+ $data = array();
+
+ foreach ($this->getTable()->getFieldNames() as $fieldName) {
+ if (isset($tmp[$fieldName])) {
+ $data[$fieldName] = $tmp[$fieldName];
+ } else if (array_key_exists($fieldName, $tmp)) {
+ $data[$fieldName] = IPF_ORM_Null::getInstance();
+ } else if (!isset($this->_data[$fieldName])) {
+ $data[$fieldName] = IPF_ORM_Null::getInstance();
+ }
+ unset($tmp[$fieldName]);
+ }
+
+ return $tmp;
+ }
+
+ public function hydrate(array $data)
+ {
+ $this->_values = array_merge($this->_values, $this->cleanData($data));
+ $this->_data = array_merge($this->_data, $data);
+ $this->prepareIdentifiers(true);
+ }
+
+ private function prepareIdentifiers($exists = true)
+ {
+ switch ($this->_table->getIdentifierType()) {
+ case IPF_ORM::IDENTIFIER_AUTOINC:
+ case IPF_ORM::IDENTIFIER_NATURAL:
+ $name = $this->_table->getIdentifier();
+ if (is_array($name)) {
+ $name = $name[0];
+ }
+ if ($exists) {
+ if (isset($this->_data[$name]) && !IPF_ORM_Null::isNull($this->_data[$name])) {
+ $this->_id[$name] = $this->_data[$name];
+ }
+ }
+ break;
+ case IPF_ORM::IDENTIFIER_COMPOSITE:
+ $names = $this->_table->getIdentifier();
+
+ foreach ($names as $name) {
+ if (IPF_ORM_Null::isNull($this->_data[$name])) {
+ $this->_id[$name] = null;
+ } else {
+ $this->_id[$name] = $this->_data[$name];
+ }
+ }
+ break;
+ }
+ }
+
+ public function serialize()
+ {
+ $event = new IPF_ORM_Event($this, IPF_ORM_Event::RECORD_SERIALIZE);
+
+ $this->preSerialize($event);
+
+ $vars = get_object_vars($this);
+
+ unset($vars['_references']);
+ unset($vars['_table']);
+ unset($vars['_errors']);
+ unset($vars['_filter']);
+
+ $name = $this->_table->getIdentifier();
+ $this->_data = array_merge($this->_data, $this->_id);
+
+ foreach ($this->_data as $k => $v) {
+ if ($v instanceof IPF_ORM_Record && $this->_table->getTypeOf($k) != 'object') {
+ unset($vars['_data'][$k]);
+ } elseif (IPF_ORM_Null::isNull($v)) {
+ unset($vars['_data'][$k]);
+ } else {
+ switch ($this->_table->getTypeOf($k)) {
+ case 'array':
+ case 'object':
+ $vars['_data'][$k] = serialize($vars['_data'][$k]);
+ break;
+ case 'gzip':
+ $vars['_data'][$k] = gzcompress($vars['_data'][$k]);
+ break;
+ case 'enum':
+ $vars['_data'][$k] = $this->_table->enumIndex($k, $vars['_data'][$k]);
+ break;
+ }
+ }
+ }
+
+ $str = serialize($vars);
+
+ $this->postSerialize($event);
+
+ return $str;
+ }
+
+ public function unserialize($serialized)
+ {
+ $event = new IPF_ORM_Event($this, IPF_ORM_Event::RECORD_UNSERIALIZE);
+
+ $this->preUnserialize($event);
+
+ $connection = IPF_ORM_Manager::connection();
+
+ $this->_oid = self::$_index;
+ self::$_index++;
+
+ $this->_table = $connection->getTable(get_class($this));
+
+ $array = unserialize($serialized);
+
+ foreach($array as $k => $v) {
+ $this->$k = $v;
+ }
+
+ foreach ($this->_data as $k => $v) {
+ switch ($this->_table->getTypeOf($k)) {
+ case 'array':
+ case 'object':
+ $this->_data[$k] = unserialize($this->_data[$k]);
+ break;
+ case 'gzip':
+ $this->_data[$k] = gzuncompress($this->_data[$k]);
+ break;
+ case 'enum':
+ $this->_data[$k] = $this->_table->enumValue($k, $this->_data[$k]);
+ break;
+
+ }
+ }
+
+ $this->_table->getRepository()->add($this);
+
+ $this->cleanData($this->_data);
+
+ $this->prepareIdentifiers($this->exists());
+
+ $this->postUnserialize($event);
+ }
+
+ public function state($state = null)
+ {
+ if ($state == null) {
+ return $this->_state;
+ }
+
+ $err = false;
+ if (is_integer($state)) {
+ if ($state >= 1 && $state <= 6) {
+ $this->_state = $state;
+ } else {
+ $err = true;
+ }
+ } else if (is_string($state)) {
+ $upper = strtoupper($state);
+
+ $const = 'IPF_ORM_Record::STATE_' . $upper;
+ if (defined($const)) {
+ $this->_state = constant($const);
+ } else {
+ $err = true;
+ }
+ }
+
+ if ($this->_state === IPF_ORM_Record::STATE_TCLEAN ||
+ $this->_state === IPF_ORM_Record::STATE_CLEAN) {
+ $this->_modified = array();
+ }
+
+ if ($err) {
+ throw new IPF_ORM_Exception('Unknown record state ' . $state);
+ }
+ }
+
+ public function refresh($deep = false)
+ {
+ $id = $this->identifier();
+ if ( ! is_array($id)) {
+ $id = array($id);
+ }
+ if (empty($id)) {
+ return false;
+ }
+ $id = array_values($id);
+
+ if ($deep) {
+ $query = $this->getTable()->createQuery();
+ foreach (array_keys($this->_references) as $name) {
+ $query->leftJoin(get_class($this) . '.' . $name);
+ }
+ $query->where(implode(' = ? AND ', $this->getTable()->getIdentifierColumnNames()) . ' = ?');
+ $this->clearRelated();
+ $record = $query->fetchOne($id);
+ } else {
+ // Use FETCH_ARRAY to avoid clearing object relations
+ $record = $this->getTable()->find($id, IPF_ORM::HYDRATE_ARRAY);
+ if ($record) {
+ $this->hydrate($record);
+ }
+ }
+
+ if ($record === false) {
+ throw new IPF_ORM_Exception('Failed to refresh. Record does not exist.');
+ }
+
+ $this->_modified = array();
+
+ $this->prepareIdentifiers();
+
+ $this->_state = IPF_ORM_Record::STATE_CLEAN;
+
+ return $this;
+ }
+
+ public function refreshRelated($name = null)
+ {
+ if (is_null($name)) {
+ foreach ($this->_table->getRelations() as $rel) {
+ $this->_references[$rel->getAlias()] = $rel->fetchRelatedFor($this);
+ }
+ } else {
+ $rel = $this->_table->getRelation($name);
+ $this->_references[$name] = $rel->fetchRelatedFor($this);
+ }
+ }
+
+ public function clearRelated()
+ {
+ $this->_references = array();
+ }
+
+ public function getTable()
+ {
+ return $this->_table;
+ }
+
+ public function getData()
+ {
+ return $this->_data;
+ }
+
+ public function rawGet($fieldName)
+ {
+ if ( ! isset($this->_data[$fieldName])) {
+ throw new IPF_ORM_Exception('Unknown property '. $fieldName);
+ }
+ if (IPF_ORM_Null::isNull($this->_data[$fieldName])) {
+ return null;
+ }
+
+ return $this->_data[$fieldName];
+ }
+
+ public function load()
+ {
+ // only load the data from database if the IPF_ORM_Record is in proxy state
+ if ($this->_state == IPF_ORM_Record::STATE_PROXY) {
+ $this->refresh();
+ $this->_state = IPF_ORM_Record::STATE_CLEAN;
+ return true;
+ }
+ return false;
+ }
+
+ public function get($fieldName, $load = true)
+ {
+ $value = IPF_ORM_Null::getInstance();
+
+ if (isset($this->_data[$fieldName])) {
+ if (IPF_ORM_Null::isNull($this->_data[$fieldName]) && $load) {
+ $this->load();
+ }
+ if (IPF_ORM_Null::isNull($this->_data[$fieldName])) {
+ $value = null;
+ } else {
+ $value = $this->_data[$fieldName];
+ }
+ return $value;
+ }
+
+ if (isset($this->_values[$fieldName])) {
+ return $this->_values[$fieldName];
+ }
+
+ if (!isset($this->_references[$fieldName]) && $load) {
+ $rel = $this->_table->getRelation($fieldName);
+ $this->_references[$fieldName] = $rel->fetchRelatedFor($this);
+ }
+ return $this->_references[$fieldName];
+ }
+
+ public function mapValue($name, $value)
+ {
+ $this->_values[$name] = $value;
+ }
+
+ public function set($fieldName, $value, $load = true)
+ {
+ if (isset($this->_data[$fieldName])) {
+ $type = $this->_table->getTypeOf($fieldName);
+ if ($value instanceof IPF_ORM_Record) {
+ $id = $value->getIncremented();
+
+ if ($id !== null && $type !== 'object') {
+ $value = $id;
+ }
+ }
+
+ if ($load) {
+ $old = $this->get($fieldName, $load);
+ } else {
+ $old = $this->_data[$fieldName];
+ }
+
+ if ($this->_isValueModified($type, $old, $value)) {
+ if ($value === null) {
+ $value = IPF_ORM_Null::getInstance();
+ }
+
+ $this->_data[$fieldName] = $value;
+ $this->_modified[] = $fieldName;
+ switch ($this->_state) {
+ case IPF_ORM_Record::STATE_CLEAN:
+ $this->_state = IPF_ORM_Record::STATE_DIRTY;
+ break;
+ case IPF_ORM_Record::STATE_TCLEAN:
+ $this->_state = IPF_ORM_Record::STATE_TDIRTY;
+ break;
+ }
+ }
+ } else {
+ $this->coreSetRelated($fieldName, $value);
+ }
+
+ return $this;
+ }
+
+ protected function _isValueModified($type, $old, $new)
+ {
+ if ($type == 'boolean' && (is_bool($old) || is_numeric($old)) && (is_bool($new) || is_numeric($new)) && $old == $new) {
+ return false;
+ } else {
+ return $old !== $new;
+ }
+ }
+
+ public function coreSetRelated($name, $value)
+ {
+ $rel = $this->_table->getRelation($name);
+
+ if ($value === null) {
+ $value = IPF_ORM_Null::getInstance();
+ }
+
+ // one-to-many or one-to-one relation
+ if ($rel instanceof IPF_ORM_Relation_ForeignKey || $rel instanceof IPF_ORM_Relation_LocalKey) {
+ if ( ! $rel->isOneToOne()) {
+ // one-to-many relation found
+ if ( ! ($value instanceof IPF_ORM_Collection)) {
+ throw new IPF_ORM_Exception("Couldn't call IPF_ORM::set(), second argument should be an instance of IPF_ORM_Collection when setting one-to-many references.");
+ }
+ if (isset($this->_references[$name])) {
+ $this->_references[$name]->setData($value->getData());
+ return $this;
+ }
+ } else {
+ if (!IPF_ORM_Null::isNull($value)) {
+ $relatedTable = $value->getTable();
+ $foreignFieldName = $relatedTable->getFieldName($rel->getForeign());
+ $localFieldName = $this->_table->getFieldName($rel->getLocal());
+
+ // one-to-one relation found
+ if ( ! ($value instanceof IPF_ORM_Record)) {
+ throw new IPF_ORM_Exception("Couldn't call IPF_ORM::set(), second argument should be an instance of IPF_ORM_Record or IPF_ORM_Null when setting one-to-one references.");
+ }
+ if ($rel instanceof IPF_ORM_Relation_LocalKey) {
+ if ( ! empty($foreignFieldName) && $foreignFieldName != $value->getTable()->getIdentifier()) {
+ $this->set($localFieldName, $value->rawGet($foreignFieldName), false);
+ } else {
+ $this->set($localFieldName, $value, false);
+ }
+ } else {
+ $value->set($foreignFieldName, $this, false);
+ }
+ }
+ }
+
+ } else if ($rel instanceof IPF_ORM_Relation_Association) {
+ // join table relation found
+ if ( ! ($value instanceof IPF_ORM_Collection)) {
+ throw new IPF_ORM_Exception("Couldn't call IPF_ORM::set(), second argument should be an instance of IPF_ORM_Collection when setting many-to-many references.");
+ }
+ }
+
+ $this->_references[$name] = $value;
+ }
+
+ public function contains($fieldName)
+ {
+ if (isset($this->_data[$fieldName])) {
+ // this also returns true if the field is a IPF_ORM_Null.
+ // imho this is not correct behavior.
+ return true;
+ }
+ if (isset($this->_id[$fieldName])) {
+ return true;
+ }
+ if (isset($this->_values[$fieldName])) {
+ return true;
+ }
+ if (isset($this->_references[$fieldName]) && !IPF_ORM_Null::isNull($this->_references[$fieldName])) {
+ return true;
+ }
+ return false;
+ }
+
+ public function __unset($name)
+ {
+ if (isset($this->_data[$name])) {
+ $this->_data[$name] = array();
+ } else if (isset($this->_references[$name])) {
+ if ($this->_references[$name] instanceof IPF_ORM_Record) {
+ $this->_pendingDeletes[] = $this->$name;
+ $this->_references[$name] = IPF_ORM_Null::getInstance();
+ } elseif ($this->_references[$name] instanceof IPF_ORM_Collection) {
+ $this->_pendingDeletes[] = $this->$name;
+ $this->_references[$name]->setData(array());
+ }
+ }
+ }
+
+ public function getPendingDeletes()
+ {
+ return $this->_pendingDeletes;
+ }
+
+ public function save(IPF_ORM_Connection $conn = null)
+ {
+ if ($conn === null) {
+ $conn = $this->_table->getConnection();
+ }
+ $conn->unitOfWork->saveGraph($this);
+ }
+
+ public function trySave(IPF_ORM_Connection $conn = null) {
+ try {
+ $this->save($conn);
+ return true;
+ } catch (IPF_ORM_Exception_Validator $ignored) {
+ return false;
+ }
+ }
+
+ public function replace(IPF_ORM_Connection $conn = null)
+ {
+ if ($conn === null) {
+ $conn = $this->_table->getConnection();
+ }
+
+ if ($this->exists()) {
+ return $this->save();
+ } else {
+ $identifier = (array) $this->getTable()->getIdentifier();
+ return $conn->replace($this->_table, $this->toArray(), $identifier);
+ }
+ }
+
+ public function getModified()
+ {
+ $a = array();
+
+ foreach ($this->_modified as $k => $v) {
+ $a[$v] = $this->_data[$v];
+ }
+ return $a;
+ }
+
+ public function modifiedFields()
+ {
+ $a = array();
+
+ foreach ($this->_modified as $k => $v) {
+ $a[$v] = $this->_data[$v];
+ }
+ return $a;
+ }
+
+ public function getPrepared(array $array = array())
+ {
+ $a = array();
+
+ if (empty($array)) {
+ $modifiedFields = $this->_modified;
+ }
+
+ foreach ($modifiedFields as $field) {
+ $type = $this->_table->getTypeOf($field);
+
+ if (IPF_ORM_Null::isNull($this->_data[$field])) {
+ $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 'enum':
+ $a[$field] = $this->_table->enumIndex($field, $this->_data[$field]);
+ break;
+ default:
+ if ($this->_data[$field] instanceof IPF_ORM_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 IPF_ORM_Record_Exception('Unexpected null value.');
+ }
+ */
+ }
+ }
+ $map = $this->_table->inheritanceMap;
+ foreach ($map as $k => $v) {
+ $k = $this->_table->getFieldName($k);
+ $old = $this->get($k, false);
+
+ if ((string) $old !== (string) $v || $old === null) {
+ $a[$k] = $v;
+ $this->_data[$k] = $v;
+ }
+ }
+
+ return $a;
+ }
+
+ public function count()
+ {
+ return count($this->_data);
+ }
+
+ public function columnCount()
+ {
+ return $this->count();
+ }
+
+ public function toArray($deep = true, $prefixKey = false)
+ {
+ if ($this->_state == self::STATE_LOCKED) {
+ return false;
+ }
+
+ $stateBeforeLock = $this->_state;
+ $this->_state = self::STATE_LOCKED;
+
+ $a = array();
+
+ foreach ($this as $column => $value) {
+ if (IPF_ORM_Null::isNull($value) || is_object($value)) {
+ $value = null;
+ }
+
+ $a[$column] = $value;
+ }
+
+ if ($this->_table->getIdentifierType() == IPF_ORM::IDENTIFIER_AUTOINC) {
+ $i = $this->_table->getIdentifier();
+ $a[$i] = $this->getIncremented();
+ }
+
+ if ($deep) {
+ foreach ($this->_references as $key => $relation) {
+ if (!IPF_ORM_Null::isNull($relation)) {
+ $a[$key] = $relation->toArray($deep, $prefixKey);
+ }
+ }
+ }
+
+ // [FIX] Prevent mapped IPF_ORM_Records from being displayed fully
+ foreach ($this->_values as $key => $value) {
+ if ($value instanceof IPF_ORM_Record) {
+ $a[$key] = $value->toArray($deep, $prefixKey);
+ } else {
+ $a[$key] = $value;
+ }
+ }
+
+ $this->_state = $stateBeforeLock;
+
+ return $a;
+ }
+
+ public function merge($data, $deep = true)
+ {
+ if ($data instanceof $this) {
+ $array = $data->toArray($deep);
+ } else if (is_array($data)) {
+ $array = $data;
+ }
+
+ return $this->fromArray($array, $deep);
+ }
+
+ public function fromArray(array $array, $deep = true)
+ {
+ $refresh = false;
+ foreach ($array as $key => $value) {
+ if ($key == '_identifier') {
+ $refresh = true;
+ $this->assignIdentifier((array) $value);
+ continue;
+ }
+
+ if ($deep && $this->getTable()->hasRelation($key)) {
+ $this->$key->fromArray($value, $deep);
+ } else if ($this->getTable()->hasField($key)) {
+ $this->set($key, $value);
+ }
+ }
+
+ if ($refresh) {
+ $this->refresh();
+ }
+ }
+
+ protected function _synchronizeWithArrayForRelation($key, $value)
+ {
+ $this->get($key)->synchronizeWithArray($value);
+ }
+
+ public function synchronizeWithArray(array $array, $deep = true)
+ {
+ $refresh = false;
+ foreach ($array as $key => $value) {
+ if ($key == '_identifier') {
+ $refresh = true;
+ $this->assignIdentifier((array) $value);
+ continue;
+ }
+ if ($deep && $this->getTable()->hasRelation($key)) {
+ $this->_synchronizeWithArrayForRelation($key, $value);
+ } else if ($this->getTable()->hasField($key)) {
+ $this->set($key, $value);
+ }
+ }
+ // eliminate relationships missing in the $array
+ foreach ($this->_references as $name => $obj) {
+ if ( ! isset($array[$name])) {
+ unset($this->$name);
+ }
+ }
+ if ($refresh) {
+ $this->refresh();
+ }
+ }
+
+ public function exists()
+ {
+ return ($this->_state !== IPF_ORM_Record::STATE_TCLEAN &&
+ $this->_state !== IPF_ORM_Record::STATE_TDIRTY);
+ }
+
+ public function isModified()
+ {
+ return ($this->_state === IPF_ORM_Record::STATE_DIRTY ||
+ $this->_state === IPF_ORM_Record::STATE_TDIRTY);
+ }
+
+ public function hasRelation($fieldName)
+ {
+ if (isset($this->_data[$fieldName]) || isset($this->_id[$fieldName])) {
+ return true;
+ }
+ return $this->_table->hasRelation($fieldName);
+ }
+
+ public function getIterator()
+ {
+ return new IPF_ORM_Record_Iterator($this);
+ }
+
+ public function delete(IPF_ORM_Connection $conn = null)
+ {
+ if ($conn == null) {
+ $conn = $this->_table->getConnection();
+ }
+ return $conn->unitOfWork->delete($this);
+ }
+
+ public function copy($deep = false)
+ {
+ $data = $this->_data;
+
+ if ($this->_table->getIdentifierType() === IPF_ORM::IDENTIFIER_AUTOINC) {
+ $id = $this->_table->getIdentifier();
+
+ unset($data[$id]);
+ }
+
+ $ret = $this->_table->create($data);
+ $modified = array();
+
+ foreach ($data as $key => $val) {
+ if (!IPF_ORM_Null::isNull($val)) {
+ $ret->_modified[] = $key;
+ }
+ }
+
+ if ($deep) {
+ foreach ($this->_references as $key => $value) {
+ if ($value instanceof IPF_ORM_Collection) {
+ foreach ($value as $record) {
+ $ret->{$key}[] = $record->copy($deep);
+ }
+ } else if($value instanceof IPF_ORM_Record) {
+ $ret->set($key, $value->copy($deep));
+ }
+ }
+ }
+
+ return $ret;
+ }
+
+ public function assignIdentifier($id = false)
+ {
+ if ($id === false) {
+ $this->_id = array();
+ $this->_data = $this->cleanData($this->_data);
+ $this->_state = IPF_ORM_Record::STATE_TCLEAN;
+ $this->_modified = array();
+ } elseif ($id === true) {
+ $this->prepareIdentifiers(true);
+ $this->_state = IPF_ORM_Record::STATE_CLEAN;
+ $this->_modified = array();
+ } else {
+ if (is_array($id)) {
+ foreach ($id as $fieldName => $value) {
+ $this->_id[$fieldName] = $value;
+ $this->_data[$fieldName] = $value;
+ }
+ } else {
+ $name = $this->_table->getIdentifier();
+ $this->_id[$name] = $id;
+ $this->_data[$name] = $id;
+ }
+ $this->_state = IPF_ORM_Record::STATE_CLEAN;
+ $this->_modified = array();
+ }
+ }
+
+ public function identifier()
+ {
+ return $this->_id;
+ }
+
+ final public function getIncremented()
+ {
+ $id = current($this->_id);
+ if ($id === false) {
+ return null;
+ }
+
+ return $id;
+ }
+
+ public function pk($sep='_')
+ {
+ $pk = '';
+ foreach($this->_id as $val) {
+ if ($pk!='')
+ $pk .= $sep;
+ $pk .= $val;
+ }
+ return $pk;
+ }
+
+ public function getLast()
+ {
+ return $this;
+ }
+
+ public function hasReference($name)
+ {
+ return isset($this->_references[$name]);
+ }
+
+ public function reference($name)
+ {
+ if (isset($this->_references[$name])) {
+ return $this->_references[$name];
+ }
+ }
+
+ public function obtainReference($name)
+ {
+ if (isset($this->_references[$name])) {
+ return $this->_references[$name];
+ }
+ throw new IPF_ORM_Exception("Unknown reference $name");
+ }
+
+ public function getReferences()
+ {
+ return $this->_references;
+ }
+
+ final public function setRelated($alias, IPF_ORM_Access $coll)
+ {
+ $this->_references[$alias] = $coll;
+ }
+
+ public function loadReference($name)
+ {
+ $rel = $this->_table->getRelation($name);
+ $this->_references[$name] = $rel->fetchRelatedFor($this);
+ }
+
+ public function call($callback, $column)
+ {
+ $args = func_get_args();
+ array_shift($args);
+
+ if (isset($args[0])) {
+ $fieldName = $args[0];
+ $args[0] = $this->get($fieldName);
+
+ $newvalue = call_user_func_array($callback, $args);
+
+ $this->_data[$fieldName] = $newvalue;
+ }
+ return $this;
+ }
+
+ public function unlink($alias, $ids = array())
+ {
+ $ids = (array) $ids;
+
+ $q = new IPF_ORM_Query();
+
+ $rel = $this->getTable()->getRelation($alias);
+
+ if ($rel instanceof IPF_ORM_Relation_Association) {
+ $q->delete()
+ ->from($rel->getAssociationTable()->getComponentName())
+ ->where($rel->getLocal() . ' = ?', array_values($this->identifier()));
+
+ if (count($ids) > 0) {
+ $q->whereIn($rel->getForeign(), $ids);
+ }
+
+ $q->execute();
+
+ } else if ($rel instanceof IPF_ORM_Relation_ForeignKey) {
+ $q->update($rel->getTable()->getComponentName())
+ ->set($rel->getForeign(), '?', array(null))
+ ->addWhere($rel->getForeign() . ' = ?', array_values($this->identifier()));
+
+ if (count($ids) > 0) {
+ $q->whereIn($rel->getTable()->getIdentifier(), $ids);
+ }
+
+ $q->execute();
+ }
+ if (isset($this->_references[$alias])) {
+ foreach ($this->_references[$alias] as $k => $record) {
+ if (in_array(current($record->identifier()), $ids)) {
+ $this->_references[$alias]->remove($k);
+ }
+ }
+ $this->_references[$alias]->takeSnapshot();
+ }
+ return $this;
+ }
+
+ public function link($alias, $ids)
+ {
+ $ids = (array) $ids;
+
+ if ( ! count($ids)) {
+ return $this;
+ }
+
+ $identifier = array_values($this->identifier());
+ $identifier = array_shift($identifier);
+
+ $rel = $this->getTable()->getRelation($alias);
+
+ if ($rel instanceof IPF_ORM_Relation_Association) {
+
+ $modelClassName = $rel->getAssociationTable()->getComponentName();
+ $localFieldName = $rel->getLocalFieldName();
+ $localFieldDef = $rel->getAssociationTable()->getColumnDefinition($localFieldName);
+ if ($localFieldDef['type'] == 'integer') {
+ $identifier = (integer) $identifier;
+ }
+ $foreignFieldName = $rel->getForeignFieldName();
+ $foreignFieldDef = $rel->getAssociationTable()->getColumnDefinition($foreignFieldName);
+ if ($foreignFieldDef['type'] == 'integer') {
+ for ($i = 0; $i < count($ids); $i++) {
+ $ids[$i] = (integer) $ids[$i];
+ }
+ }
+
+ foreach ($ids as $id) {
+ $record = new $modelClassName;
+ $record[$localFieldName] = $identifier;
+ $record[$foreignFieldName] = $id;
+ $record->save();
+ }
+
+ } else if ($rel instanceof IPF_ORM_Relation_ForeignKey) {
+
+ $q = new IPF_ORM_Query();
+
+ $q->update($rel->getTable()->getComponentName())
+ ->set($rel->getForeign(), '?', array_values($this->identifier()));
+
+ if (count($ids) > 0) {
+ $q->whereIn($rel->getTable()->getIdentifier(), $ids);
+ }
+
+ $q->execute();
+
+ } else if ($rel instanceof IPF_ORM_Relation_LocalKey) {
+
+ $q = new IPF_ORM_Query();
+
+ $q->update($this->getTable()->getComponentName())
+ ->set($rel->getLocalFieldName(), '?', $ids);
+
+ if (count($ids) > 0) {
+ $q->whereIn($rel->getTable()->getIdentifier(), array_values($this->identifier()));
+ }
+
+ $q->execute();
+
+ }
+
+ return $this;
+ }
+
+ public function __call($method, $args)
+ {
+ if (($template = $this->_table->getMethodOwner($method)) !== false) {
+ $template->setInvoker($this);
+ return call_user_func_array(array($template, $method), $args);
+ }
+
+ foreach ($this->_table->getTemplates() as $template) {
+ if (method_exists($template, $method)) {
+ $template->setInvoker($this);
+ $this->_table->setMethodOwner($method, $template);
+
+ return call_user_func_array(array($template, $method), $args);
+ }
+ }
+
+ throw new IPF_ORM_Exception(sprintf('Unknown method %s::%s', get_class($this), $method));
+ }
+
+ public function free($deep = false)
+ {
+ if ($this->_state != self::STATE_LOCKED) {
+ $this->_state = self::STATE_LOCKED;
+
+ $this->_table->getRepository()->evict($this->_oid);
+ $this->_table->removeRecord($this);
+ $this->_data = array();
+ $this->_id = array();
+
+ if ($deep) {
+ foreach ($this->_references as $name => $reference) {
+ if (!IPF_ORM_Null::isNull($reference)) {
+ $reference->free($deep);
+ }
+ }
+ }
+
+ $this->_references = array();
+ }
+ }
+
+ public function toString()
+ {
+ return IPF_ORM::dump(get_object_vars($this));
+ }
+
+ public function __toString()
+ {
+ return sprintf('<%s #%d>', get_class($this), $this->_oid);
+ }
+
+ public function SetFromFormData($cleaned_values)
+ {
+ $names = $this->_table->getFieldNames();
+ foreach ($cleaned_values as $key=>$val) {
+ $validators = $this->getTable()->getFieldValidators($key);
+ if (
+ array_key_exists('image',$validators) ||
+ array_key_exists('file',$validators) ||
+ array_key_exists('email',$validators)
+ ){
+ if (($val!==null) && ($val==''))
+ continue;
+ }
+ if (array_search($key,$names)){
+ $this->$key = $val;
+ }
+ }
+ }
+
+ public function SetCustom($name, $val)
+ {
+ $this->_custom[$name] = $val;
+ }
+
+ public function GetCustom($name)
+ {
+ if (isset($this->_custom[$name]))
+ return $this->_custom[$name];
+ return null;
+ }
+}
+
--- /dev/null
+<?php
+
+abstract class IPF_ORM_Record_Abstract extends IPF_ORM_Access
+{
+ protected $_table;
+
+ public static function setTableDefinition(IPF_ORM_Table $table)
+ {
+ }
+
+ public static function setUp(IPF_ORM_Table $table)
+ {
+ }
+
+ public function getTable()
+ {
+ return $this->_table;
+ }
+
+ public function setInheritanceMap($map)
+ {
+ $this->_table->setOption('inheritanceMap', $map);
+ }
+
+ public function attribute($attr, $value)
+ {
+ if ($value == null) {
+ if (is_array($attr)) {
+ foreach ($attr as $k => $v) {
+ $this->_table->setAttribute($k, $v);
+ }
+ } else {
+ return $this->_table->getAttribute($attr);
+ }
+ } else {
+ $this->_table->setAttribute($attr, $value);
+ }
+ }
+
+ public function check($constraint, $name = null)
+ {
+ if (is_array($constraint)) {
+ foreach ($constraint as $name => $def) {
+ $this->_table->addCheckConstraint($def, $name);
+ }
+ } else {
+ $this->_table->addCheckConstraint($constraint, $name);
+ }
+ return $this;
+ }
+
+ public function __debugInfo()
+ {
+ $r = array();
+ foreach ($this->getTable()->getColumnNames() as $column)
+ $r[$column] = $this->get($column);
+ return $r;
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Record_Iterator extends ArrayIterator
+{
+ private $record;
+
+ public function __construct(IPF_ORM_Record $record)
+ {
+ $this->record = $record;
+ parent::__construct($record->getData());
+ }
+
+ public function current()
+ {
+ $value = parent::current();
+
+ if (IPF_ORM_Null::isNull($value)) {
+ return null;
+ } else {
+ return $value;
+ }
+ }
+}
+
--- /dev/null
+<?php
+
+abstract class IPF_ORM_Relation implements ArrayAccess
+{
+ const ONE_AGGREGATE = 0;
+ const ONE_COMPOSITE = 1;
+ const MANY_AGGREGATE = 2;
+ const MANY_COMPOSITE = 3;
+
+ const ONE = 0;
+ const MANY = 2;
+
+ protected $definition = array('alias' => true,
+ 'foreign' => true,
+ 'local' => true,
+ 'class' => true,
+ 'type' => true,
+ 'table' => true,
+ 'localTable' => true,
+ 'name' => null,
+ 'refTable' => null,
+ 'onDelete' => null,
+ 'onUpdate' => null,
+ 'deferred' => null,
+ 'deferrable' => null,
+ 'constraint' => null,
+ 'equal' => false,
+ 'cascade' => array(), // application-level cascades
+ 'owningSide' => false, // whether this is the owning side
+ 'exclude' => false,
+ );
+
+ public function __construct(array $definition)
+ {
+ $def = array();
+ foreach ($this->definition as $key => $val) {
+ if ( ! isset($definition[$key]) && $val) {
+ throw new IPF_ORM_Exception($key . ' is required!');
+ }
+ if (isset($definition[$key])) {
+ $def[$key] = $definition[$key];
+ } else {
+ $def[$key] = $this->definition[$key];
+ }
+ }
+ $this->definition = $def;
+ }
+
+ public function hasConstraint()
+ {
+ return $this->definition['constraint'] ||
+ $this->definition['onUpdate'] ||
+ $this->definition['onDelete'];
+ }
+
+ public function isDeferred()
+ {
+ return $this->definition['deferred'];
+ }
+
+ public function isDeferrable()
+ {
+ return $this->definition['deferrable'];
+ }
+ public function isEqual()
+ {
+ return $this->definition['equal'];
+ }
+
+ public function offsetExists($offset)
+ {
+ return isset($this->definition[$offset]);
+ }
+
+ public function offsetGet($offset)
+ {
+ if (isset($this->definition[$offset])) {
+ return $this->definition[$offset];
+ }
+
+ return null;
+ }
+
+ public function offsetSet($offset, $value)
+ {
+ if (isset($this->definition[$offset])) {
+ $this->definition[$offset] = $value;
+ }
+ }
+
+ public function offsetUnset($offset)
+ {
+ $this->definition[$offset] = false;
+ }
+
+ public function toArray()
+ {
+ return $this->definition;
+ }
+
+ final public function getAlias()
+ {
+ return $this->definition['alias'];
+ }
+
+ final public function getType()
+ {
+ return $this->definition['type'];
+ }
+
+ public function isCascadeDelete()
+ {
+ return in_array('delete', $this->definition['cascade']);
+ }
+
+ final public function getTable()
+ {
+ return IPF_ORM_Manager::connection()->getTable($this->definition['class']);
+ }
+
+ final public function getClass()
+ {
+ return $this->definition['class'];
+ }
+
+ final public function getLocal()
+ {
+ return $this->definition['local'];
+ }
+
+ final public function getLocalFieldName()
+ {
+ return $this->definition['localTable']->getFieldName($this->definition['local']);
+ }
+
+ final public function getForeign()
+ {
+ return $this->definition['foreign'];
+ }
+
+ final public function getForeignFieldName()
+ {
+ return $this->definition['table']->getFieldName($this->definition['foreign']);
+ }
+
+ final public function isComposite()
+ {
+ return ($this->definition['type'] == IPF_ORM_Relation::ONE_COMPOSITE ||
+ $this->definition['type'] == IPF_ORM_Relation::MANY_COMPOSITE);
+ }
+
+ final public function isOneToOne()
+ {
+ return ($this->definition['type'] == IPF_ORM_Relation::ONE_AGGREGATE ||
+ $this->definition['type'] == IPF_ORM_Relation::ONE_COMPOSITE);
+ }
+
+ abstract public function getRelationDql($count, $context);
+
+ abstract public function fetchRelatedFor(IPF_ORM_Record $record);
+
+ public function __toString()
+ {
+ $r[] = "<pre>";
+ foreach ($this->definition as $k => $v) {
+ if (is_object($v)) {
+ $v = 'Object(' . get_class($v) . ')';
+ }
+ $r[] = $k . ' : ' . $v;
+ }
+ $r[] = "</pre>";
+ return implode("\n", $r);
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Relation_Association extends IPF_ORM_Relation
+{
+ public function getAssociationFactory()
+ {
+ return $this->definition['refTable'];
+ }
+ public function getAssociationTable()
+ {
+ return $this->definition['refTable'];
+ }
+
+ public function getRelationDql($count, $context)
+ {
+ $component = $this->definition['refTable']->getComponentName();
+ $thisTable = $this->getTable()->getComponentName();
+
+ $sub = substr(str_repeat('?, ', $count), 0, -2);
+ switch ($context) {
+ case "record":
+ $dql = 'FROM ' . $thisTable . '.' . $component .
+ ' WHERE ' . $thisTable . '.' . $component . '.' . $this->definition['local'] . ' IN (' . $sub . ')';
+ break;
+ case "collection":
+ $dql = 'FROM ' . $component . '.' . $thisTable .
+ ' WHERE ' . $component . '.' . $this->definition['local'] . ' IN (' . $sub . ')';
+ break;
+ }
+ return $dql;
+ }
+
+ public function fetchRelatedFor(IPF_ORM_Record $record)
+ {
+ $id = $record->pk();
+ if (empty($id) || ! $this->definition['table']->getAttribute(IPF_ORM::ATTR_LOAD_REFERENCES)) {
+ $coll = new IPF_ORM_Collection($this->getTable());
+ } else {
+ $coll = $this->getTable()->getConnection()->query($this->getRelationDql(1, 'record'), array($id));
+ }
+ $coll->setReference($record, $this);
+ return $coll;
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Relation_ForeignKey extends IPF_ORM_Relation
+{
+ public function fetchRelatedFor(IPF_ORM_Record $record)
+ {
+ $id = array();
+ $localTable = $record->getTable();
+ foreach ((array) $this->definition['local'] as $local) {
+ $value = $record->get($localTable->getFieldName($local));
+ if (isset($value)) {
+ $id[] = $value;
+ }
+ }
+ if ($this->isOneToOne()) {
+ if ( ! $record->exists() || empty($id) ||
+ ! $this->definition['table']->getAttribute(IPF_ORM::ATTR_LOAD_REFERENCES)) {
+
+ $related = $this->getTable()->create();
+ } else {
+ $dql = 'FROM ' . $this->getTable()->getComponentName()
+ . ' WHERE ' . $this->getCondition();
+
+ $coll = $this->getTable()->getConnection()->query($dql, $id);
+ $related = $coll[0];
+ }
+
+ $related->set($related->getTable()->getFieldName($this->definition['foreign']),
+ $record, false);
+ } else {
+
+ if ( ! $record->exists() || empty($id) ||
+ ! $this->definition['table']->getAttribute(IPF_ORM::ATTR_LOAD_REFERENCES)) {
+
+ $related = new IPF_ORM_Collection($this->getTable());
+ } else {
+ $query = $this->getRelationDql(1, '');
+ $related = $this->getTable()->getConnection()->query($query, $id);
+ }
+ $related->setReference($record, $this);
+ }
+ return $related;
+ }
+
+ public function getRelationDql($count, $context)
+ {
+ $table = $this->getTable();
+ $component = $table->getComponentName();
+
+ $dql = 'FROM ' . $component
+ . ' WHERE ' . $component . '.' . $this->definition['foreign']
+ . ' IN (' . substr(str_repeat('?, ', $count), 0, -2) . ')';
+
+ $ordering = $table->getOrdering();
+ if ($ordering)
+ $dql .= ' ORDER BY ' . implode(', ', $ordering);
+
+ return $dql;
+ }
+
+ public function getCondition($alias = null)
+ {
+ if ( ! $alias) {
+ $alias = $this->getTable()->getComponentName();
+ }
+ $conditions = array();
+ foreach ((array) $this->definition['foreign'] as $foreign) {
+ $conditions[] = $alias . '.' . $foreign . ' = ?';
+ }
+ return implode(' AND ', $conditions);
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+class IPF_ORM_Relation_LocalKey extends IPF_ORM_Relation
+{
+ public function fetchRelatedFor(IPF_ORM_Record $record)
+ {
+ $localFieldName = $record->getTable()->getFieldName($this->definition['local']);
+ $id = $record->get($localFieldName);
+
+ if (is_null($id) || ! $this->definition['table']->getAttribute(IPF_ORM::ATTR_LOAD_REFERENCES)) {
+ $related = $this->getTable()->create();
+ } else {
+ $dql = 'FROM ' . $this->getTable()->getComponentName()
+ . ' WHERE ' . $this->getCondition();
+
+ $related = $this->getTable()
+ ->getConnection()
+ ->query($dql, array($id))
+ ->getFirst();
+
+ if ( ! $related || empty($related)) {
+ $related = $this->getTable()->create();
+ }
+ }
+
+ $record->set($localFieldName, $related, false);
+
+ return $related;
+ }
+
+ public function getRelationDql($count, $context)
+ {
+ $component = $this->getTable()->getComponentName();
+ return 'FROM ' . $component .
+ ' WHERE ' . $component . '.' . $this->definition['foreign'] .
+ ' IN (' . substr(str_repeat('?, ', $count), 0, -2) . ')';
+ }
+
+ public function getCondition($alias = null)
+ {
+ if ( ! $alias) {
+ $alias = $this->getTable()->getComponentName();
+ }
+ return $alias . '.' . $this->definition['foreign'] . ' = ?';
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Relation_Nest extends IPF_ORM_Relation_Association
+{
+ public function getRelationDql($count, $context = 'record')
+ {
+ switch ($context) {
+ case 'record':
+ $identifierColumnNames = $this->definition['table']->getIdentifierColumnNames();
+ $identifier = array_pop($identifierColumnNames);
+ $sub = 'SELECT '.$this->definition['foreign']
+ . ' FROM '.$this->definition['refTable']->getTableName()
+ . ' WHERE '.$this->definition['local']
+ . ' = ?';
+
+ $sub2 = 'SELECT '.$this->definition['local']
+ . ' FROM '.$this->definition['refTable']->getTableName()
+ . ' WHERE '.$this->definition['foreign']
+ . ' = ?';
+
+ $dql = 'FROM ' . $this->definition['table']->getComponentName()
+ . '.' . $this->definition['refTable']->getComponentName()
+ . ' WHERE ' . $this->definition['table']->getComponentName()
+ . '.' . $identifier
+ . ' IN (' . $sub . ')'
+ . ' || ' . $this->definition['table']->getComponentName()
+ . '.' . $identifier
+ . ' IN (' . $sub2 . ')';
+ break;
+ case 'collection':
+ $sub = substr(str_repeat('?, ', $count),0,-2);
+ $dql = 'FROM '.$this->definition['refTable']->getComponentName()
+ . '.' . $this->definition['table']->getComponentName()
+ . ' WHERE '.$this->definition['refTable']->getComponentName()
+ . '.' . $this->definition['local'] . ' IN (' . $sub . ')';
+ };
+
+ return $dql;
+ }
+
+ public function fetchRelatedFor(IPF_ORM_Record $record)
+ {
+ $id = $record->getIncremented();
+
+
+ if (empty($id) || ! $this->definition['table']->getAttribute(IPF_ORM::ATTR_LOAD_REFERENCES)) {
+ return new IPF_ORM_Collection($this->getTable());
+ } else {
+ $q = new IPF_ORM_RawSql($this->getTable()->getConnection());
+
+ $assocTable = $this->getAssociationFactory()->getTableName();
+ $tableName = $record->getTable()->getTableName();
+ $identifierColumnNames = $record->getTable()->getIdentifierColumnNames();
+ $identifier = array_pop($identifierColumnNames);
+
+ $sub = 'SELECT ' . $this->getForeign()
+ . ' FROM ' . $assocTable
+ . ' WHERE ' . $this->getLocal()
+ . ' = ?';
+
+ $condition[] = $tableName . '.' . $identifier . ' IN (' . $sub . ')';
+ $joinCondition[] = $tableName . '.' . $identifier . ' = ' . $assocTable . '.' . $this->getForeign();
+
+ if ($this->definition['equal']) {
+ $sub2 = 'SELECT ' . $this->getLocal()
+ . ' FROM ' . $assocTable
+ . ' WHERE ' . $this->getForeign()
+ . ' = ?';
+
+ $condition[] = $tableName . '.' . $identifier . ' IN (' . $sub2 . ')';
+ $joinCondition[] = $tableName . '.' . $identifier . ' = ' . $assocTable . '.' . $this->getLocal();
+ }
+ $q->select('{'.$tableName.'.*}')
+ ->addSelect('{'.$assocTable.'.*}')
+ ->from($tableName . ' INNER JOIN ' . $assocTable . ' ON ' . implode(' OR ', $joinCondition))
+ ->where(implode(' OR ', $condition));
+ $q->addComponent($tableName, $record->getTable()->getComponentName());
+ $q->addComponent($assocTable, $record->getTable()->getComponentName(). '.' . $this->getAssociationFactory()->getComponentName());
+
+ $params = ($this->definition['equal']) ? array($id, $id) : array($id);
+
+ return $q->execute($params);
+ }
+ }
+}
--- /dev/null
+<?php
+
+class IPF_ORM_Relation_Parser
+{
+ protected $_table;
+ protected $_relations = array();
+ protected $_pending = array();
+
+ public function __construct(IPF_ORM_Table $table)
+ {
+ $this->_table = $table;
+ }
+
+ public function getTable()
+ {
+ return $this->_table;
+ }
+
+ public function getPendingRelation($name)
+ {
+ if ( ! isset($this->_pending[$name])) {
+ throw new IPF_ORM_Exception('Unknown pending relation ' . $name);
+ }
+
+ return $this->_pending[$name];
+ }
+
+ public function getPendingRelations()
+ {
+ return $this->_pending;
+ }
+
+ public function unsetPendingRelations($name)
+ {
+ unset($this->_pending[$name]);
+ }
+
+ public function hasRelation($name)
+ {
+ return isset($this->_pending[$name]) || isset($this->_relations[$name]);
+ }
+
+ public function bind($name, $alias, $type, $options=array())
+ {
+ if (!$alias)
+ $alias = $name;
+
+ unset($this->relations[$alias]);
+
+ $options['class'] = $name;
+ $options['alias'] = $alias;
+ $options['type'] = $type;
+
+ $this->_pending[$alias] = $options;
+ }
+
+ private function createRelation($alias)
+ {
+ if (isset($this->_relations[$alias]))
+ return;
+
+ if (!isset($this->_pending[$alias]))
+ throw new IPF_ORM_Exception('Unknown relation alias "' . $alias . '".');
+
+ $def = $this->_pending[$alias];
+ $identifierColumnNames = $this->_table->getIdentifierColumnNames();
+ $idColumnName = array_pop($identifierColumnNames);
+
+ // check if reference class name exists
+ // if it does we are dealing with association relation
+ if (isset($def['refClass'])) {
+ $def = $this->completeAssocDefinition($def);
+ $localClasses = array_merge($this->_table->getOption('parents'), array($this->_table->getComponentName()));
+
+ if ( ! isset($this->_pending[$def['refClass']]) &&
+ ! isset($this->_relations[$def['refClass']])) {
+
+ $parser = $def['refTable']->getRelationParser();
+ if ( ! $parser->hasRelation($this->_table->getComponentName())) {
+ $parser->bind($this->_table->getComponentName(), null, IPF_ORM_Relation::ONE, array(
+ 'local' => $def['local'],
+ 'foreign' => $idColumnName,
+ 'localKey' => true,
+ ));
+ }
+
+ if (!$this->hasRelation($def['refClass'])) {
+ $this->bind($def['refClass'], null, IPF_ORM_Relation::MANY, array(
+ 'foreign' => $def['local'],
+ 'local' => $idColumnName,
+ ));
+ }
+ }
+ if (in_array($def['class'], $localClasses)) {
+ $rel = new IPF_ORM_Relation_Nest($def);
+ } else {
+ $rel = new IPF_ORM_Relation_Association($def);
+ }
+ } else {
+ // simple foreign key relation
+ $def = $this->completeDefinition($def);
+
+ if (isset($def['localKey'])) {
+ $rel = new IPF_ORM_Relation_LocalKey($def);
+
+ // Automatically index foreign keys which are not primary
+ $foreign = (array) $def['foreign'];
+ foreach ($foreign as $fk) {
+ if ( ! $rel->getTable()->isIdentifier($fk)) {
+ $rel->getTable()->addIndex($fk, array('fields' => array($fk)));
+ }
+ }
+ } else {
+ $rel = new IPF_ORM_Relation_ForeignKey($def);
+ }
+ }
+
+ if (isset($rel)) {
+ // unset pending relation
+ unset($this->_pending[$alias]);
+
+ $this->_relations[$alias] = $rel;
+ }
+ }
+
+ public function getRelation($alias)
+ {
+ $this->getRelations();
+ return $this->_relations[$alias];
+ }
+
+ public function getRelations()
+ {
+ foreach ($this->_pending as $k => $v) {
+ $this->createRelation($k);
+ }
+
+ return $this->_relations;
+ }
+
+ public function completeAssocDefinition($def)
+ {
+ $conn = $this->_table->getConnection();
+
+ $def['table'] = IPF_ORM::getTable($def['class']);
+ $def['localTable'] = $this->_table;
+ $def['class'] = $def['table']->getComponentName();
+ $def['refTable'] = IPF_ORM::getTable($def['refClass']);
+
+ $id = $def['refTable']->getIdentifierColumnNames();
+
+ if (count($id) > 1) {
+ if ( ! isset($def['foreign'])) {
+ // foreign key not set
+ // try to guess the foreign key
+
+ $def['foreign'] = ($def['local'] === $id[0]) ? $id[1] : $id[0];
+ }
+ if ( ! isset($def['local'])) {
+ // foreign key not set
+ // try to guess the foreign key
+
+ $def['local'] = ($def['foreign'] === $id[0]) ? $id[1] : $id[0];
+ }
+ } else {
+
+ if ( ! isset($def['foreign'])) {
+ // foreign key not set
+ // try to guess the foreign key
+
+ $columns = $this->getIdentifiers($def['table']);
+
+ $def['foreign'] = $columns;
+ }
+ if ( ! isset($def['local'])) {
+ // local key not set
+ // try to guess the local key
+ $columns = $this->getIdentifiers($this->_table);
+
+ $def['local'] = $columns;
+ }
+ }
+ return $def;
+ }
+
+ public function getIdentifiers(IPF_ORM_Table $table)
+ {
+ $componentNameToLower = strtolower($table->getComponentName());
+ if (is_array($table->getIdentifier())) {
+ $columns = array();
+ foreach ((array) $table->getIdentifierColumnNames() as $identColName) {
+ $columns[] = $componentNameToLower . '_' . $identColName;
+ }
+ } else {
+ $columns = $componentNameToLower . '_' . $table->getColumnName(
+ $table->getIdentifier());
+ }
+
+ return $columns;
+ }
+
+ public function guessColumns(array $classes, IPF_ORM_Table $foreignTable)
+ {
+ $conn = $this->_table->getConnection();
+
+ foreach ($classes as $class) {
+ try {
+ $table = $conn->getTable($class);
+ } catch (IPF_ORM_Exception $e) {
+ continue;
+ }
+ $columns = $this->getIdentifiers($table);
+ $found = true;
+
+ foreach ((array) $columns as $column) {
+ if ( ! $foreignTable->hasColumn($column)) {
+ $found = false;
+ break;
+ }
+ }
+ if ($found) {
+ break;
+ }
+ }
+
+ if ( ! $found) {
+ throw new IPF_ORM_Exception("Couldn't find columns.");
+ }
+
+ return $columns;
+ }
+
+ public function completeDefinition($def)
+ {
+ $conn = $this->_table->getConnection();
+ $def['table'] = IPF_ORM::getTable($def['class']);
+ $def['localTable'] = $this->_table;
+ $def['class'] = $def['table']->getComponentName();
+
+ $foreignClasses = array_merge($def['table']->getOption('parents'), array($def['class']));
+ $localClasses = array_merge($this->_table->getOption('parents'), array($this->_table->getComponentName()));
+
+ $localIdentifierColumnNames = $this->_table->getIdentifierColumnNames();
+ $localIdentifierCount = count($localIdentifierColumnNames);
+ $localIdColumnName = array_pop($localIdentifierColumnNames);
+ $foreignIdentifierColumnNames = $def['table']->getIdentifierColumnNames();
+ $foreignIdColumnName = array_pop($foreignIdentifierColumnNames);
+
+ if (isset($def['local'])) {
+ if ( ! isset($def['foreign'])) {
+ // local key is set, but foreign key is not
+ // try to guess the foreign key
+
+ if ($def['local'] === $localIdColumnName) {
+ $def['foreign'] = $this->guessColumns($localClasses, $def['table']);
+ } else {
+ // the foreign field is likely to be the
+ // identifier of the foreign class
+ $def['foreign'] = $foreignIdColumnName;
+ $def['localKey'] = true;
+ }
+ } else {
+ if ($localIdentifierCount == 1) {
+ if ($def['local'] == $localIdColumnName && isset($def['owningSide'])
+ && $def['owningSide'] === true) {
+ $def['localKey'] = true;
+ } else if (($def['local'] !== $localIdColumnName && $def['type'] == IPF_ORM_Relation::ONE)) {
+ $def['localKey'] = true;
+ }
+ } else if ($localIdentifierCount > 1) {
+ // It's a composite key and since 'foreign' can not point to a composite
+ // key currently, we know that 'local' must be the foreign key.
+ $def['localKey'] = true;
+ }
+ }
+ } else {
+ if (isset($def['foreign'])) {
+ // local key not set, but foreign key is set
+ // try to guess the local key
+ if ($def['foreign'] === $foreignIdColumnName) {
+ $def['localKey'] = true;
+ try {
+ $def['local'] = $this->guessColumns($foreignClasses, $this->_table);
+ } catch (IPF_ORM_Exception $e) {
+ $def['local'] = $localIdColumnName;
+ }
+ } else {
+ $def['local'] = $localIdColumnName;
+ }
+ } else {
+ // neither local or foreign key is being set
+ // try to guess both keys
+
+ $conn = $this->_table->getConnection();
+
+ // the following loops are needed for covering inheritance
+ foreach ($localClasses as $class) {
+ $table = $conn->getTable($class);
+ $identifierColumnNames = $table->getIdentifierColumnNames();
+ $idColumnName = array_pop($identifierColumnNames);
+ $column = strtolower($table->getComponentName())
+ . '_' . $idColumnName;
+
+ foreach ($foreignClasses as $class2) {
+ $table2 = $conn->getTable($class2);
+ if ($table2->hasColumn($column)) {
+ $def['foreign'] = $column;
+ $def['local'] = $idColumnName;
+ return $def;
+ }
+ }
+ }
+
+ foreach ($foreignClasses as $class) {
+ $table = $conn->getTable($class);
+ $identifierColumnNames = $table->getIdentifierColumnNames();
+ $idColumnName = array_pop($identifierColumnNames);
+ $column = strtolower($table->getComponentName())
+ . '_' . $idColumnName;
+
+ foreach ($localClasses as $class2) {
+ $table2 = $conn->getTable($class2);
+ if ($table2->hasColumn($column)) {
+ $def['foreign'] = $idColumnName;
+ $def['local'] = $column;
+ $def['localKey'] = true;
+ return $def;
+ }
+ }
+ }
+
+ // auto-add columns and auto-build relation
+ $columns = array();
+ foreach ((array) $this->_table->getIdentifierColumnNames() as $id) {
+ // ?? should this not be $this->_table->getComponentName() ??
+ $column = strtolower($table->getComponentName())
+ . '_' . $id;
+
+ $col = $this->_table->getColumnDefinition($id);
+ $type = $col['type'];
+ $length = $col['length'];
+
+ unset($col['type']);
+ unset($col['length']);
+ unset($col['autoincrement']);
+ unset($col['primary']);
+
+ $def['table']->setColumn($column, $type, $length, $col);
+
+ $columns[] = $column;
+ }
+ if (count($columns) > 1) {
+ $def['foreign'] = $columns;
+ } else {
+ $def['foreign'] = $columns[0];
+ }
+ $def['local'] = $localIdColumnName;
+ }
+ }
+ return $def;
+ }
+}
--- /dev/null
+<?php
+
+class IPF_ORM_Table extends IPF_ORM_Configurable implements Countable
+{
+ protected $_data = array();
+ protected $_identifier = array();
+ protected $_identifierType;
+ protected $_conn;
+ protected $_identityMap = array();
+
+ protected $_repository;
+ protected $_columns = array();
+ protected $_fieldNames = array();
+
+ protected $_columnNames = array();
+
+ protected $hasDefaultValues;
+
+ protected $_options = array('name' => null,
+ 'tableName' => null,
+ 'inheritanceMap' => array(),
+ 'enumMap' => array(),
+ 'type' => null,
+ 'charset' => null,
+ 'collation' => null,
+ 'indexes' => array(),
+ 'parents' => array(),
+ 'queryParts' => array(),
+ 'versioning' => null,
+ 'subclasses' => array(),
+ );
+
+ protected $_ordering = null;
+
+ protected $_parser;
+
+ protected $_templates = array();
+ protected $_invokedMethods = array();
+
+ public $listeners = array();
+
+ public function __construct($name, IPF_ORM_Connection $conn)
+ {
+ if (empty($name) || !class_exists($name))
+ throw new IPF_ORM_Exception("Couldn't find class " . $name);
+
+ $this->_conn = $conn;
+ $this->setParent($this->_conn);
+
+ $this->_options['name'] = $name;
+ $this->_parser = new IPF_ORM_Relation_Parser($this);
+
+ $this->initParents($name);
+
+ // create database table
+ $name::setTableDefinition($this);
+
+ if (!isset($this->_options['tableName'])) {
+ $this->setTableName(IPF_ORM_Inflector::tableize($class->getName()));
+ }
+
+ $this->initIdentifier();
+
+ $name::setUp($this);
+
+ $this->_repository = new IPF_ORM_Table_Repository($this);
+ }
+
+ private function initParents($name)
+ {
+ $names = array();
+
+ $class = $name;
+ do {
+ if ($class === 'IPF_ORM_Record')
+ break;
+
+ $name = $class;
+ $names[] = $name;
+ } while ($class = get_parent_class($class));
+
+ if ($class === false)
+ throw new IPF_ORM_Exception('Class "' . $name . '" must be a child class of IPF_ORM_Record');
+
+ // reverse names
+ $names = array_reverse($names);
+ // save parents
+ array_pop($names);
+ $this->_options['parents'] = $names;
+ }
+
+ public function initIdentifier()
+ {
+ switch (count($this->_identifier)) {
+ case 0:
+ $definition = array('type' => 'integer',
+ 'length' => 20,
+ 'autoincrement' => true,
+ 'primary' => true);
+ $this->setColumn('id', $definition['type'], $definition['length'], $definition, true);
+ $this->_identifier = 'id';
+ $this->_identifierType = IPF_ORM::IDENTIFIER_AUTOINC;
+ break;
+ case 1:
+ foreach ($this->_identifier as $pk) {
+ $e = $this->getDefinitionOf($pk);
+
+ $found = false;
+
+ foreach ($e as $option => $value) {
+ if ($found) {
+ break;
+ }
+
+ $e2 = explode(':', $option);
+
+ switch (strtolower($e2[0])) {
+ case 'autoincrement':
+ case 'autoinc':
+ if ($value !== false) {
+ $this->_identifierType = IPF_ORM::IDENTIFIER_AUTOINC;
+ $found = true;
+ }
+ break;
+ }
+ }
+ if ( ! isset($this->_identifierType)) {
+ $this->_identifierType = IPF_ORM::IDENTIFIER_NATURAL;
+ }
+ }
+
+ $this->_identifier = $pk;
+
+ break;
+ default:
+ $this->_identifierType = IPF_ORM::IDENTIFIER_COMPOSITE;
+ }
+ }
+
+ public function isIdentifier($fieldName)
+ {
+ return ($fieldName === $this->getIdentifier() ||
+ in_array($fieldName, (array) $this->getIdentifier()));
+ }
+
+ public function isIdentifierAutoincrement()
+ {
+ return $this->getIdentifierType() === IPF_ORM::IDENTIFIER_AUTOINC;
+ }
+
+ public function isIdentifierComposite()
+ {
+ return $this->getIdentifierType() === IPF_ORM::IDENTIFIER_COMPOSITE;
+ }
+
+ public function getMethodOwner($method)
+ {
+ return (isset($this->_invokedMethods[$method])) ?
+ $this->_invokedMethods[$method] : false;
+ }
+
+ public function setMethodOwner($method, $class)
+ {
+ $this->_invokedMethods[$method] = $class;
+ }
+
+ public function getTemplates()
+ {
+ return $this->_templates;
+ }
+
+ public function getRelationParser()
+ {
+ return $this->_parser;
+ }
+
+ public function __get($option)
+ {
+ if (isset($this->_options[$option])) {
+ return $this->_options[$option];
+ }
+ return null;
+ }
+
+ public function __isset($option)
+ {
+ return isset($this->_options[$option]);
+ }
+
+ public function getOptions()
+ {
+ return $this->_options;
+ }
+
+ public function setOptions($options)
+ {
+ foreach ($options as $key => $value) {
+ $this->setOption($key, $value);
+ }
+ }
+
+ public function addCheckConstraint($definition, $name)
+ {
+ if (is_string($name)) {
+ $this->_options['checks'][$name] = $definition;
+ } else {
+ $this->_options['checks'][] = $definition;
+ }
+
+ return $this;
+ }
+
+ public function addIndex($index, array $definition)
+ {
+ $this->_options['indexes'][$index] = $definition;
+ }
+
+ public function getIndex($index)
+ {
+ if (isset($this->_options['indexes'][$index])) {
+ return $this->_options['indexes'][$index];
+ }
+
+ return false;
+ }
+
+ public function bind($class, $alias, $type, array $options)
+ {
+ $this->_parser->bind($class, $alias, $type, $options);
+ }
+
+ public function ownsOne($class, $alias, $options=array())
+ {
+ $this->bind($class, $alias, IPF_ORM_Relation::ONE_COMPOSITE, $options);
+ }
+
+ public function ownsMany($class, $alias, $options=array())
+ {
+ $this->bind($class, $alias, IPF_ORM_Relation::MANY_COMPOSITE, $options);
+ }
+
+ public function hasOne($class, $alias, $options=array())
+ {
+ $this->bind($class, $alias, IPF_ORM_Relation::ONE_AGGREGATE, $options);
+ }
+
+ public function hasMany($class, $alias, $options=array())
+ {
+ $this->bind($class, $alias, IPF_ORM_Relation::MANY_AGGREGATE, $options);
+ }
+
+ public function hasRelation($alias)
+ {
+ return $this->_parser->hasRelation($alias);
+ }
+
+ public function getRelation($alias)
+ {
+ return $this->_parser->getRelation($alias);
+ }
+
+ public function getRelations()
+ {
+ return $this->_parser->getRelations();
+ }
+
+ public function createQuery($alias = '')
+ {
+ if ( ! empty($alias)) {
+ $alias = ' ' . trim($alias);
+ }
+ return IPF_ORM_Query::create($this->_conn)->from($this->getComponentName() . $alias);
+ }
+
+ public function getRepository()
+ {
+ return $this->_repository;
+ }
+
+ public function setOption($name, $value)
+ {
+ switch ($name) {
+ case 'name':
+ case 'tableName':
+ break;
+ case 'enumMap':
+ case 'inheritanceMap':
+ case 'index':
+ if (!is_array($value))
+ throw new IPF_ORM_Exception($name . ' should be an array.');
+ break;
+ }
+ $this->_options[$name] = $value;
+ }
+
+ public function getOption($name, $default=null)
+ {
+ if (isset($this->_options[$name])) {
+ return $this->_options[$name];
+ }
+ return $default;
+ }
+
+ public function getColumnName($fieldName)
+ {
+ // FIX ME: This is being used in places where an array is passed, but it should not be an array
+ // For example in places where IPF_ORM should support composite foreign/primary keys
+ $fieldName = is_array($fieldName) ? $fieldName[0]:$fieldName;
+
+ if (isset($this->_columnNames[$fieldName])) {
+ return $this->_columnNames[$fieldName];
+ }
+
+ return strtolower($fieldName);
+ }
+
+ public function getColumnDefinition($columnName, $default=false)
+ {
+ if (isset($this->_columns[$columnName])) {
+ return $this->_columns[$columnName];
+ }
+ return false;
+ }
+
+ public function getFieldName($columnName)
+ {
+ if (isset($this->_fieldNames[$columnName])) {
+ return $this->_fieldNames[$columnName];
+ }
+ return $columnName;
+ }
+ public function setColumns(array $definitions)
+ {
+ foreach ($definitions as $name => $options) {
+ $this->setColumn($name, $options['type'], $options['length'], $options);
+ }
+ }
+
+ 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]);
+ } 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 'string':
+ case 'clob':
+ case 'float':
+ case 'double':
+ case 'integer':
+ case 'array':
+ case 'object':
+ case 'blob':
+ case 'gzip':
+ // use php int max
+ $length = 2147483647;
+ 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;
+ }
+ }
+
+ public function hasDefaultValues()
+ {
+ return $this->hasDefaultValues;
+ }
+
+ public function getDefaultValueOf($fieldName)
+ {
+ $columnName = $this->getColumnName($fieldName);
+ if ( ! isset($this->_columns[$columnName])) {
+ throw new IPF_ORM_Exception("Couldn't get default value. Column ".$columnName." doesn't exist.");
+ }
+ if (isset($this->_columns[$columnName]['default'])) {
+ return $this->_columns[$columnName]['default'];
+ } else {
+ return null;
+ }
+ }
+
+ public function getIdentifier()
+ {
+ return $this->_identifier;
+ }
+
+ public function getIdentifierType()
+ {
+ return $this->_identifierType;
+ }
+
+ public function hasColumn($columnName)
+ {
+ return isset($this->_columns[strtolower($columnName)]);
+ }
+
+ public function hasField($fieldName)
+ {
+ return isset($this->_columnNames[$fieldName]);
+ }
+
+ public function setSubClasses(array $map)
+ {
+ $class = $this->getComponentName();
+ if (isset($map[$class])) {
+ $this->setOption('inheritanceMap', $map[$class]);
+ } else {
+ $this->setOption('subclasses', array_keys($map));
+ }
+ }
+
+ public function setConnection(IPF_ORM_Connection $conn)
+ {
+ $this->_conn = $conn;
+
+ $this->setParent($this->_conn);
+
+ return $this;
+ }
+
+ public function getConnection()
+ {
+ return $this->_conn;
+ }
+
+ public function create(array $array = array())
+ {
+ $record = new $this->_options['name']($this, true);
+ $record->fromArray($array);
+
+ return $record;
+ }
+
+ public function find($id, $hydrationMode = null)
+ {
+ if (is_null($id)) {
+ return false;
+ }
+
+ $id = is_array($id) ? array_values($id) : array($id);
+
+ $q = $this->createQuery();
+ $q->where(implode(' = ? AND ', (array) $this->getIdentifier()) . ' = ?', $id)
+ ->limit(1);
+ $res = $q->fetchOne(array(), $hydrationMode);
+ $q->free();
+
+ return $res;
+ }
+
+ public function findAll($hydrationMode = null)
+ {
+ $q = $this->createQuery();
+ if ($this->_ordering) {
+ $q->orderBy(implode(', ', $this->_ordering));
+ } elseif ($this->hasTemplate('IPF_ORM_Template_Orderable')) {
+ $q->orderBy($this->getTemplate('IPF_ORM_Template_Orderable')->getColumnName());
+ }
+ return $q->execute(array(), $hydrationMode);
+ }
+
+ public function findBySql($dql, $params = array(), $hydrationMode = null)
+ {
+ return $this->createQuery()->where($dql)->execute($params, $hydrationMode);
+ }
+
+ public function findByDql($dql, $params = array(), $hydrationMode = null)
+ {
+ $parser = new IPF_ORM_Query($this->_conn);
+ $component = $this->getComponentName();
+ $query = 'FROM ' . $component . ' WHERE ' . $dql;
+
+ return $parser->query($query, $params, $hydrationMode);
+ }
+
+ public function findOneByDql($dql, $params = array(), $hydrationMode = null)
+ {
+ $results = $this->findByDql($dql, $params, $hydrationMode);
+ if (is_array($results) && isset($results[0])) {
+ return $results[0];
+ } else if ($results instanceof IPF_ORM_Collection && $results->count() > 0) {
+ return $results->getFirst();
+ } else {
+ return false;
+ }
+ }
+
+
+ public function execute($queryKey, $params = array(), $hydrationMode = IPF_ORM::HYDRATE_RECORD)
+ {
+ return IPF_ORM_Manager::getInstance()
+ ->getQueryRegistry()
+ ->get($queryKey, $this->getComponentName())
+ ->execute($params, $hydrationMode);
+ }
+
+ public function executeOne($queryKey, $params = array(), $hydrationMode = IPF_ORM::HYDRATE_RECORD)
+ {
+ return IPF_ORM_Manager::getInstance()
+ ->getQueryRegistry()
+ ->get($queryKey, $this->getComponentName())
+ ->fetchOne($params, $hydrationMode);
+ }
+
+ public function clear()
+ {
+ $this->_identityMap = array();
+ }
+
+ public function addRecord(IPF_ORM_Record $record)
+ {
+ $id = implode(' ', $record->identifier());
+
+ if (isset($this->_identityMap[$id])) {
+ return false;
+ }
+
+ $this->_identityMap[$id] = $record;
+
+ return true;
+ }
+
+ public function removeRecord(IPF_ORM_Record $record)
+ {
+ $id = implode(' ', $record->identifier());
+
+ if (isset($this->_identityMap[$id])) {
+ unset($this->_identityMap[$id]);
+ return true;
+ }
+
+ return false;
+ }
+
+ public function getRecord()
+ {
+ if ( ! empty($this->_data)) {
+ $identifierFieldNames = $this->getIdentifier();
+
+ if ( ! is_array($identifierFieldNames)) {
+ $identifierFieldNames = array($identifierFieldNames);
+ }
+
+ $found = false;
+ foreach ($identifierFieldNames as $fieldName) {
+ if ( ! isset($this->_data[$fieldName])) {
+ // primary key column not found return new record
+ $found = true;
+ break;
+ }
+ $id[] = $this->_data[$fieldName];
+ }
+
+ if ($found) {
+ $recordName = $this->getComponentName();
+ $record = new $recordName($this, true);
+ $this->_data = array();
+ return $record;
+ }
+
+
+ $id = implode(' ', $id);
+
+ if (isset($this->_identityMap[$id])) {
+ $record = $this->_identityMap[$id];
+ $record->hydrate($this->_data);
+ } else {
+ $recordName = $this->getComponentName();
+ $record = new $recordName($this);
+ $this->_identityMap[$id] = $record;
+ }
+ $this->_data = array();
+ } else {
+ $recordName = $this->getComponentName();
+ $record = new $recordName($this, true);
+ }
+
+ return $record;
+ }
+
+ final public function applyInheritance($where)
+ {
+ if ( ! empty($this->_options['inheritanceMap'])) {
+ $a = array();
+ foreach ($this->_options['inheritanceMap'] as $field => $value) {
+ $a[] = $this->getColumnName($field) . ' = ?';
+ }
+ $i = implode(' AND ', $a);
+ $where .= ' AND ' . $i;
+ }
+ return $where;
+ }
+
+ public function count()
+ {
+ $a = $this->_conn->execute('SELECT COUNT(1) FROM ' . $this->_options['tableName'])->fetch(IPF_ORM::FETCH_NUM);
+ return current($a);
+ }
+
+ public function getQueryObject()
+ {
+ $graph = new IPF_ORM_Query($this->getConnection());
+ $graph->load($this->getComponentName());
+ return $graph;
+ }
+
+ public function getEnumValues($fieldName)
+ {
+ $columnName = $this->getColumnName($fieldName);
+ if (isset($this->_columns[$columnName]['values'])) {
+ return $this->_columns[$columnName]['values'];
+ } else {
+ return array();
+ }
+ }
+
+ public function enumValue($fieldName, $index)
+ {
+ if (IPF_ORM_Null::isNull($index)) {
+ return $index;
+ }
+
+ $columnName = $this->getColumnName($fieldName);
+ if ( ! $this->_conn->getAttribute(IPF_ORM::ATTR_USE_NATIVE_ENUM)
+ && isset($this->_columns[$columnName]['values'][$index])
+ ) {
+ return $this->_columns[$columnName]['values'][$index];
+ }
+
+ return $index;
+ }
+
+ public function enumIndex($fieldName, $value)
+ {
+ $values = $this->getEnumValues($fieldName);
+
+ $index = array_search($value, $values);
+ if ($index === false || !$this->_conn->getAttribute(IPF_ORM::ATTR_USE_NATIVE_ENUM)) {
+ return $index;
+ }
+ return $value;
+ }
+
+ public function getColumnCount()
+ {
+ return count($this->_columns);
+ }
+
+ public function getColumns()
+ {
+ return $this->_columns;
+ }
+
+ public function removeColumn($fieldName)
+ {
+ if ($this->hasField($fieldName)) {
+ $columnName = $this->getColumnName($fieldName);
+ unset($this->_columnNames[$fieldName], $this->_fieldNames[$columnName], $this->_columns[$columnName]);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ public function getColumnNames(array $fieldNames = null)
+ {
+ if ($fieldNames === null) {
+ return array_keys($this->_columns);
+ } else {
+ $columnNames = array();
+ foreach ($fieldNames as $fieldName) {
+ $columnNames[] = $this->getColumnName($fieldName);
+ }
+ return $columnNames;
+ }
+ }
+
+ public function getIdentifierColumnNames()
+ {
+ return $this->getColumnNames((array) $this->getIdentifier());
+ }
+
+ public function getFieldNames()
+ {
+ return array_values($this->_fieldNames);
+ }
+
+ public function getDefinitionOf($fieldName)
+ {
+ $columnName = $this->getColumnName($fieldName);
+ return $this->getColumnDefinition($columnName);
+ }
+
+ public function getTypeOf($fieldName)
+ {
+ return $this->getTypeOfColumn($this->getColumnName($fieldName));
+ }
+
+ public function getTypeOfColumn($columnName)
+ {
+ return isset($this->_columns[$columnName]) ? $this->_columns[$columnName]['type'] : false;
+ }
+
+ public function setData(array $data)
+ {
+ $this->_data = $data;
+ }
+
+ public function getData()
+ {
+ return $this->_data;
+ }
+
+ public function prepareValue($fieldName, $value, $typeHint = null)
+ {
+ if (IPF_ORM_Null::isNull($value)) {
+ return $value;
+ } else if ($value === null) {
+ return null;
+ } else {
+ $type = is_null($typeHint) ? $this->getTypeOf($fieldName) : $typeHint;
+
+ switch ($type) {
+ case 'integer':
+ case 'string';
+ // don't do any casting here PHP INT_MAX is smaller than what the databases support
+ break;
+ case 'enum':
+ return $this->enumValue($fieldName, $value);
+ break;
+ case 'boolean':
+ return (boolean) $value;
+ break;
+ case 'array':
+ case 'object':
+ if (is_string($value)) {
+ $value = empty($value) ? null:unserialize($value);
+
+ if ($value === false) {
+ throw new IPF_ORM_Exception('Unserialization of ' . $fieldName . ' failed.');
+ }
+ return $value;
+ }
+ break;
+ case 'gzip':
+ $value = gzuncompress($value);
+
+ if ($value === false) {
+ throw new IPF_ORM_Exception('Uncompressing of ' . $fieldName . ' failed.');
+ }
+ return $value;
+ break;
+ }
+ }
+ return $value;
+ }
+
+ public function getComponentName()
+ {
+ return $this->_options['name'];
+ }
+
+ public function getTableName()
+ {
+ return $this->_options['tableName'];
+ }
+
+ public function setTableName($tableName)
+ {
+ $this->setOption('tableName', $tableName);
+ }
+
+ public function getTemplate($template)
+ {
+ if (!isset($this->_templates[$template]))
+ throw new IPF_ORM_Exception('Template ' . $template . ' not loaded');
+ return $this->_templates[$template];
+ }
+
+ public function hasTemplate($template)
+ {
+ return isset($this->_templates[$template]);
+ }
+
+ public function addTemplate($tpl, array $options=array())
+ {
+ if (!is_object($tpl)) {
+ $className = 'IPF_ORM_Template_' . $tpl;
+
+ if (class_exists($className, true)) {
+ $tpl = new $className($options);
+ } else if (class_exists($tpl, true)) {
+ $tpl = new $tpl($options);
+ } else {
+ throw new IPF_ORM_Record_Exception('Could not load behavior named: "' . $tpl . '"');
+ }
+ }
+
+ if (!($tpl instanceof IPF_ORM_Template)) {
+ throw new IPF_ORM_Record_Exception('Loaded behavior class is not an istance of IPF_ORM_Template.');
+ }
+
+ $className = get_class($tpl);
+ $this->_templates[$className] = $tpl;
+
+ $tpl->setTableDefinition($this);
+ }
+
+ public function setOrdering($ordering)
+ {
+ $this->_ordering = $ordering;
+ }
+
+ public function getOrdering()
+ {
+ return $this->_ordering;
+ }
+
+ public function bindQueryParts(array $queryParts)
+ {
+ $this->_options['queryParts'] = $queryParts;
+ return $this;
+ }
+
+ public function bindQueryPart($queryPart, $value)
+ {
+ $this->_options['queryParts'][$queryPart] = $value;
+ return $this;
+ }
+
+ public function getFieldValidators($fieldName)
+ {
+ $validators = array();
+ $columnName = $this->getColumnName($fieldName);
+ // this loop is a dirty workaround to get the validators filtered out of
+ // the options, since everything is squeezed together currently
+
+ if (!isset($this->_columns[$columnName]))
+ return array();
+
+ foreach ($this->_columns[$columnName] as $name => $args) {
+ if (empty($name)
+ || $name == 'primary'
+ || $name == 'protected'
+ || $name == 'autoincrement'
+ || $name == 'default'
+ || $name == 'values'
+ || $name == 'zerofill'
+ || $name == 'owner'
+ || $name == 'scale'
+ || $name == 'type'
+ || $name == 'length'
+ || $name == 'fixed') {
+ continue;
+ }
+ if ($name == 'notnull' && isset($this->_columns[$columnName]['autoincrement'])) {
+ continue;
+ }
+ // skip it if it's explicitly set to FALSE (i.e. notnull => false)
+ if ($args === false) {
+ continue;
+ }
+ $validators[$name] = $args;
+ }
+
+ return $validators;
+ }
+
+ public function getBoundQueryPart($queryPart)
+ {
+ if (isset($this->_options['queryParts'][$queryPart]))
+ return $this->_options['queryParts'][$queryPart];
+ else
+ return null;
+ }
+
+ public function __toString()
+ {
+ return IPF_ORM_Utils::getTableAsString($this);
+ }
+
+ protected function findBy($fieldName, $value, $hydrationMode = null)
+ {
+ $q = $this->createQuery()->where($fieldName . ' = ?', array($value));
+ if ($this->_ordering)
+ $q->orderBy(implode(', ', $this->_ordering));
+ return $q->execute(array(), $hydrationMode);
+ }
+
+ protected function findOneBy($fieldName, $value, $hydrationMode = null)
+ {
+ $results = $this->createQuery()
+ ->where($fieldName . ' = ?',array($value))
+ ->limit(1)
+ ->execute(array(), $hydrationMode);
+
+ if (is_array($results) && isset($results[0])) {
+ return $results[0];
+ } else if ($results instanceof IPF_ORM_Collection && $results->count() > 0) {
+ return $results->getFirst();
+ } else {
+ return false;
+ }
+ }
+
+ protected function _resolveFindByFieldName($name)
+ {
+ $fieldName = IPF_ORM_Inflector::tableize($name);
+ if ($this->hasColumn($name) || $this->hasField($name)) {
+ return $this->getFieldName($this->getColumnName($name));
+ } else if ($this->hasColumn($fieldName) || $this->hasField($fieldName)) {
+ return $this->getFieldName($this->getColumnName($fieldName));
+ } else {
+ return false;
+ }
+ }
+
+ public function __call($method, $arguments)
+ {
+ if (substr($method, 0, 6) == 'findBy') {
+ $by = substr($method, 6, strlen($method));
+ $method = 'findBy';
+ } else if (substr($method, 0, 9) == 'findOneBy') {
+ $by = substr($method, 9, strlen($method));
+ $method = 'findOneBy';
+ }
+
+ if (isset($by)) {
+ if ( ! isset($arguments[0])) {
+ throw new IPF_ORM_Exception('You must specify the value to findBy');
+ }
+
+ $fieldName = $this->_resolveFindByFieldName($by);
+ $hydrationMode = isset($arguments[1]) ? $arguments[1]:null;
+ if ($this->hasField($fieldName)) {
+ return $this->$method($fieldName, $arguments[0], $hydrationMode);
+ } else if ($this->hasRelation($by)) {
+ $relation = $this->getRelation($by);
+
+ if ($relation['type'] === IPF_ORM_Relation::MANY) {
+ throw new IPF_ORM_Exception('Cannot findBy many relationship.');
+ }
+
+ return $this->$method($relation['local'], $arguments[0], $hydrationMode);
+ } else {
+ throw new IPF_ORM_Exception('Cannot find by: ' . $by . '. Invalid column or relationship alias.');
+ }
+ }
+
+ throw new IPF_ORM_Exception(sprintf('Unknown method %s::%s', get_class($this), $method));
+ }
+
+ public function notifyRecordListeners($method, $event)
+ {
+ foreach ($this->listeners as $listener)
+ if (is_callable(array($listener, $method)))
+ $listener->$method($event);
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Table_Repository implements Countable, IteratorAggregate
+{
+ private $table;
+ private $registry = array();
+
+ public function __construct(IPF_ORM_Table $table)
+ {
+ $this->table = $table;
+ }
+
+ public function getTable()
+ {
+ return $this->table;
+ }
+
+ public function add(IPF_ORM_Record $record)
+ {
+ $oid = $record->getOID();
+
+ if (isset($this->registry[$oid])) {
+ return false;
+ }
+ $this->registry[$oid] = $record;
+
+ return true;
+ }
+
+ public function get($oid)
+ {
+ if ( ! isset($this->registry[$oid])) {
+ throw new IPF_ORM_Exception("Unknown object identifier");
+ }
+ return $this->registry[$oid];
+ }
+
+ public function count()
+ {
+ return count($this->registry);
+ }
+
+ public function evict($oid)
+ {
+ if ( ! isset($this->registry[$oid])) {
+ return false;
+ }
+ unset($this->registry[$oid]);
+ return true;
+ }
+
+ public function evictAll()
+ {
+ $evicted = 0;
+ foreach ($this->registry as $oid=>$record) {
+ if ($this->evict($oid)) {
+ $evicted++;
+ }
+ }
+ return $evicted;
+ }
+
+ public function getIterator()
+ {
+ return new ArrayIterator($this->registry);
+ }
+
+ public function contains($oid)
+ {
+ return isset($this->registry[$oid]);
+ }
+
+ public function loadAll()
+ {
+ $this->table->findAll();
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+abstract class IPF_ORM_Template
+{
+ protected $_invoker;
+
+ public function setInvoker(IPF_ORM_Record $invoker)
+ {
+ $this->_invoker = $invoker;
+ }
+
+ public function getInvoker()
+ {
+ return $this->_invoker;
+ }
+
+ abstract public function setTableDefinition(IPF_ORM_Table $table);
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Template_Listener_Orderable
+{
+ private $columnName, $prepend;
+
+ public function __construct($columnName, $prepend)
+ {
+ $this->columnName = $columnName;
+ $this->prepend = $prepend;
+ }
+
+ public function preInsert(IPF_ORM_Event $event)
+ {
+ $this->setOrderValue($event->getInvoker());
+ }
+
+ public function preUpdate(IPF_ORM_Event $event)
+ {
+ $this->setOrderValue($event->getInvoker());
+ }
+
+ private function setOrderValue($obj)
+ {
+ $columnName = $this->columnName;
+ if ($obj->$columnName !== null)
+ return;
+
+ if ($this->prepend) {
+ $f = 'min';
+ $d = '-';
+ } else {
+ $f = 'max';
+ $d = '+';
+ }
+
+ $res = IPF_ORM_Query::create()
+ ->select('coalesce('.$f.'('.$this->columnName.') '.$d.' 1, 1) as x_ord')
+ ->from(get_class($obj))
+ ->execute();
+
+ $obj->$columnName = (int)$res[0]->x_ord;
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Template_Listener_Owned
+{
+ private $columnName;
+
+ public function __construct($columnName)
+ {
+ $this->columnName = $columnName;
+ }
+
+ public function preInsert(IPF_ORM_Event $event)
+ {
+ $this->setOwner($event->getInvoker());
+ }
+
+ public function preUpdate(IPF_ORM_Event $event)
+ {
+ $this->setOwner($event->getInvoker());
+ }
+
+ private function setOwner($obj)
+ {
+ $columnName = $this->columnName;
+ if ($obj->$columnName)
+ return;
+
+ $request = IPF_Project::getInstance()->request;
+ if ($request && !$request->user->isAnonymous()) {
+ $obj->$columnName = $request->user->id;
+ }
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Template_Listener_Sluggable
+{
+ protected $_options = array();
+
+ public function __construct(array $options)
+ {
+ $this->_options = $options;
+ }
+
+ public function preInsert(IPF_ORM_Event $event)
+ {
+ $name = $this->_options['name'];
+
+ $record = $event->getInvoker();
+
+ if ( ! $record->$name) {
+ $record->$name = $this->buildSlug($record);
+ }
+ }
+
+ public function preUpdate(IPF_ORM_Event $event)
+ {
+ if (false !== $this->_options['unique']) {
+ $name = $this->_options['name'];
+
+ $record = $event->getInvoker();
+
+ if ( ! $record->$name ||
+ (false !== $this->_options['canUpdate'] &&
+ array_key_exists($name, $record->getModified()))) {
+ $record->$name = $this->buildSlug($record);
+ }
+ }
+ }
+
+ protected function buildSlug($record)
+ {
+ if (empty($this->_options['fields'])) {
+ if (method_exists($record, 'getUniqueSlug')) {
+ $value = $record->getUniqueSlug($record);
+ } else {
+ $value = (string) $record;
+ }
+ } else {
+ if ($this->_options['unique'] === true) {
+ $value = $this->getUniqueSlug($record);
+ } else {
+ $value = '';
+ foreach ($this->_options['fields'] as $field) {
+ $value .= $record->$field . ' ';
+ }
+ }
+ }
+
+ $value = call_user_func_array($this->_options['builder'], array($value, $record));
+
+ return $value;
+ }
+
+ public function getUniqueSlug($record)
+ {
+ $name = $this->_options['name'];
+ $slugFromFields = '';
+ foreach ($this->_options['fields'] as $field) {
+ $slugFromFields .= $record->$field . ' ';
+ }
+
+ $proposal = $record->$name ? $record->$name : $slugFromFields;
+ $proposal = call_user_func_array($this->_options['builder'], array($proposal, $record));
+ $slug = $proposal;
+
+ $whereString = 'r.' . $name . ' LIKE ?';
+ $whereParams = array($proposal.'%');
+
+ if ($record->exists()) {
+ $identifier = $record->identifier();
+ $whereString .= ' AND r.' . implode(' != ? AND r.', $record->getTable()->getIdentifierColumnNames()) . ' != ?';
+ $whereParams = array_merge($whereParams, array_values($identifier));
+ }
+
+ foreach ($this->_options['uniqueBy'] as $uniqueBy) {
+ if (is_null($record->$uniqueBy)) {
+ $whereString .= ' AND r.'.$uniqueBy.' IS NULL';
+ } else {
+ $whereString .= ' AND r.'.$uniqueBy.' = ?';
+ $whereParams[] = $record->$uniqueBy;
+ }
+ }
+
+ $query = IPF_ORM_Query::create()
+ ->select('r.'.$name)
+ ->from(get_class($record).' r')
+ ->where($whereString , $whereParams)
+ ->setHydrationMode(IPF_ORM::HYDRATE_ARRAY);
+
+ $similarSlugResult = $query->execute();
+
+ $similarSlugs = array();
+ foreach ($similarSlugResult as $key => $value) {
+ $similarSlugs[$key] = $value[$name];
+ }
+
+ $i = 1;
+ while (in_array($slug, $similarSlugs)) {
+ $slug = $proposal.'-'.$i;
+ $i++;
+ }
+
+ return $slug;
+ }
+}
--- /dev/null
+<?php
+
+class IPF_ORM_Template_Listener_Timestampable
+{
+ protected $_options = array();
+ public function __construct(array $options)
+ {
+ $this->_options = $options;
+ }
+
+ public function preInsert(IPF_ORM_Event $event)
+ {
+ if( ! $this->_options['created']['disabled']) {
+ $createdName = $this->_options['created']['name'];
+ $event->getInvoker()->$createdName = $this->getTimestamp('created');
+ }
+
+ if( ! $this->_options['updated']['disabled'] && $this->_options['updated']['onInsert']) {
+ $updatedName = $this->_options['updated']['name'];
+ $event->getInvoker()->$updatedName = $this->getTimestamp('updated');
+ }
+ }
+
+ public function preUpdate(IPF_ORM_Event $event)
+ {
+ if( ! $this->_options['updated']['disabled']) {
+ $updatedName = $this->_options['updated']['name'];
+ $event->getInvoker()->$updatedName = $this->getTimestamp('updated');
+ }
+ }
+
+ public function getTimestamp($type)
+ {
+ $options = $this->_options[$type];
+
+ if ($options['expression'] !== false && is_string($options['expression'])) {
+ return new IPF_ORM_Expression($options['expression']);
+ } else {
+ if ($options['type'] == 'date') {
+ return gmdate($options['format']);
+ } else if ($options['type'] == 'timestamp') {
+ return gmdate($options['format']);
+ } else {
+ return gmmktime();
+ }
+ }
+ }
+}
--- /dev/null
+<?php
+
+class IPF_ORM_Template_Orderable extends IPF_ORM_Template
+{
+ private $columnName = 'ord';
+ private $exclude = true;
+ private $prepend = false;
+
+ public function __construct(array $options=array())
+ {
+ if ($options) {
+ if (array_key_exists('name', $options))
+ $this->columnName = $options['name'];
+ if (array_key_exists('exclude', $options))
+ $this->exclude = $options['exclude'];
+ if (array_key_exists('prepend', $options))
+ $this->prepend = $options['prepend'];
+ }
+ }
+
+ public function getColumnName()
+ {
+ return $this->columnName;
+ }
+
+ public function setTableDefinition(IPF_ORM_Table $table)
+ {
+ $table->setColumn($this->columnName, 'integer', null, array('exclude' => $this->exclude));
+ $table->addIndex($table->getOption('tableName') . '_orderable_' . $this->columnName, array('fields' => array($this->columnName)));
+ $table->listeners['Orderable_'.$this->columnName] = new IPF_ORM_Template_Listener_Orderable($this->columnName, $this->prepend);
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Template_Owned extends IPF_ORM_Template
+{
+ private $name = 'owner';
+ private $columnName = 'owner_id';
+ private $exclude = true;
+ private $verbose = 'owner';
+
+ public function __construct(array $options=array())
+ {
+ if ($options) {
+ if (array_key_exists('column', $options))
+ $this->columnName = $options['column'];
+ if (array_key_exists('name', $options))
+ $this->name = $options['name'];
+ if (array_key_exists('exclude', $options))
+ $this->exclude = $options['exclude'];
+ if (array_key_exists('verbose', $options))
+ $this->verbose = $options['verbose'];
+ }
+ }
+
+ public function getColumnName()
+ {
+ return $this->columnName;
+ }
+
+ public function setTableDefinition(IPF_ORM_Table $table)
+ {
+ $table->setColumn($this->columnName, 'integer', null, array(
+ 'exclude' => $this->exclude,
+ 'verbose' => $this->verbose,
+ ));
+
+ $fks = $table->getOption('foreignKeys', array());
+ $fks[] = array(
+ 'local' => $this->columnName,
+ 'foreign' => 'id',
+ 'foreignTable' => 'auth_users',
+ 'onUpdate' => null,
+ 'onDelete' => 'CASCADE',
+ );
+ $table->setOption('foreignKeys', $fks);
+
+ $table->listeners['Owned_'.$this->columnName] = new IPF_ORM_Template_Listener_Owned($this->columnName);
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Template_Sluggable extends IPF_ORM_Template
+{
+ protected $_options = array('name' => 'slug',
+ 'type' => 'string',
+ 'length' => 255,
+ 'unique' => true,
+ 'options' => array(),
+ 'fields' => array(),
+ 'uniqueBy' => array(),
+ 'uniqueIndex' => true,
+ 'canUpdate' => false,
+ 'builder' => array('IPF_ORM_Inflector', 'urlize'),
+ 'indexName' => 'sluggable'
+ );
+
+ public function __construct(array $options = array())
+ {
+ $this->_options = IPF_ORM_Utils::arrayDeepMerge($this->_options, $options);
+ }
+
+ public function setTableDefinition(IPF_ORM_Table $table)
+ {
+ $table->setColumn($this->_options['name'], $this->_options['type'], $this->_options['length'], $this->_options['options']);
+
+ if ($this->_options['unique'] == true && $this->_options['uniqueIndex'] == true && !empty($this->_options['fields'])) {
+ $indexFields = array($this->_options['name']);
+ $indexFields = array_merge($indexFields, $this->_options['uniqueBy']);
+ $table->addIndex($this->_options['indexName'], array('fields' => $indexFields, 'type' => 'unique'));
+ }
+ $table->listeners['Sluggable_'.print_r($this->_options, true)] = new IPF_ORM_Template_Listener_Sluggable($this->_options);
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Template_Timestampable extends IPF_ORM_Template
+{
+ protected $_options = array(
+ 'created' => array(
+ 'name' => 'created_at',
+ 'type' => 'timestamp',
+ 'format' => 'Y-m-d H:i:s',
+ 'disabled' => false,
+ 'expression' => false,
+ 'options' => array('exclude' => true),
+ ),
+ 'updated' => array(
+ 'name' => 'updated_at',
+ 'type' => 'timestamp',
+ 'format' => 'Y-m-d H:i:s',
+ 'disabled' => false,
+ 'expression' => false,
+ 'onInsert' => true,
+ 'exclude' => true,
+ 'options' => array('exclude' => true),
+ ),
+ );
+
+ public function __construct(array $options = array())
+ {
+ $this->_options = IPF_ORM_Utils::arrayDeepMerge($this->_options, $options);
+ }
+
+ public function setTableDefinition(IPF_ORM_Table $table)
+ {
+ if (!$this->_options['created']['disabled']) {
+ $table->setColumn($this->_options['created']['name'], $this->_options['created']['type'], null, $this->_options['created']['options']);
+ }
+ if (!$this->_options['updated']['disabled']) {
+ $table->setColumn($this->_options['updated']['name'], $this->_options['updated']['type'], null, $this->_options['updated']['options']);
+ }
+ $table->listeners['Timestampable_'.print_r($this->_options, true)] = new IPF_ORM_Template_Listener_Timestampable($this->_options);
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Transaction extends IPF_ORM_Connection_Module
+{
+ const STATE_SLEEP = 0;
+ const STATE_ACTIVE = 1;
+ const STATE_BUSY = 2;
+
+ protected $_nestingLevel = 0;
+ protected $_internalNestingLevel = 0;
+ protected $invalid = array();
+ protected $savePoints = array();
+ protected $_collections = array();
+
+ public function addCollection(IPF_ORM_Collection $coll)
+ {
+ $this->_collections[] = $coll;
+
+ return $this;
+ }
+
+ public function getState()
+ {
+ switch ($this->_nestingLevel) {
+ case 0:
+ return IPF_ORM_Transaction::STATE_SLEEP;
+ break;
+ case 1:
+ return IPF_ORM_Transaction::STATE_ACTIVE;
+ break;
+ default:
+ return IPF_ORM_Transaction::STATE_BUSY;
+ }
+ }
+
+ public function addInvalid(IPF_ORM_Record $record)
+ {
+ if (in_array($record, $this->invalid, true)) {
+ return false;
+ }
+ $this->invalid[] = $record;
+ return true;
+ }
+
+ public function getInvalid()
+ {
+ return $this->invalid;
+ }
+
+ public function getTransactionLevel()
+ {
+ return $this->_nestingLevel;
+ }
+
+ public function getInternalTransactionLevel()
+ {
+ return $this->_internalNestingLevel;
+ }
+
+ public function beginTransaction($savepoint = null)
+ {
+ if ( ! is_null($savepoint)) {
+ $this->savePoints[] = $savepoint;
+
+ $event = new IPF_ORM_Event($this, IPF_ORM_Event::SAVEPOINT_CREATE);
+
+ $this->conn->notifyDBListeners('preSavepointCreate', $event);
+
+ if ( ! $event->skipOperation) {
+ $this->createSavePoint($savepoint);
+ }
+
+ $this->conn->notifyDBListeners('postSavepointCreate', $event);
+ } else {
+ if ($this->_nestingLevel == 0) {
+ $event = new IPF_ORM_Event($this, IPF_ORM_Event::TX_BEGIN);
+
+ $this->conn->notifyDBListeners('preTransactionBegin', $event);
+
+ if ( ! $event->skipOperation) {
+ try {
+ $this->_doBeginTransaction();
+ } catch (Exception $e) {
+ throw new IPF_ORM_Exception($e->getMessage());
+ }
+ }
+ $this->conn->notifyDBListeners('postTransactionBegin', $event);
+ }
+ }
+
+ $level = ++$this->_nestingLevel;
+
+ return $level;
+ }
+
+ public function commit($savepoint = null)
+ {
+ if ($this->_nestingLevel == 0) {
+ throw new IPF_ORM_Exception("Commit failed. There is no active transaction.");
+ }
+
+ if ( ! is_null($savepoint)) {
+ $this->_nestingLevel -= $this->removeSavePoints($savepoint);
+
+ $event = new IPF_ORM_Event($this, IPF_ORM_Event::SAVEPOINT_COMMIT);
+
+ $this->conn->notifyDBListeners('preSavepointCommit', $event);
+
+ if ( ! $event->skipOperation) {
+ $this->releaseSavePoint($savepoint);
+ }
+
+ $this->conn->notifyDBListeners('postSavepointCommit', $event);
+ } else {
+
+ if ($this->_nestingLevel == 1 || $this->_internalNestingLevel == 1) {
+ if ( ! empty($this->invalid)) {
+ if ($this->_internalNestingLevel == 1) {
+ // transaction was started by IPF_ORM, so we are responsible
+ // for a rollback
+ $this->rollback();
+ $tmp = $this->invalid;
+ $this->invalid = array();
+ throw new IPF_ORM_Exception_Validator($tmp);
+ }
+ }
+ if ($this->_nestingLevel == 1) {
+ // take snapshots of all collections used within this transaction
+ foreach ($this->_collections as $coll) {
+ $coll->takeSnapshot();
+ }
+ $this->_collections = array();
+
+ $event = new IPF_ORM_Event($this, IPF_ORM_Event::TX_COMMIT);
+
+ $this->conn->notifyDBListeners('preTransactionCommit', $event);
+ if ( ! $event->skipOperation) {
+ $this->_doCommit();
+ }
+ $this->conn->notifyDBListeners('postTransactionCommit', $event);
+ }
+ }
+
+ if ($this->_nestingLevel > 0) {
+ $this->_nestingLevel--;
+ }
+ if ($this->_internalNestingLevel > 0) {
+ $this->_internalNestingLevel--;
+ }
+ }
+
+ return true;
+ }
+
+ public function rollback($savepoint = null)
+ {
+ if ($this->_nestingLevel == 0) {
+ throw new IPF_ORM_Exception("Rollback failed. There is no active transaction.");
+ }
+
+ if ($this->_internalNestingLevel > 1 || $this->_nestingLevel > 1) {
+ $this->_internalNestingLevel--;
+ $this->_nestingLevel--;
+ return false;
+ }
+
+ if ( ! is_null($savepoint)) {
+ $this->_nestingLevel -= $this->removeSavePoints($savepoint);
+
+ $event = new IPF_ORM_Event($this, IPF_ORM_Event::SAVEPOINT_ROLLBACK);
+
+ $this->conn->notifyDBListeners('preSavepointRollback', $event);
+
+ if ( ! $event->skipOperation) {
+ $this->rollbackSavePoint($savepoint);
+ }
+
+ $this->conn->notifyDBListeners('postSavepointRollback', $event);
+ } else {
+ $event = new IPF_ORM_Event($this, IPF_ORM_Event::TX_ROLLBACK);
+
+ $this->conn->notifyDBListeners('preTransactionRollback', $event);
+
+ if ( ! $event->skipOperation) {
+ $this->_nestingLevel = 0;
+ $this->_internalNestingLevel = 0;
+ try {
+ $this->_doRollback();
+ } catch (Exception $e) {
+ throw new IPF_ORM_Exception($e->getMessage());
+ }
+ }
+
+ $this->conn->notifyDBListeners('postTransactionRollback', $event);
+ }
+
+ return true;
+ }
+
+ protected function createSavePoint($savepoint)
+ {
+ throw new IPF_ORM_Exception('Savepoints not supported by this driver.');
+ }
+
+ protected function releaseSavePoint($savepoint)
+ {
+ throw new IPF_ORM_Exception('Savepoints not supported by this driver.');
+ }
+
+ protected function rollbackSavePoint($savepoint)
+ {
+ throw new IPF_ORM_Exception('Savepoints not supported by this driver.');
+ }
+
+ protected function _doRollback()
+ {
+ $this->conn->getDbh()->rollback();
+ }
+
+ protected function _doCommit()
+ {
+ $this->conn->getDbh()->commit();
+ }
+
+ protected function _doBeginTransaction()
+ {
+ $this->conn->getDbh()->beginTransaction();
+ }
+
+ private function removeSavePoints($savepoint)
+ {
+ $this->savePoints = array_values($this->savePoints);
+
+ $found = false;
+ $i = 0;
+
+ foreach ($this->savePoints as $key => $sp) {
+ if ( ! $found) {
+ if ($sp === $savepoint) {
+ $found = true;
+ }
+ }
+ if ($found) {
+ $i++;
+ unset($this->savePoints[$key]);
+ }
+ }
+
+ return $i;
+ }
+
+ public function setIsolation($isolation)
+ {
+ throw new IPF_ORM_Exception('Transaction isolation levels not supported by this driver.');
+ }
+
+ public function getIsolation()
+ {
+ throw new IPF_ORM_Exception('Fetching transaction isolation level not supported by this driver.');
+ }
+
+ public function beginInternalTransaction($savepoint = null)
+ {
+ $this->_internalNestingLevel++;
+ return $this->beginTransaction($savepoint);
+ }
+}
--- /dev/null
+<?php
+
+class IPF_ORM_Transaction_Mysql extends IPF_ORM_Transaction
+{
+ protected function createSavePoint($savepoint)
+ {
+ $query = 'SAVEPOINT ' . $savepoint;
+
+ return $this->conn->execute($query);
+ }
+
+ protected function releaseSavePoint($savepoint)
+ {
+ $query = 'RELEASE SAVEPOINT ' . $savepoint;
+
+ return $this->conn->execute($query);
+ }
+
+ protected function rollbackSavePoint($savepoint)
+ {
+ $query = 'ROLLBACK TO SAVEPOINT ' . $savepoint;
+
+ return $this->conn->execute($query);
+ }
+
+ public function setIsolation($isolation)
+ {
+ switch ($isolation) {
+ case 'READ UNCOMMITTED':
+ case 'READ COMMITTED':
+ case 'REPEATABLE READ':
+ case 'SERIALIZABLE':
+ break;
+ default:
+ throw new IPF_ORM_Exception('Isolation level ' . $isolation . ' is not supported.');
+ }
+
+ $query = 'SET SESSION TRANSACTION ISOLATION LEVEL ' . $isolation;
+
+ return $this->conn->execute($query);
+ }
+
+ public function getIsolation()
+ {
+ return $this->conn->fetchOne('SELECT @@tx_isolation');
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+class IPF_ORM_Utils {
+
+ public static function getConnectionStateAsString($state)
+ {
+ switch ($state) {
+ case IPF_ORM_Transaction::STATE_SLEEP:
+ return "open";
+ break;
+ case IPF_ORM_Transaction::STATE_BUSY:
+ return "busy";
+ break;
+ case IPF_ORM_Transaction::STATE_ACTIVE:
+ return "active";
+ break;
+ }
+ }
+
+ public static function getConnectionAsString(IPF_ORM_Connection $connection)
+ {
+ $r[] = '<pre>';
+ $r[] = 'IPF_ORM_Connection object';
+ $r[] = 'State : ' . IPF_ORM_Utils::getConnectionStateAsString($connection->transaction->getState());
+ $r[] = 'Open Transactions : ' . $connection->transaction->getTransactionLevel();
+ $r[] = 'Table in memory : ' . $connection->count();
+ $r[] = 'Driver name : ' . $connection->getDriverName();
+ $r[] = "</pre>";
+ return implode("\n",$r)."<br>";
+ }
+
+ public static function getValidators()
+ {
+ return array(
+ 'country',
+ 'creditcard',
+ 'date',
+ 'driver',
+ 'email',
+ 'exception',
+ 'future',
+ 'htmlcolor',
+ 'ip',
+ 'minlength',
+ 'nospace',
+ 'notblank',
+ 'notnull',
+ 'past',
+ 'range',
+ 'readonly',
+ 'regexp',
+ 'time',
+ 'timestamp',
+ 'unique',
+ 'unsigned',
+ 'usstate',
+ 'file',
+ 'image',
+ 'html',
+ 'uploadTo',
+ 'verbose',
+ );
+ }
+
+
+ public static function arrayDeepMerge()
+ {
+ switch (func_num_args()) {
+ case 0:
+ return false;
+ case 1:
+ return func_get_arg(0);
+ case 2:
+ $args = func_get_args();
+ $args[2] = array();
+
+ if (is_array($args[0]) && is_array($args[1]))
+ {
+ foreach (array_unique(array_merge(array_keys($args[0]),array_keys($args[1]))) as $key)
+ {
+ $isKey0 = array_key_exists($key, $args[0]);
+ $isKey1 = array_key_exists($key, $args[1]);
+
+ if ($isKey0 && $isKey1 && is_array($args[0][$key]) && is_array($args[1][$key]))
+ {
+ $args[2][$key] = self::arrayDeepMerge($args[0][$key], $args[1][$key]);
+ } else if ($isKey0 && $isKey1) {
+ $args[2][$key] = $args[1][$key];
+ } else if ( ! $isKey1) {
+ $args[2][$key] = $args[0][$key];
+ } else if ( ! $isKey0) {
+ $args[2][$key] = $args[1][$key];
+ }
+ }
+
+ return $args[2];
+ } else {
+ return $args[1];
+ }
+ default:
+ $args = func_get_args();
+ $args[1] = self::arrayDeepMerge($args[0], $args[1]);
+ array_shift($args);
+ return call_user_func_array(array('IPF', 'arrayDeepMerge'), $args);
+ break;
+ }
+ }
+
+ public static function getTableAsString(IPF_ORM_Table $table)
+ {
+ $r[] = "<pre>";
+ $r[] = "Component : ".$table->getComponentName();
+ $r[] = "Table : ".$table->getTableName();
+ $r[] = "</pre>";
+
+ return implode("\n",$r)."<br>";
+ }
+
+ public static function getCollectionAsString(IPF_ORM_Collection $collection)
+ {
+ $r[] = "<pre>";
+ $r[] = get_class($collection);
+ $r[] = 'data : ' . IPF_ORM::dump($collection->getData(), false);
+ //$r[] = 'snapshot : ' . IPF_ORM::dump($collection->getSnapshot());
+ $r[] = "</pre>";
+ return implode("\n",$r);
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Validator
+{
+ private static $validators = array();
+ public static function getValidator($name)
+ {
+ if ( ! isset(self::$validators[$name])) {
+ $class = 'IPF_ORM_Validator_' . ucfirst($name);
+ if (class_exists($class)) {
+ self::$validators[$name] = new $class;
+ } else if (class_exists($name)) {
+ self::$validators[$name] = new $name;
+ } else {
+ throw new IPF_ORM_Exception("Validator named '$name' not available.");
+ }
+
+ }
+ return self::$validators[$name];
+ }
+
+ public function validateRecord(IPF_ORM_Record $record)
+ {
+ $table = $record->getTable();
+
+ // if record is transient all fields will be validated
+ // if record is persistent only the modified fields will be validated
+ $fields = $record->exists() ? $record->getModified():$record->getData();
+ foreach ($fields as $fieldName => $value) {
+ $this->validateField($table, $fieldName, $value, $record);
+ }
+ }
+
+ private function validateField(IPF_ORM_Table $table, $fieldName, $value, IPF_ORM_Record $record)
+ {
+ if (IPF_ORM_Null::isNull($value)) {
+ $value = null;
+ } else if ($value instanceof IPF_ORM_Record) {
+ $value = $value->getIncremented();
+ }
+
+ $dataType = $table->getTypeOf($fieldName);
+
+ // Validate field type
+ if (!IPF_ORM_Validator::isValidType($value, $dataType)) {
+ $record->addError($fieldName, 'type');
+ }
+
+ if ($dataType == 'enum') {
+ $enumIndex = $table->enumIndex($fieldName, $value);
+ if ($enumIndex === false) {
+ $record->addError($fieldName, 'enum');
+ }
+ }
+
+ // Validate field length
+ $definition = $table->getDefinitionOf($fieldName);
+ if (!$this->validateLength($value, $dataType, $definition['length'])) {
+ $record->addError($fieldName, 'length');
+ }
+
+ // Run all custom validators
+ foreach ($table->getFieldValidators($fieldName) as $validatorName => $args) {
+ if (!is_string($validatorName)) {
+ $validatorName = $args;
+ $args = array();
+ }
+
+ $validator = IPF_ORM_Validator::getValidator($validatorName);
+ $validator->invoker = $record;
+ $validator->field = $fieldName;
+ $validator->args = $args;
+ if (!$validator->validate($value)) {
+ $record->addError($fieldName, $validatorName);
+ }
+ }
+ }
+
+ public function validateLength($value, $type, $maximumLength)
+ {
+ if ($type == 'timestamp' || $type == 'integer' || $type == 'enum') {
+ return true;
+ } else if ($type == 'array' || $type == 'object') {
+ $length = strlen(serialize($value));
+ } else if ($type == 'decimal') {
+ if (!$maximumLength)
+ $maximumLength = 18;
+ $length = strlen($value);
+ } else {
+ $length = strlen($value);
+ }
+
+ if ($length > $maximumLength) {
+ return false;
+ }
+ return true;
+ }
+
+ public function hasErrors()
+ {
+ return (count($this->stack) > 0);
+ }
+
+ public static function isValidType($var, $type)
+ {
+ if ($var instanceof IPF_ORM_Expression) {
+ return true;
+ } else if ($var === null) {
+ return true;
+ } else if (is_object($var)) {
+ return $type == 'object';
+ }
+
+ switch ($type) {
+ case 'float':
+ case 'double':
+ case 'decimal':
+ return (string)$var == strval(floatval($var));
+ case 'integer':
+ return (string)$var == strval(intval($var));
+ case 'string':
+ return is_string($var) || is_numeric($var);
+ case 'blob':
+ case 'clob':
+ case 'gzip':
+ return is_string($var);
+ case 'array':
+ return is_array($var);
+ case 'object':
+ return is_object($var);
+ case 'boolean':
+ return is_bool($var) || (is_numeric($var) && ($var == 0 || $var == 1));
+ case 'timestamp':
+ $validator = self::getValidator('timestamp');
+ return $validator->validate($var);
+ case 'time':
+ $validator = self::getValidator('time');
+ return $validator->validate($var);
+ case 'date':
+ $validator = self::getValidator('date');
+ return $validator->validate($var);
+ case 'enum':
+ return is_string($var) || is_int($var);
+ default:
+ return false;
+ }
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Validator_Country
+{
+ private static $countries = array(
+ 'ad' => 'Andorra',
+ 'ae' => 'United Arab Emirates',
+ 'af' => 'Afghanistan',
+ 'ag' => 'Antigua and Barbuda',
+ 'ai' => 'Anguilla',
+ 'al' => 'Albania',
+ 'am' => 'Armenia',
+ 'an' => 'Netherlands Antilles',
+ 'ao' => 'Angola',
+ 'aq' => 'Antarctica',
+ 'ar' => 'Argentina',
+ 'as' => 'American Samoa',
+ 'at' => 'Austria',
+ 'au' => 'Australia',
+ 'aw' => 'Aruba',
+ 'az' => 'Azerbaijan',
+ 'ba' => 'Bosnia Hercegovina',
+ 'bb' => 'Barbados',
+ 'bd' => 'Bangladesh',
+ 'be' => 'Belgium',
+ 'bf' => 'Burkina Faso',
+ 'bg' => 'Bulgaria',
+ 'bh' => 'Bahrain',
+ 'bi' => 'Burundi',
+ 'bj' => 'Benin',
+ 'bm' => 'Bermuda',
+ 'bn' => 'Brunei Darussalam',
+ 'bo' => 'Bolivia',
+ 'br' => 'Brazil',
+ 'bs' => 'Bahamas',
+ 'bt' => 'Bhutan',
+ 'bv' => 'Bouvet Island',
+ 'bw' => 'Botswana',
+ 'by' => 'Belarus (Byelorussia)',
+ 'bz' => 'Belize',
+ 'ca' => 'Canada',
+ 'cc' => 'Cocos Islands',
+ 'cd' => 'Congo, The Democratic Republic of the',
+ 'cf' => 'Central African Republic',
+ 'cg' => 'Congo',
+ 'ch' => 'Switzerland',
+ 'ci' => 'Ivory Coast',
+ 'ck' => 'Cook Islands',
+ 'cl' => 'Chile',
+ 'cm' => 'Cameroon',
+ 'cn' => 'China',
+ 'co' => 'Colombia',
+ 'cr' => 'Costa Rica',
+ 'cs' => 'Czechoslovakia',
+ 'cu' => 'Cuba',
+ 'cv' => 'Cape Verde',
+ 'cx' => 'Christmas Island',
+ 'cy' => 'Cyprus',
+ 'cz' => 'Czech Republic',
+ 'de' => 'Germany',
+ 'dj' => 'Djibouti',
+ 'dk' => 'Denmark',
+ 'dm' => 'Dominica',
+ 'do' => 'Dominican Republic',
+ 'dz' => 'Algeria',
+ 'ec' => 'Ecuador',
+ 'ee' => 'Estonia',
+ 'eg' => 'Egypt',
+ 'eh' => 'Western Sahara',
+ 'er' => 'Eritrea',
+ 'es' => 'Spain',
+ 'et' => 'Ethiopia',
+ 'fi' => 'Finland',
+ 'fj' => 'Fiji',
+ 'fk' => 'Falkland Islands',
+ 'fm' => 'Micronesia',
+ 'fo' => 'Faroe Islands',
+ 'fr' => 'France',
+ 'fx' => 'France, Metropolitan FX',
+ 'ga' => 'Gabon',
+ 'gb' => 'United Kingdom (Great Britain)',
+ 'gd' => 'Grenada',
+ 'ge' => 'Georgia',
+ 'gf' => 'French Guiana',
+ 'gh' => 'Ghana',
+ 'gi' => 'Gibraltar',
+ 'gl' => 'Greenland',
+ 'gm' => 'Gambia',
+ 'gn' => 'Guinea',
+ 'gp' => 'Guadeloupe',
+ 'gq' => 'Equatorial Guinea',
+ 'gr' => 'Greece',
+ 'gs' => 'South Georgia and the South Sandwich Islands',
+ 'gt' => 'Guatemala',
+ 'gu' => 'Guam',
+ 'gw' => 'Guinea-bissau',
+ 'gy' => 'Guyana',
+ 'hk' => 'Hong Kong',
+ 'hm' => 'Heard and McDonald Islands',
+ 'hn' => 'Honduras',
+ 'hr' => 'Croatia',
+ 'ht' => 'Haiti',
+ 'hu' => 'Hungary',
+ 'id' => 'Indonesia',
+ 'ie' => 'Ireland',
+ 'il' => 'Israel',
+ 'in' => 'India',
+ 'io' => 'British Indian Ocean Territory',
+ 'iq' => 'Iraq',
+ 'ir' => 'Iran',
+ 'is' => 'Iceland',
+ 'it' => 'Italy',
+ 'jm' => 'Jamaica',
+ 'jo' => 'Jordan',
+ 'jp' => 'Japan',
+ 'ke' => 'Kenya',
+ 'kg' => 'Kyrgyzstan',
+ 'kh' => 'Cambodia',
+ 'ki' => 'Kiribati',
+ 'km' => 'Comoros',
+ 'kn' => 'Saint Kitts and Nevis',
+ 'kp' => 'North Korea',
+ 'kr' => 'South Korea',
+ 'kw' => 'Kuwait',
+ 'ky' => 'Cayman Islands',
+ 'kz' => 'Kazakhstan',
+ 'la' => 'Laos',
+ 'lb' => 'Lebanon',
+ 'lc' => 'Saint Lucia',
+ 'li' => 'Lichtenstein',
+ 'lk' => 'Sri Lanka',
+ 'lr' => 'Liberia',
+ 'ls' => 'Lesotho',
+ 'lt' => 'Lithuania',
+ 'lu' => 'Luxembourg',
+ 'lv' => 'Latvia',
+ 'ly' => 'Libya',
+ 'ma' => 'Morocco',
+ 'mc' => 'Monaco',
+ 'md' => 'Moldova Republic',
+ 'mg' => 'Madagascar',
+ 'mh' => 'Marshall Islands',
+ 'mk' => 'Macedonia, The Former Yugoslav Republic of',
+ 'ml' => 'Mali',
+ 'mm' => 'Myanmar',
+ 'mn' => 'Mongolia',
+ 'mo' => 'Macau',
+ 'mp' => 'Northern Mariana Islands',
+ 'mq' => 'Martinique',
+ 'mr' => 'Mauritania',
+ 'ms' => 'Montserrat',
+ 'mt' => 'Malta',
+ 'mu' => 'Mauritius',
+ 'mv' => 'Maldives',
+ 'mw' => 'Malawi',
+ 'mx' => 'Mexico',
+ 'my' => 'Malaysia',
+ 'mz' => 'Mozambique',
+ 'na' => 'Namibia',
+ 'nc' => 'New Caledonia',
+ 'ne' => 'Niger',
+ 'nf' => 'Norfolk Island',
+ 'ng' => 'Nigeria',
+ 'ni' => 'Nicaragua',
+ 'nl' => 'Netherlands',
+ 'no' => 'Norway',
+ 'np' => 'Nepal',
+ 'nr' => 'Nauru',
+ 'nt' => 'Neutral Zone',
+ 'nu' => 'Niue',
+ 'nz' => 'New Zealand',
+ 'om' => 'Oman',
+ 'pa' => 'Panama',
+ 'pe' => 'Peru',
+ 'pf' => 'French Polynesia',
+ 'pg' => 'Papua New Guinea',
+ 'ph' => 'Philippines',
+ 'pk' => 'Pakistan',
+ 'pl' => 'Poland',
+ 'pm' => 'St. Pierre and Miquelon',
+ 'pn' => 'Pitcairn',
+ 'pr' => 'Puerto Rico',
+ 'pt' => 'Portugal',
+ 'pw' => 'Palau',
+ 'py' => 'Paraguay',
+ 'qa' => 'Qatar',
+ 're' => 'Reunion',
+ 'ro' => 'Romania',
+ 'ru' => 'Russia',
+ 'rw' => 'Rwanda',
+ 'sa' => 'Saudi Arabia',
+ 'sb' => 'Solomon Islands',
+ 'sc' => 'Seychelles',
+ 'sd' => 'Sudan',
+ 'se' => 'Sweden',
+ 'sg' => 'Singapore',
+ 'sh' => 'St. Helena',
+ 'si' => 'Slovenia',
+ 'sj' => 'Svalbard and Jan Mayen Islands',
+ 'sk' => 'Slovakia (Slovak Republic)',
+ 'sl' => 'Sierra Leone',
+ 'sm' => 'San Marino',
+ 'sn' => 'Senegal',
+ 'so' => 'Somalia',
+ 'sr' => 'Suriname',
+ 'st' => 'Sao Tome and Principe',
+ 'sv' => 'El Salvador',
+ 'sy' => 'Syria',
+ 'sz' => 'Swaziland',
+ 'tc' => 'Turks and Caicos Islands',
+ 'td' => 'Chad',
+ 'tf' => 'French Southern Territories',
+ 'tg' => 'Togo',
+ 'th' => 'Thailand',
+ 'tj' => 'Tajikistan',
+ 'tk' => 'Tokelau',
+ 'tm' => 'Turkmenistan',
+ 'tn' => 'Tunisia',
+ 'to' => 'Tonga',
+ 'tp' => 'East Timor',
+ 'tr' => 'Turkey',
+ 'tt' => 'Trinidad, Tobago',
+ 'tv' => 'Tuvalu',
+ 'tw' => 'Taiwan',
+ 'tz' => 'Tanzania',
+ 'ua' => 'Ukraine',
+ 'ug' => 'Uganda',
+ 'uk' => 'United Kingdom',
+ 'um' => 'United States Minor Islands',
+ 'us' => 'United States of America',
+ 'uy' => 'Uruguay',
+ 'uz' => 'Uzbekistan',
+ 'va' => 'Vatican City',
+ 'vc' => 'Saint Vincent, Grenadines',
+ 've' => 'Venezuela',
+ 'vg' => 'Virgin Islands (British)',
+ 'vi' => 'Virgin Islands (USA)',
+ 'vn' => 'Viet Nam',
+ 'vu' => 'Vanuatu',
+ 'wf' => 'Wallis and Futuna Islands',
+ 'ws' => 'Samoa',
+ 'ye' => 'Yemen',
+ 'yt' => 'Mayotte',
+ 'yu' => 'Yugoslavia',
+ 'za' => 'South Africa',
+ 'zm' => 'Zambia',
+ 'zr' => 'Zaire',
+ 'zw' => 'Zimbabwe');
+
+ public static function getCountries()
+ {
+ return self::$countries;
+ }
+
+ public function validate($value)
+ {
+ $value = strtolower($value);
+ return isset(self::$countries[$value]);
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Validator_Creditcard
+{
+ public function validate($value)
+ {
+ $card_regexes = array(
+ "/^4\d{12}(\d\d\d){0,1}$/" => 'visa',
+ "/^5[12345]\d{14}$/" => 'mastercard',
+ "/^3[47]\d{13}$/" => 'amex',
+ "/^6011\d{12}$/" => 'discover',
+ "/^30[012345]\d{11}$/" => 'diners',
+ "/^3[68]\d{12}$/" => 'diners',
+ );
+
+ $cardType = '';
+ foreach ($card_regexes as $regex => $type) {
+ if (preg_match($regex, $value)) {
+ $cardType = $type;
+ break;
+ }
+ }
+ if (!$cardType)
+ return false;
+
+ /* mod 10 checksum algorithm */
+ $revcode = strrev($value);
+ $checksum = 0;
+ for ($i = 0; $i < strlen($revcode); $i++) {
+ $currentNum = intval($revcode[$i]);
+ if ($i & 1) { /* Odd position */
+ $currentNum *= 2;
+ }
+ /* Split digits and add. */
+ $checksum += $currentNum % 10;
+ if ($currentNum > 9) {
+ $checksum += 1;
+ }
+ }
+ return $checksum % 10 == 0;
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Validator_Date
+{
+ public function validate($value)
+ {
+ if ($value === null) {
+ return true;
+ }
+ $e = explode('-', $value);
+
+ if (count($e) !== 3) {
+ return false;
+ }
+ return checkdate($e[1], $e[2], $e[0]);
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+class IPF_ORM_Validator_Driver
+{
+ protected $_args = array();
+
+ public function __get($arg)
+ {
+ if (isset($this->_args[$arg])) {
+ return $this->_args[$arg];
+ }
+ return null;
+ }
+
+ public function __isset($arg)
+ {
+ return isset($this->_args[$arg]);
+ }
+
+ public function __set($arg, $value)
+ {
+ $this->_args[$arg] = $value;
+
+ return $this;
+ }
+
+ public function getArg($arg)
+ {
+ if ( ! isset($this->_args[$arg])) {
+ throw new IPF_ORM_Exception_Validator('Unknown option ' . $arg);
+ }
+ return $this->_args[$arg];
+ }
+
+ public function setArg($arg, $value)
+ {
+ $this->_args[$arg] = $value;
+
+ return $this;
+ }
+
+ public function getArgs()
+ {
+ return $this->_args;
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+class IPF_ORM_Validator_Email
+{
+ public function validate($value)
+ {
+ if ($value === null)
+ return true;
+ return IPF_Utils::isEmail($value);
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+class IPF_ORM_Validator_Exclude
+{
+ public function validate($value)
+ {
+ return true;
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Validator_File
+{
+ public function validate($value){
+ return true;
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+class IPF_ORM_Validator_Future
+{
+ public function validate($value)
+ {
+ if ($value === null) {
+ return true;
+ }
+ $e = explode('-', $value);
+
+ if (count($e) !== 3) {
+ return false;
+ }
+
+ if (is_array($this->args) && isset($this->args['timezone'])) {
+ switch (strtolower($this->args['timezone'])) {
+ case 'gmt':
+ $now = gmdate("U") - date("Z");
+ break;
+ default:
+ $now = getdate();
+ break;
+ }
+ } else {
+ $now = getdate();
+ }
+
+ if ($now['year'] > $e[0]) {
+ return false;
+ } else if ($now['year'] == $e[0]) {
+ if ($now['mon'] > $e[1]) {
+ return false;
+ } else if ($now['mon'] == $e[1]) {
+ return $now['mday'] < $e[2];
+ } else {
+ return true;
+ }
+ } else {
+ return true;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+class IPF_ORM_Validator_Html
+{
+ public function validate($value){
+ return true;
+ }
+}
--- /dev/null
+<?php
+
+class IPF_ORM_Validator_Htmlcolor
+{
+ public function validate($value)
+ {
+ if ( ! preg_match("/^#{0,1}[0-9a-fA-F]{6}$/", $value)) {
+ return false;
+ }
+ return true;
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+class IPF_ORM_Validator_Image
+{
+ public function validate($value){
+ return true;
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+class IPF_ORM_Validator_Ip
+{
+ public function validate($value)
+ {
+ return (bool) ip2long(str_replace("\0", '', $value));
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+class IPF_ORM_Validator_Minlength
+{
+ public function validate($value)
+ {
+ if (isset($this->args) && strlen($value) < $this->args) {
+ return false;
+ }
+ return true;
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+class IPF_ORM_Validator_Nospace extends IPF_ORM_Validator_Driver
+{
+ public function validate($value)
+ {
+ return ($value === null || ! preg_match('/\s/', $value));
+ }
+}
--- /dev/null
+<?php
+
+class IPF_ORM_Validator_Notblank extends IPF_ORM_Validator_Driver
+{
+ public function validate($value)
+ {
+ return (trim($value) !== '' && $value !== null);
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+class IPF_ORM_Validator_Notnull extends IPF_ORM_Validator_Driver
+{
+ public function validate($value)
+ {
+ return ($value !== null);
+ }
+}
--- /dev/null
+<?php
+
+class IPF_ORM_Validator_Past
+{
+ public function validate($value)
+ {
+ if ($value === null) {
+ return true;
+ }
+ $e = explode('-', $value);
+
+ if (count($e) !== 3) {
+ return false;
+ }
+
+ if (is_array($this->args) && isset($this->args['timezone'])) {
+ switch (strtolower($this->args['timezone'])) {
+ case 'gmt':
+ $now = gmdate("U") - date("Z");
+ break;
+ default:
+ $now = getdate();
+ break;
+ }
+ } else {
+ $now = getdate();
+ }
+
+ if ($now['year'] < $e[0]) {
+ return false;
+ } else if ($now['year'] == $e[0]) {
+ if ($now['mon'] < $e[1]) {
+ return false;
+ } else if ($now['mon'] == $e[1]) {
+ return $now['mday'] > $e[2];
+ } else {
+ return true;
+ }
+ } else {
+ return true;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+class IPF_ORM_Validator_Range
+{
+ public function validate($value)
+ {
+ if (isset($this->args[0]) && $value < $this->args[0]) {
+ return false;
+ }
+ if (isset($this->args[1]) && $value > $this->args[1]) {
+ return false;
+ }
+ return true;
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+class IPF_ORM_Validator_Readonly
+{
+ public function validate($value)
+ {
+ $modified = $this->invoker->getModified();
+ return array_key_exists($this->field, $modified) ? false : true;
+ }
+}
--- /dev/null
+<?php
+
+class IPF_ORM_Validator_Regexp
+{
+ public function validate($value)
+ {
+ if ( ! isset($this->args)) {
+ return true;
+ }
+ if (is_array($this->args)) {
+ foreach ($this->args as $regexp) {
+ if ( ! preg_match($regexp, $value)) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ if (preg_match($this->args, $value)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+class IPF_ORM_Validator_Time
+{
+ public function validate($value)
+ {
+ if ($value === null) {
+ return true;
+ }
+
+ $e = explode(':', $value);
+
+ if (count($e) !== 3) {
+ return false;
+ }
+
+ if ( ! preg_match('/^ *[0-9]{2}:[0-9]{2}:[0-9]{2} *$/', $value)) {
+ return false;
+ }
+
+ $hr = intval($e[0], 10);
+ $min = intval($e[1], 10);
+ $sec = intval($e[2], 10);
+
+ return $hr >= 0 && $hr <= 23 && $min >= 0 && $min <= 59 && $sec >= 0 && $sec <= 59;
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+class IPF_ORM_Validator_Timestamp
+{
+ public function validate($value)
+ {
+ if ($value === null) {
+ return true;
+ }
+
+ if ( ! preg_match('/^ *[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} *$/', $value)) {
+ return false;
+ }
+
+ list($date, $time) = explode(' ', trim($value));
+
+ $dateValidator = IPF_ORM_Validator::getValidator('date');
+ $timeValidator = IPF_ORM_Validator::getValidator('time');
+
+ if ( ! $dateValidator->validate($date)) {
+ return false;
+ }
+
+ if ( ! $timeValidator->validate($time)) {
+ return false;
+ }
+
+ return true;
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+class IPF_ORM_Validator_Unique
+{
+ public function validate($value)
+ {
+ $table = $this->invoker->getTable();
+ $pks = $table->getIdentifier();
+
+ if ( is_array($pks) ) {
+ $pks = join(',', $pks);
+ }
+
+ $sql = 'SELECT ' . $pks . ' FROM ' . $table->getTableName() . ' WHERE ' . $this->field . ' = ?';
+
+ $values = array();
+ $values[] = $value;
+
+ // If the record is not new we need to add primary key checks because its ok if the
+ // unique value already exists in the database IF the record in the database is the same
+ // as the one that is validated here.
+ $state = $this->invoker->state();
+ if ( ! ($state == IPF_ORM_Record::STATE_TDIRTY || $state == IPF_ORM_Record::STATE_TCLEAN)) {
+ foreach ((array) $table->getIdentifier() as $pk) {
+ $sql .= " AND {$pk} != ?";
+ $values[] = $this->invoker->$pk;
+ }
+ }
+
+ $stmt = $table->getConnection()->getDbh()->prepare($sql);
+ $stmt->execute($values);
+
+ return ( ! is_array($stmt->fetch()));
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+class IPF_ORM_Validator_Unsigned
+{
+ public function validate($value)
+ {
+ $int = (int) $value;
+
+ if ($int != $value || $int < 0) {
+ return false;
+ }
+ return true;
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+class IPF_ORM_Validator_UploadTo
+{
+ public function validate($value)
+ {
+ return true;
+ }
+}
+
--- /dev/null
+<?php
+
+class IPF_ORM_Validator_Usstate
+{
+ private static $states = array(
+ 'AK' => true,
+ 'AL' => true,
+ 'AR' => true,
+ 'AZ' => true,
+ 'CA' => true,
+ 'CO' => true,
+ 'CT' => true,
+ 'DC' => true,
+ 'DE' => true,
+ 'FL' => true,
+ 'GA' => true,
+ 'HI' => true,
+ 'IA' => true,
+ 'ID' => true,
+ 'IL' => true,
+ 'IN' => true,
+ 'KS' => true,
+ 'KY' => true,
+ 'LA' => true,
+ 'MA' => true,
+ 'MD' => true,
+ 'ME' => true,
+ 'MI' => true,
+ 'MN' => true,
+ 'MO' => true,
+ 'MS' => true,
+ 'MT' => true,
+ 'NC' => true,
+ 'ND' => true,
+ 'NE' => true,
+ 'NH' => true,
+ 'NJ' => true,
+ 'NM' => true,
+ 'NV' => true,
+ 'NY' => true,
+ 'OH' => true,
+ 'OK' => true,
+ 'OR' => true,
+ 'PA' => true,
+ 'PR' => true,
+ 'RI' => true,
+ 'SC' => true,
+ 'SD' => true,
+ 'TN' => true,
+ 'TX' => true,
+ 'UT' => true,
+ 'VA' => true,
+ 'VI' => true,
+ 'VT' => true,
+ 'WA' => true,
+ 'WI' => true,
+ 'WV' => true,
+ 'WY' => true
+ );
+ public function getStates()
+ {
+ return self::$states;
+ }
+
+ public function validate($value)
+ {
+ return isset(self::$states[$value]);
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+class IPF_ORM_Validator_Verbose
+{
+ public function validate($value)
+ {
+ return true;
+ }
+}
+