energine
[ class tree: energine ] [ index: energine ] [ all elements ]

Source for file DBDataSet.class.php

Documentation is available at DBDataSet.class.php

  1. <?php
  2. /**
  3.  * Содержит класс DBDataSet
  4.  *
  5.  * @package energine
  6.  * @subpackage share
  7.  * @author dr.Pavka
  8.  * @copyright Energine 2006
  9.  * @version $Id$
  10.  */
  11.  
  12. //require_once('core/modules/share/components/DataSet.class.php');
  13. //require_once('core/framework/MultiLanguageBuilder.class.php');
  14.  
  15. /**
  16.  * Класс позволяющий выводить  данные из БД
  17.  *
  18.  * @package energine
  19.  * @subpackage share
  20.  * @author dr.Pavka
  21.  */
  22. class DBDataSet extends DataSet {
  23.  
  24.     /**
  25.      * Имя таблицы содержащей переводы
  26.      *
  27.      * @var string 
  28.      * @access private
  29.      */
  30.     private $translationTableName = false;
  31.  
  32.     /**
  33.      * Имя поля первичного ключа
  34.      *
  35.      * @var string 
  36.      * @access private
  37.      */
  38.     private $pk = false;
  39.  
  40.     /**
  41.      * Условия выборки
  42.      *
  43.      * @var array 
  44.      * @access private
  45.      */
  46.     private $filter = array();
  47.  
  48.     /**
  49.      * Условие сортировки
  50.      *
  51.      * @var mixed 
  52.      * @access protected
  53.      */
  54.     private $order = null;
  55.  
  56.     /**
  57.      * Ограничение количества записей
  58.      *
  59.      * @var array 
  60.      * @access private
  61.      */
  62.     private $limit = null;
  63.  
  64.     /**
  65.      * Действие которое исполнялось на предыдущем шаге
  66.      * используется в методе сохранения
  67.      *
  68.      * @var string 
  69.      * @access private
  70.      */
  71.     private $previousAction = false;
  72.  
  73.     /**
  74.      * Конструктор класса
  75.      *
  76.      * @return void 
  77.      */
  78.     public function __construct($name$moduleDocument $document,  array $params null{
  79.         parent::__construct($name$module$document,  $params);
  80.         $this->setType(self::COMPONENT_TYPE_LIST);
  81.     }
  82.  
  83.     /**
  84.      * Добавлен параметр tableName
  85.      *
  86.      * @return type 
  87.      * @access protected
  88.      */
  89.  
  90.     protected function defineParams({
  91.         return array_merge(
  92.         parent::defineParams(),
  93.         array(
  94.         'tableName' => false,
  95.         'onlyCurrentLang' => false
  96.         )
  97.         );
  98.     }
  99.  
  100.     /**
  101.      * Переопределенный метод загрузки описания данных
  102.      * Возвращает информацию о колонках в основной таблице и таблице переводов
  103.      *
  104.      * @return array 
  105.      * @access protected
  106.      */
  107.     protected function loadDataDescription({
  108.         $result $this->dbh->getColumnsInfo($this->getTableName());
  109.         if ($this->getTranslationTableName()) {
  110.             $transColumnsDescription $this->dbh->getColumnsInfo($this->getTranslationTableName());
  111.             foreach (array_keys($transColumnsDescriptionas $fieldName{
  112.                 //для всех полей кроме идентификатора языка и первичного ключа выставляем дополнительное свойство isMultiLanguage
  113.                 if (!in_array($fieldNamearray($this->getPK()'lang_id'))) {
  114.                     $transColumnsDescription[$fieldName]['isMultilanguage'true;
  115.                 }
  116.                 elseif ($fieldName === 'lang_id' && $this->getPK(!== 'lang_id'{
  117.                     $transColumnsDescription[$fieldName]['languageID'true;
  118.                 }
  119.             }
  120.             $result += $transColumnsDescription;
  121.             if (isset($result['lang_id'])) {
  122.                 $result['lang_id']['key'false;
  123.             }
  124.             else {
  125.                 throw new SystemException('ERR_DEV_NO_LANG_ID'SystemException::ERR_DEVELOPER);
  126.             }
  127.         }
  128.  
  129.         return $result;
  130.     }
  131.  
  132.     /**
  133.       * Переопределенный метод загрузки данных
  134.       *
  135.       * @return array 
  136.       * @access protected
  137.       */
  138.     protected function loadData({
  139.         $data false;
  140.         if ($this->pager{
  141.             // pager существует -- загружаем только часть данных, текущую страницу
  142.             $this->setLimit($this->pager->getLimit());
  143.         }
  144.         //Если не существует таблицы с переводами, то выбираем данные из основной таблицы
  145.         if (!$this->getTranslationTableName()) {
  146.             $dbFields array();
  147.             foreach ($this->getDataDescription()->getFieldDescriptions(as $fieldName => $field{
  148.                 if (is_null($field->getPropertyValue('customField'))) {
  149.                     if(
  150.                        ($field->getPropertyValue('origType'&& ($field->getType(== FieldDescription::FIELD_TYPE_BOOL))
  151.                     ){
  152.                         $fieldName ' IF(('.$fieldName.' IS NOT NULL) AND ('.$fieldName.' <> ""), 1, 0) AS '.$fieldName;
  153.                     }
  154.                     array_push($dbFields$fieldName);   
  155.                 }
  156.             }
  157.             //Если не пустой массив полей для отбора
  158.             if (!empty($dbFields)) {
  159.                 if ($this->pager{
  160.                     $recordCount simplifyDBResult($this->dbh->select($this->getTableName()'count(*) as recordsCount'$this->getFilter())'recordsCount'true);
  161.                     $this->pager->setRecordsCount($recordCount);
  162.                 }
  163.                 if ($this->getType(== self::COMPONENT_TYPE_FORM_ADD {
  164.                     $dbFields array_flip($dbFields);
  165.                     foreach ($dbFields as $key => $value{
  166.                         $dbFields[$key'';
  167.                     }
  168.                     $res array($dbFields);
  169.                 }
  170.                 else {
  171.                     $res $this->dbh->select($this->getTableName()$dbFields$this->getFilter()$this->getOrder()$this->getLimit());
  172.                 }
  173.                 if (is_array($res)) {
  174.                     $data $res;
  175.                 }
  176.             }
  177.         }
  178.         else {
  179.             //Для мультиязычной таблицы - дергаем отдельный метод загрузки данных
  180.             $data $this->multiLoadData();
  181.         }
  182.         return $data;
  183.     }
  184.     /**
  185.      * Возвращает язык на которм берутся данные
  186.      *
  187.      * @return int 
  188.      * @access protected
  189.      */
  190.  
  191.     protected function getDataLanguage({
  192.         $result false;
  193.         if ($this->getParam('onlyCurrentLang')) {
  194.             $result $this->document->getLang();
  195.         }
  196.         return $result;
  197.     }
  198.  
  199.     /**
  200.      * Загрузка мультиязычных данных
  201.      *
  202.      * @return array 
  203.      * @access private
  204.      */
  205.  
  206.     private function multiLoadData({
  207.         $data false;
  208.         $lang Language::getInstance();
  209.         $lang $lang->getLanguages();
  210.         $dbFields array();
  211.         $filter $order $limit '';
  212.         //Создаем перечень полей  в формате array('имя основной таблицы' => array('имя поля'=>'имя таблицы.имя поля'), 'имя таблицыпереводов' => array('имя поля'=>'имя таблицы.имя поля'))
  213.         foreach ($this->getDataDescription()->getFieldDescriptions(as $fieldName => $field{
  214.             //Не включаем в набор идентификатор языка
  215.             if (!$field->getPropertyValue('languageID'&& $field->getPropertyValue('key'!== true{
  216.                 //не включаем в набор поля полученные  из конфигурации
  217.                 if (is_null($field->getPropertyValue('customField'))) {
  218.                     if(
  219.                        !($field->getPropertyValue('origType'&& ($field->getType(== FieldDescription::FIELD_TYPE_BOOL))
  220.                     ){
  221.                         $dbFields[$field->getPropertyValue('tableName')][$fieldName$field->getPropertyValue('tableName').'.'.$fieldName;
  222.                     }
  223.                     else{
  224.                            $dbFields[$field->getPropertyValue('tableName')][$fieldName
  225.                               ' IF(('.$field->getPropertyValue('tableName').'.'.$fieldName.' IS NOT NULL) AND ('.$field->getPropertyValue('tableName').'.'.$fieldName.' <> ""), 1, 0) AS '.$fieldName;
  226.                     }
  227.                 }
  228.             }
  229.         }
  230.         if (!is_null($this->getOrder())){
  231.             $order $this->dbh->buildOrderCondition($this->getOrder());
  232.         }
  233.  
  234.         $filterCondition $this->getFilter();
  235.         if (!empty($filterCondition)) {
  236.             $filter $this->dbh->buildWhereCondition($filterCondition).($this->getParam('onlyCurrentLang')?' AND lang_id = '.$this->getDataLanguage():'');
  237.         }
  238.         elseif($this->getDataLanguage(&&  $this->getParam('onlyCurrentLang')) {
  239.             $filter ' WHERE lang_id = '.$this->getDataLanguage();
  240.         }
  241.  
  242.         if (!is_null($this->getLimit())) {
  243.             $limit $this->getLimit();
  244.             $limit $this->dbh->buildLimitStatement($limit);
  245.         }
  246.  
  247.         //Если существует листалка указываем ей количество записей
  248.         if ($this->pager{
  249.             //Определяем общее количество записей
  250.             $request sprintf(
  251.             "SELECT COUNT(*) as records_count
  252.                    FROM %s
  253.                    LEFT JOIN %s ON %s.%s = %s.%s
  254.                    %s
  255.                    ",
  256.             $this->getTableName(),
  257.             $this->getTranslationTableName()$this->getTranslationTableName()$this->getPK()$this->getTableName()$this->getPK(),
  258.             $filter
  259.             );
  260.             $recordsCount $this->dbh->selectRequest($request);
  261.             $recordsCount simplifyDBResult($recordsCount'records_count'true);
  262.             $this->pager->setRecordsCount($recordsCount);
  263.         }
  264.         if ($this->getType(!= self::COMPONENT_TYPE_FORM_ADD{
  265.             $request=sprintf(
  266.             'SELECT
  267.                    %s.%s, %s.lang_id,
  268.                    %s
  269.                    %s
  270.                    FROM %1$s
  271.                    LEFT JOIN %3$s ON %3$s.%2$s = %1$s.%2$s
  272.                    %s
  273.                    %s
  274.                    %s
  275.                    ',
  276.             $this->getTableName()$this->getPK()$this->getTranslationTableName(),
  277.             (isset($dbFields[$this->getTableName()]))?implode(','$dbFields[$this->getTableName()]):'',
  278.             isset($dbFields[$this->getTranslationTableName()])?((isset($dbFields[$this->getTableName()]))?',':'').implode(','$dbFields[$this->getTranslationTableName()]):'',
  279.             $filter,
  280.             $order,
  281.             $limit
  282.             );
  283.             $data $this->dbh->selectRequest($request);
  284.  
  285.             //Если данные не только для текущего языка
  286.             if (is_array($data&&(!$this->getDataLanguage(|| $this->getDataLanguage()&&!$this->getParam('onlyCurrentLang'&& isset($dbFields[$this->getTranslationTableName()]))) {
  287.  
  288.                 //формируем матрицу
  289.                 foreach ($data as $row{
  290.                     $matrix[$row[$this->getPK()]][$row['lang_id']] $row;
  291.                 }
  292.                 //формируем образец
  293.                 //в нем все языкозависимые поля заполнены nullами
  294.                 foreach (array_keys($dbFields[$this->getTranslationTableName()]as $fieldName{
  295.                     $translationColumns['NULL as '.$fieldName;
  296.                 }
  297.                 $request sprintf('
  298.                     SELECT %s, %s %s
  299.                     FROM %s
  300.                     WHERE %s IN(%s)
  301.                 ',
  302.                 $this->getPK()(isset($dbFields[$this->getTableName()]))?implode(','$dbFields[$this->getTableName()]).',':''implode(','$translationColumns),
  303.                 $this->getTableName(),
  304.                 $this->getPK()implode(','array_keys($matrix)));
  305.                 $res $this->dbh->selectRequest($request);
  306.  
  307.                 foreach ($res as $row{
  308.                     $template[$row[$this->getPK()]] $row;
  309.                 }
  310.  
  311.                 $data array();
  312.  
  313.                 if ($this->getDataLanguage()&&!$this->getParam('onlyCurrentLang')) {
  314.                     $lang array($this->getDataLanguage(=> $lang[$this->getDataLanguage()]);
  315.                 }
  316.  
  317.                 foreach ($matrix as $ltagID => $langVersions{
  318.                     foreach (array_keys($langas $langID{
  319.                         if (isset($langVersions[$langID])) {
  320.                             $data[$langVersions[$langID];
  321.                         }
  322.                         else {
  323.                             $data[arrayPush($data$template[$ltagID])]['lang_id'$langID;
  324.                         }
  325.                     }
  326.                 }
  327.             }
  328.         }
  329.         else {
  330.             $i=0;
  331.             $dbFields array_merge(
  332.             (isset($dbFields[$this->getTableName()]))?array_keys($dbFields[$this->getTableName()]):array(),
  333.             array_keys($dbFields[$this->getTranslationTableName()])
  334.             );
  335.             $dbFields array_flip($dbFields);
  336.             foreach ($dbFields as $key => $value{
  337.                 $dbFields[$key'';
  338.             }
  339.             foreach (array_keys($langas $langID{
  340.                 $data[$i][$this->getPK()null;
  341.                 $data[$i]['lang_id'$langID;
  342.                 $data[$iarray_merge($data[$i]$dbFields);
  343.                 $i++;
  344.             }
  345.         }
  346.  
  347.         return $data;
  348.     }
  349.  
  350.     /**
  351.      * Устанавливает имя таблицы
  352.      *
  353.      * @param string 
  354.      * @return type 
  355.      * @access protected
  356.      */
  357.  
  358.     protected function setTableName($tableName{
  359.         $this->setParam('tableName'$tableName);
  360.     }
  361.     /**
  362.      * Для параметра tableName устанавливаем еще и имя таблицы переводов
  363.      *
  364.      * @param string $name 
  365.      * @param mixed $value 
  366.      * @return void 
  367.      * @access protected
  368.      */
  369.  
  370.     protected function setParam($name$value{
  371.         if ($name == 'tableName'{
  372.             $this->translationTableName $this->dbh->getTranslationTablename($value);
  373.         }
  374.  
  375.         parent::setParam($name$value);
  376.     }
  377.     /**
  378.      * Возвращает имя таблицы
  379.      *
  380.      * @return string 
  381.      * @access protected
  382.      * @final
  383.      */
  384.  
  385.     final protected function getTableName({
  386.         if (!$this->getParam('tableName')) {
  387.             throw new SystemException('ERR_DEV_NO_TABLENAME'SystemException::ERR_DEVELOPER);
  388.         }
  389.  
  390.         return $this->getParam('tableName');
  391.     }
  392.  
  393.     /**
  394.      * Возвращает значение фильтра
  395.      *
  396.      * @return mixed 
  397.      * @access protected
  398.      * @final
  399.      */
  400.  
  401.     final public function getFilter({
  402.         return $this->filter;
  403.     }
  404.  
  405.     /**
  406.      * Устанавливает значение фильтра
  407.      *
  408.      * @param mixed 
  409.      * @return void 
  410.      * @access protected
  411.      * @final
  412.      * @see QAL::select()
  413.      */
  414.  
  415.     final protected function setFilter($filter{
  416.         $this->clearFilter();
  417.         if (!empty($filter)) {
  418.             $this->addFilterCondition($filter);
  419.         }
  420.     }
  421.  
  422.     /**
  423.      * Добавляет условие к фильтру
  424.      *
  425.      * @return void 
  426.      * @access protected
  427.      */
  428.  
  429.     protected function addFilterCondition($filter{
  430.         if (is_numeric($filter)) {
  431.             $filter array($this->getTableName().'.'.$this->getPK()=>$filter);
  432.         }
  433.         elseif (is_string($filter)) {
  434.             $filter array($filter);
  435.         }
  436.         $this->filter array_merge($this->filter$filter);
  437.     }
  438.  
  439.     /**
  440.      * Сброс фильтра
  441.      *
  442.      * @return void 
  443.      * @access protected
  444.      * @final
  445.      */
  446.  
  447.     final protected function clearFilter({
  448.         $this->filter array();
  449.     }
  450.  
  451.     /**
  452.      * Возвращает условия сортровки
  453.      *
  454.      * @return array 
  455.      * @access protected
  456.      * @final
  457.      */
  458.  
  459.     final protected function getOrder({
  460.         return $this->order;
  461.     }
  462.  
  463.     /**
  464.      * Устанавливает условие сортровки
  465.      *
  466.      * @param mixed 
  467.      * @return void 
  468.      * @access protected
  469.      * @final
  470.      */
  471.  
  472.     final protected function setOrder($order{
  473.         if (is_array($order)) {
  474.             if (!in_array(current($order)array(QAL::ASCQAL::DESC))) {
  475.                 throw new SystemException('ERR_DEV_BAD_ORDER_FORMAT'SystemException::ERR_DEVELOPER);
  476.             }
  477.         }
  478.         $this->order $order;
  479.  
  480.         /*else {
  481.             $this->order = array($order=>QAL::ASC);
  482.         }*/
  483.     }
  484.  
  485.     /**
  486.      * Возвращает ограничения по количеству записей
  487.      *
  488.      * @return array 
  489.      * @access protected
  490.      * @final
  491.      */
  492.  
  493.     final protected function getLimit({
  494.         return $this->limit;
  495.     }
  496.  
  497.     /**
  498.      * Устанавливает ограничения по количеству записей
  499.      *
  500.      * @param array 
  501.      * @return void 
  502.      * @access protected
  503.      * @final
  504.      */
  505.  
  506.     final protected function setLimit(array $limit{
  507.         $this->limit $limit;
  508.     }
  509.  
  510.  
  511.  
  512.     /**
  513. /**
  514.      * Возвращает имя поля - первичного ключа
  515.      *
  516.      * @return string 
  517.      * @access protected
  518.      * @final
  519.      */
  520.  
  521.     final protected function getPK({
  522.         if (!$this->pk{
  523.             $res $this->dbh->getColumnsInfo($this->getTableName());
  524.             if (is_array($res)) {
  525.                 foreach ($res as $fieldName => $fieldInfo{
  526.                     if ($fieldInfo['key'=== true{
  527.                         $this->pk $fieldName;
  528.                     }
  529.                 }
  530.                 if (!isset($this->pk)) {
  531.                     throw new SystemException('ERR_DEV_NO_PK'SystemException::ERR_DEVELOPER);
  532.                 }
  533.             }
  534.             else {
  535.                 throw new SystemException('ERR_DEV_NO_PK'SystemException::ERR_DEVELOPER);
  536.             }
  537.  
  538.         }
  539.  
  540.         return $this->pk;
  541.     }
  542.  
  543.     final protected function setPK($primaryColumnName){
  544.         $this->pk $primaryColumnName;
  545.     }
  546.  
  547.     /**
  548.      * Для мультиязычного грида
  549.      * подменяем построитель
  550.      *
  551.      * @return Builder 
  552.      * @access protected
  553.      */
  554.  
  555.     protected function createBuilder({
  556.         if (!$this->getTranslationTableName()) {
  557.             $result parent::createBuilder();
  558.         }
  559.         else {
  560.             $result new MultiLanguageBuilder();
  561.         }
  562.         return $result;
  563.     }
  564.  
  565.     /**
  566.      * добавлена обработка ключей
  567.      *
  568.      * @return DataDescription 
  569.      * @access protected
  570.      */
  571.  
  572.     protected function createDataDescription({
  573.         $result parent::createDataDescription();
  574.         foreach ($result->getFieldDescriptions(as $fieldName => $fieldMetaData{
  575.             $keyInfo $fieldMetaData->getPropertyValue('key');
  576.             //Если это внешний ключ
  577.             if (is_array($keyInfo&& in_array($fieldMetaData->getType()array(FieldDescription::FIELD_TYPE_SELECTFieldDescription::FIELD_TYPE_MULTI))) {
  578.                 $fkTableName $keyInfo['tableName'];
  579.                 $fkKeyName $keyInfo['fieldName'];
  580.                 //загружаем информацию о возможных значениях
  581.                 call_user_func_array(array($fieldMetaData'loadAvailableValues')$this->getFKData($fkTableName$fkKeyName));
  582.  
  583.             }
  584.         }
  585.         return $result;
  586.     }
  587.  
  588.     /**
  589.      * Возвращает данные о значения в связанной таблицы
  590.      *
  591.      * @return array 
  592.      * @access protected
  593.      */
  594.  
  595.     protected function getFKData($fkTableName$fkKeyName{
  596.         return $this->dbh->getForeignKeyData($fkTableName$fkKeyName$this->document->getLang());
  597.     }
  598.  
  599.     /**
  600.      * Возвращает имя таблицы переводов
  601.      *
  602.      * @return string 
  603.      * @access protected
  604.      * @final
  605.      */
  606.  
  607.     final protected function getTranslationTableName({
  608.         return $this->translationTableName;
  609.     }
  610.  
  611.     /**
  612.      * Метод выводит форму просмотра
  613.      *
  614.      * @return void 
  615.      * @access protected
  616.      */
  617.  
  618.     protected function view({
  619.         $this->setType(self::COMPONENT_TYPE_FORM);
  620.         //$this->addCrumb('TXT_VIEW_ITEM');
  621.         $id $this->getActionParams();
  622.         list($id$id;
  623.         if (!$this->recordExists($id)) {
  624.             throw new SystemException('ERR_404'SystemException::ERR_404);
  625.         }
  626.         $this->setFilter($id);
  627.  
  628.         $this->prepare();
  629.         foreach ($this->getDataDescription()->getFieldDescriptions(as $fieldDescription{
  630.             $fieldDescription->setMode(FieldDescription::FIELD_MODE_READ);
  631.         }
  632.     }
  633.  
  634.     /**
  635.      * Определяет существует ли запись с идентификатором переданным в параметре
  636.      * Вызывается из методов где нужно быть уверенным в наличии записи(view, edit,delete)
  637.      *
  638.      * @param string 
  639.      * @param mixed 
  640.      * @return void 
  641.      * @access protected
  642.      * @final
  643.      */
  644.  
  645.     final protected function recordExists($id$fieldName false{
  646.         if (!$fieldName{
  647.             $fieldName $this->getPK();
  648.         }
  649.  
  650.         $res $this->dbh->select($this->getTableName()array($fieldName)array($fieldName=>$id));
  651.         return is_array($res);
  652.     }
  653.  
  654.     /**
  655.      * Возвращает предыдущее действие
  656.      *
  657.      * @return string 
  658.      * @access protected
  659.      * @final
  660.      */
  661.  
  662.     final protected function getPreviousAction({
  663.         if (!$this->previousAction{
  664.             if (!isset($_POST['componentAction'])) {
  665.                 throw new SystemException('ERR_NO_COMPONENT_ACTION'SystemException::ERR_CRITICAL);
  666.             }
  667.             else {
  668.                 $this->previousAction $_POST['componentAction'];
  669.             }
  670.         }
  671.  
  672.         return $this->previousAction;
  673.     }
  674. }
В создании документации нам помог: phpDocumentor