За последние 24 часа нас посетили 18182 программиста и 1662 робота. Сейчас ищут 1705 программистов ...

Класс работы с документами и каталогами

Тема в разделе "Решения, алгоритмы", создана пользователем MaXyC_Web_Studio, 29 май 2011.

?

Что дальше делать?

  1. Пешы есчо

    0 голосов
    0,0%
  2. Убейся ап стену

    0 голосов
    0,0%
  1. MaXyC_Web_Studio

    MaXyC_Web_Studio Активный пользователь

    С нами с:
    31 дек 2006
    Сообщения:
    678
    Симпатии:
    3
    Адрес:
    Новоуральск
    Набросал вот в пятницу класс для работы с различными видами каталогов и документов.
    Подобный класс успешно используется, например, на сайте _corbina.tv, этот немного оптимизирован и доработан.
    Хотелось бы услышать конструктивную критику и предложения по улучшению функционала.

    Использование:
    Создание объекта.
    PHP:
    1.  
    2.      // в конструкторе необходимо указать тип работы с иерархией
    3.      [b]$catalog = new Catalog(Catalog::FULLUPDATETREE );[/b]
    4.  
    PHP:
    1.  
    2.      const C2_FULLUPDATETREE = 1;              // обновлять кол-во документов в категориях от текущей категории до корня
    3.      const C2_ONLYPARENTUPDATETREE = 0; // обновлять кол-во документов только в текущей категории
    4.  
    отключение обновления кол-ва документов не делал, т.к. еще в практике использования каталогизаторов такого не было необходимости.

    Категории
    В качестве категорий я взял жесткую структуру таблицы категорий - заголовок, короткая аннотация, большой текст - описание, изображение. (!)Изображения я храню в отдельной таблице, потому в классе использую в качестве ссылки на изображение его id в другой таблице. В таблице категорий так же присутствует поле section, оно может быть либо числом, либо текстом. лично я юзаю текстовые названия секций. Если на сайте необходимо использовать множество разных каталогов, то секции тут в помощь. по умолчанию у секции значение default

    PHP:
    1.  
    2.      /**
    3.      * Добавление категории/нода
    4.      *
    5.      * @param int $parentid ID родителя. У root id = 1
    6.      * @param string $title Заголовок категории
    7.      * @param string  $note Краткое описание категории
    8.      * @param string  $text Полное описание категории
    9.      * @param int  $imageid ID изображения
    10.      *
    11.      * @return bool|int Результат добавления false или ID вставленной категории
    12.      */
    13.      [b]$category->addNode(1, 'Название категории', 'краткое описание','длинное описание', false);[/b]
    14.  
    15.      /**
    16.      * Редактирование категории/нода
    17.      *
    18.      * @param int|array $id         ID категории/нода
    19.      * @param string $title Заголовок категории
    20.      * @param string  $note Краткое описание категории
    21.      * @param string  $text Полное описание категории
    22.      * @param int  $imageid ID изображения
    23.      *
    24.      * @return bool|int Результат добавления false или ID вставленной категории
    25.      */
    26.      [b]$catalog->updateNode(2, 'Новое название категории', '', '', false);[/b]
    27.  
    28.      /**
    29.      * Перемещение категории/нода
    30.      *
    31.      * @param int|array $id                 ID категории/нода
    32.      * @param int $new_parent_id    ID нового родителя категории/нода
    33.      *
    34.      * @return bool Результат перемещения
    35.      */
    36.      [b]$catalog->moveNode(3, 2);[/b]
    37.      // прошу обратить внимание, что для перемещения нодов, можно передавать в качестве ID целое число или массив целых чисел в качестве ID категорий. Т.е. категории можно перемещать пачкой
    38.  
    39.      /**
    40.      * Удаление категории/нода
    41.      *
    42.      * @param int $id           ID категории/нода
    43.      * @param bool $fullDelete  true - удалять физически / false - использовать флажок удаления в БД
    44.      * @return bool         true - удаление успешно / false - какая-то ошибка
    45.      */
    46.      [b]$catalog->deleteNode(3, false);[/b]
    47.      // при удалении категории, удаляются и все подкатегории. (!)Документы при этом не затрагиваются, их необходимо удалять отдельно. Можно удалять физически, либо используя флаг удаления (рекомендуется, для избежания "не то удалил и теперь не восстановить", так же для избежания фрагментации)
    48.  
    Короткие и длинные описания мне обычно необходимы были в интернет магазинах

    Работа с документами
    Для работы с документами, у нас бОльший простор для фантазии со структурой таблиц. единственное условие - в таблице документов обязательно должно быть поле category_id. если вам необходимо, чтобы документ мог находиться одновременно в нескольких категориях, то category_id должен быть (var)char. Иначе достаточно smallint, или даже tinyint.

    PHP:
    1.  
    2.      /**
    3.      * Добавление документа в БД
    4.      *
    5.      * @param int|array $category_id
    6.      * @param array $data
    7.      * @return bool
    8.      */
    9.      [b]$category->addDocument($category_id, $data);[/b]
    10.  
    11.      /**
    12.      * Удаление документа из БД
    13.      *
    14.      * @param int|array $id
    15.      * @param bool $fullDelete
    16.      * @return bool
    17.      */
    18.      [b]$category->deleteDocument($id, $fullDelete = false);[/b]
    19.  
    20.      /**
    21.      * Редактирование документа в БД
    22.      * Для перемещения документа в другую категорию достаточно просто указать новые категории в $data
    23.      *
    24.      * @param int $id ID документа
    25.      * @param array $data   Данные документа
    26.      * @return bool
    27.      */
    28.      [b]$category->updateDocument($id, $data);[/b]
    29.  
    30.      /**
    31.      * Перемещаем документ(ы) в новую категорию(и)
    32.      *
    33.      * @param int|array $id
    34.      * @param int|array $new_category_id
    35.      * @param bool $updateDocument Необходимо ли обновлять поле category_id у текущего документа(ов), по умолчаанию true
    36.      * @return bool
    37.      */
    38.      [b]$category->moveDocument($id, $new_category_id, $updateDocument = true);[/b]
    39.  
    40.      /**
    41.      * Вернуть документ по его id
    42.      *
    43.      * @param int $id ID документа
    44.      * @param bool $updateViews Необходимо ли обновлять счетчик просмотров. По умолчанию true
    45.      * @return array Массив с докумеентом
    46.      */
    47.      [b]$category->getDocumentById($id, $updateViews = true);[/b]
    48.  
    В классе заведомо упущены 2 метода. Если вы знакомы с php вам не составит труда выделить 2 минуты на их написссание. Вот сам класс:
    PHP:
    1.  
    2. <?php
    3.     /**
    4.      * Класс для управления каталогами и документами
    5.      * Каталоги фиксированные для всех. Документы абстрактны.
    6.      */
    7.     class Catalog{
    8.         // Константы
    9.         const C2_FULLUPDATETREE = 1;
    10.         const C2_ONLYPARENTUPDATETREE = 0;
    11.  
    12.         // Внутренние переменные
    13.         private $_tblCategories     =   'in_catalog_new';       // название таблицы  каталога
    14.         private $_tblDocuments      =   'in_catalog_documents'; // название таблицы документов
    15.  
    16.         private $_section           =   'default';              // ID секции
    17.  
    18.         private $_cacher;
    19.         private $_cacheKeyCategory  =   '';                     // ключ для кеширования нодов. задаются в конструкторе
    20.         private $_cacheKeyDocuments =   '';                     // ключ для кеширования доков. задаются в конструкторе
    21.  
    22.         private $_categoryUpdateMethod = 0;
    23.  
    24.         private $_nodes             =   array();                // массив нод/категорий
    25.         private $_paths             =   array();                // массив путей до корня
    26.  
    27.         public function __construct($updateMethod = self::C2_FULLUPDATETREE){
    28.             $this->_cacher              =   Config::get('cacher');
    29.             $this->_cacheKeyCategory    =   'catalog-categories-'.$this->_section;
    30.             $this->_cacheKeyDocuments   =   'catalog-documents-' .$this->_section;
    31.  
    32.             $this->_categoryUpdateMethod = $updateMethod;
    33.  
    34.             if($cache = $this->_cacher->read($this->_cacheKeyCategory)){
    35.                 $this->_nodes = $cache['_nodes'];
    36.                 $this->_paths = $cache['_paths'];
    37.             }else{
    38.                 // загружаем массив категорий
    39.                 $this->_nodes = $this->_getNodeList();
    40.  
    41.                 // создаем путь к корню для каждой категории
    42.                 $this->_paths = $this->_compilePathToRoot($this->_nodes, 1);
    43.  
    44.                 // сохраняем в кеше навечно. обновляем только при обновлении категорий
    45.                 $this->_cacher->write($this->_cacheKeyCategory, array('_nodes'=>$this->_nodes, '_paths'=>$this->_paths), null, 0);
    46.             }
    47.         }
    48.  
    49.         /****  УПРАВЛЕНИЕ КАТАЛОГАМИ ****/
    50.  
    51.         /**
    52.          * Добавление категории/нода
    53.          *
    54.          * @param int $parentid ID родителя
    55.          * @param string $title Заголовок категории
    56.          * @param string  $note Краткое описание категории
    57.          * @param string  $text Полное описание категории
    58.          * @param int  $imageid ID изображения
    59.          *
    60.          * @return bool|int Результат добавления false или ID вставленной категории
    61.          */
    62.         public function addNode($parent_id = 1, $title, $note = '', $text = '', $imageid = null){
    63.             if(!$title) return;
    64.             $this->_clearCache(true); // чистим кеш
    65.  
    66.             return
    67.                 DB::insert(
    68.                     $this->_tblCategories,
    69.                     array(
    70.                         'parent_id' =>  $parent_id,
    71.                         'section'   =>  $this->_section,
    72.                         'posttime'  =>  time(),
    73.                         'user_id'   =>  Auth::$id,
    74.                         'title'     =>  $title,
    75.                         'note'      =>  $note,
    76.                         'text'      =>  $text,
    77.                         'image_id'  =>  $imageid
    78.                     )
    79.                 );
    80.         }
    81.  
    82.         /**
    83.          * Редактирование категории/нода
    84.          *
    85.          * @param int|array $id         ID категории/нода
    86.          * @param string $title Заголовок категории
    87.          * @param string  $note Краткое описание категории
    88.          * @param string  $text Полное описание категории
    89.          * @param int  $imageid ID изображения
    90.          *
    91.          * @return bool|int Результат добавления false или ID вставленной категории
    92.          */
    93.         public function updateNode($id, $title = '', $note = '', $text = '', $imageid = null){
    94.             if(!$id || !$title) return;
    95.             $this->_clearCache(true); // чистим кеш
    96.  
    97.             $data = array(
    98.                 'title'     =>  $title,
    99.                 'note'      =>  $note,
    100.                 'text'      =>  $text,
    101.             );
    102.             if($imageid)
    103.                 $data['image_id'] = $imageid;
    104.  
    105.             return
    106.                 DB::update(
    107.                     $this->_tblCategories,
    108.                     $data,
    109.                     is_array($id) ? '`id` IN ('.join(',',$id).')' : '`id`='.$id
    110.                 );
    111.         }
    112.  
    113.         /**
    114.          * Редактирование категории/нода
    115.          *
    116.          * @param int|array $id                 ID категории/нода
    117.          * @param int $new_parent_id    ID нового родителя категории/нода
    118.          *
    119.          * @return bool Результат перемещения
    120.          */
    121.         public function moveNode($id, $new_parent_id){
    122.             if(!$id || !$new_parent_id) return;
    123.  
    124.             $this->_clearCache(true); // чистим кеш
    125.  
    126.             return
    127.                 DB::update(
    128.                     $this->_tblCategories,
    129.                     array('parent_id' => $new_parent_id),
    130.                     is_array($id) ? '`id` IN ('.join(',',$id).')' : '`id`='.$id
    131.                 );
    132.         }
    133.  
    134.         /**
    135.          * Удаление категории/нода
    136.          *
    137.          * @param int $id           ID категории/нода
    138.          * @param bool $fullDelete  true - удалять физически / false - использовать флажок удаления
    139.          * @return bool             true - удаление успешно / false - какая-то ошибка
    140.          */
    141.         public function deleteNode($id, $fullDelete = false){
    142.             if(!$id) return false;
    143.             $this->_clearCache(true); // чистим кеш
    144.  
    145.             // получаем массив ID всех затронутых веток
    146.             $ids = join(',', $this->_compileChilds($this->_nodes, $id));
    147.  
    148.             //TODO: что делать с доками?
    149.             if(!$fullDelete){
    150.                 // проставляем флаги удаления
    151.                 DB::update(
    152.                     $this->_tblCategories,
    153.                     array('deleted' =>  true),
    154.                     '`id` IN ('.$ids.')'
    155.                 );
    156.             }else{
    157.                 // удаляем из БД физически
    158.                 DB::delete(
    159.                     $this->_tblCategories,
    160.                     '`id` IN ('.$ids.')'
    161.                 );
    162.             }
    163.             return true;
    164.         }
    165.  
    166.         /**** УПРАВЛЕНИЕ ДОКУМЕНТАМИ ****/
    167.  
    168.         /**
    169.          * Добавление документа в БД
    170.          *
    171.          * @param int|array $category_id
    172.          * @param array $data
    173.          * @return bool
    174.          */
    175.         public function addDocument($category_id, $data){
    176.             $data['category_id'] = is_array($category_id) ? join(',',$category_id) : $category_id;
    177.  
    178.             // увеличиваем кол-во документов в категории на 1
    179.             $this->_updateCounter($category_id, 1, $this->_categoryUpdateMethod);
    180.  
    181.             return
    182.                 DB::insert(
    183.                     $this->_tblDocuments,
    184.                     $data
    185.                 );
    186.         }
    187.  
    188.         /**
    189.          * Удаление документа из БД
    190.          *
    191.          * @param int|array $category_id
    192.          * @param array $data
    193.          * @return bool
    194.          */
    195.         public function deleteDocument($id, $fullDelete = false){
    196.             $d = $this->getDocument($id);
    197.             $category_id = (array)explode(',', $d['category_id']);
    198.  
    199.             // уменьшаем кол-во документов в категории на 1
    200.             $this->_updateCounter($category_id, -1, $this->_categoryUpdateMethod);
    201.  
    202.             return
    203.                 DB::delete(
    204.                     $this->_tblDocuments,
    205.                     '`id` = '.$id
    206.                 );
    207.         }
    208.  
    209.         /**
    210.          * Редактирование документа в БД
    211.          * Для перемещения документа в другую категорию достаточно просто указать новые категории в $data
    212.          *
    213.          * @param int $id ID документа
    214.          * @param array $data   Данные документа
    215.          * @return bool
    216.          */
    217.         public function updateDocument($id, $data){
    218.             $doc = $this->getDocumentById($id, false);
    219.            
    220.             $data['category_id'] = is_array($data['category_id']) ? join(',',$data['category_id']) : $data['category_id'];
    221.            
    222.             if($data['category_id'] != $doc['category_id']) // если категория(и) изменились,
    223.                 $this->moveDocument($id, $data['category_id'], false); // то необходимо перенести документ
    224.  
    225.             return
    226.                 DB::update(
    227.                     $this->_tblDocuments,
    228.                     $data,
    229.                     '`id`=' . $id
    230.                 );
    231.         }
    232.  
    233.         /**
    234.          * Перемещаем документ(ы) в новую категорию(и)
    235.          *
    236.          * @param int|array $id
    237.          * @param int|array $new_category_id
    238.          * @param bool $updateDocument Необходимо ли обновлять поле category_id у текущего документа(ов), по умолчааааанию true
    239.          * @return bool
    240.          */
    241.         public function moveDocument($id, $new_category_id, $updateDocument = true){
    242.             $ids = (array)$id; // приводим к общему виду
    243.            
    244.             foreach($ids as $id){
    245.                 $doc = $this->getDocumentById($id);
    246.                
    247.                 // уменьшаем кол-во документов в категории на 1
    248.                 $this->_updateCounter($doc['category_id'], -1, $this->_categoryUpdateMethod);
    249.                 // увеличиваем кол-во документов на 1 в новых категориях
    250.                 $this->_updateCounter($new_category_id, 1, $this->_categoryUpdateMethod);
    251.                
    252.                 if($updateDocument)
    253.                     DB::update($this->_tblDocuments, array('category_id'=>$new_category_id), '`id`=' . $id);
    254.             }
    255.            
    256.             return true;
    257.         }
    258.        
    259.         /**
    260.          * Вернуть документ по его id
    261.          *
    262.          * @param int $id ID документа
    263.          * @param bool $updateViews Необходимо ли обновлять счетчик просмотров. По умолчанию true
    264.          * @return array Массив с докумеентом
    265.          */
    266.         public function getDocumentById($id, $updateViews = true){
    267.             // обновляем число просмотров
    268.             if($updateViews)
    269.                 DB::query('UPDATE `'.$this->_tblDocuments.'` SET `views`=`views`+1 WHERE `id`='.$id);
    270.                
    271.             return
    272.                 DB::selectRow('
    273.                     SELECT
    274.                         *
    275.                     FROM
    276.                         `'.$this->_tblDocuments.'`
    277.                     WHERE
    278.                         `id`=' . (int)$id
    279.                 );
    280.         }
    281.  
    282.         /****    СЛУЖЕБНЫЕ ФУНКЦИИ   ****/
    283.  
    284.         /**
    285.          * Обновление счетчика документов в ноде
    286.          *
    287.          * @param int|array $category_id
    288.          * @param int $num
    289.          * @param int $updateFullTree       Если true, то обновляется весь путь до корня, иначе только текущая категория
    290.          * @return bool
    291.          */
    292.         private function _updateCounter($category_id, $num, $updateFullTree = false){
    293.             if(!is_array($category_id)) // приведем к общему виду
    294.                 $category_id[]=$category_id;
    295.  
    296.             if($updateFullTree){ // если надо обновить весь путь, то
    297.                 // пробегаемся по каждой категории, получаем весь путь до корня, и весь путь до корня обновляем
    298.                 foreach($category_id as $id)
    299.                     $ids = array_merge((array)$ids, $this->_paths[ $id ]);
    300.                 $category_id = join(',',array_merge((array)$ids, $category_id)); // получили список всех затронутых веток
    301.             }
    302.  
    303.             return
    304.                 DB::query('
    305.                     UPDATE
    306.                         `'.$this->_tblCategories.'`
    307.                     SET
    308.                         `count` = `count` + '.$num.'
    309.                     WHERE
    310.                         `id` IN ('.join(',',$category_id).')'
    311.                     );
    312.         }
    313.  
    314.         /**
    315.          * Возвращает массив всех веток
    316.          *
    317.          * @return array
    318.          */
    319.         private function _getNodeList(){
    320.             return
    321.                 DB::select('
    322.                     SELECT
    323.                         *
    324.                     FROM
    325.                         `'.$this->_tblCategories.'`
    326.                     WHERE
    327.                         `section`=\''.$this->_section.'\'
    328.                         AND `deleted` = 0
    329.                 ');
    330.         }
    331.  
    332.         /**
    333.          * Компиляция путей к корню для каждой категории
    334.          *
    335.          * @param int $id ID категории/верхушки для построения дерева
    336.          */
    337.         private function _compilePathToRoot($nodes, $parent_id = 1, &$path = array()){
    338.             static $out = array();
    339.  
    340.             foreach($nodes as $node){
    341.                 if($node['parent_id'] == $parent_id){
    342.                     $path[]=$node['parent_id'];
    343.                     $out[ $node['id'] ] = $path;
    344.                     unset($nodes[ $node['id'] ]); // чтоб по использованным уже нодам не бегать
    345.                     $this->_compilePathToRoot($nodes, $node['id'], $path);
    346.                     $path = array();
    347.                 }
    348.  
    349.                 // в основной массив добавляем URI пути к корню
    350.                 if(isset($out[ $node['id'] ])){
    351.                     $_s = array(); foreach($out[ $node['id'] ] as $id=>$_t) $_s[] = $this->_nodes[$_t]['name'];
    352.                     $this->_nodes[ $node['id'] ]['path'] = '/'.join('/', $_s).'/'.$this->_nodes[$node['id']]['name'];
    353.                 }else
    354.                     $this->_nodes[ $node['id'] ]['path'] = '/';
    355.             }
    356.  
    357.             return $out;
    358.         }
    359.  
    360.         /**
    361.          * Компилируем все ветки от корня $id
    362.          *
    363.          * @param array $nodes
    364.          * @param int $id
    365.          * @return array
    366.          */
    367.         private function _compileChilds($nodes, $parent_id = 1){
    368.             static $out = array();
    369.  
    370.             foreach($nodes as $node){
    371.                 if($node['parent_id'] == $parent_id){
    372.                     $out[] = $node['id'];
    373.                     unset($nodes[ $node['id'] ]);
    374.                     $this->_compileChilds($nodes, $node['id']);
    375.                 }
    376.             }
    377.  
    378.             return $out;
    379.         }
    380.  
    381.         /**
    382.          * Чистим кэш категорий или документов
    383.          *
    384.          * @param bool $category
    385.          * @param bool $documents
    386.          */
    387.         private function _clearCache($category = false, $documents = false){
    388.             if($category) $this->_cacher->write($this->_cacheKeyCategory, array(), null, 1);
    389.             if($documents)$this->_cacher->write($this->_cacheKeyDocuments,array(), null, 1);
    390.         }
    391.     }
    392.  
     
  2. MaXyC_Web_Studio

    MaXyC_Web_Studio Активный пользователь

    С нами с:
    31 дек 2006
    Сообщения:
    678
    Симпатии:
    3
    Адрес:
    Новоуральск
    Дополнительные классы:
    DB и Cache

    PHP:
    1. $this->_cacher                =    Config::get('cacher');
    $this->_cacher хранит в себе объект мемкеша. Методы read и write полностью идентичнны get и set в мемкеше.

    статик методы DB::select(), DB::selectRow() возвращает приведенные в массив поля таблиц.
    insert, update, delete просто помогают генерировать sql код на основе передаваемых данных.

    если необходимо то выложу дополнительные классы. но там совсем банальщина.
     
  3. [vs]

    [vs] Суперстар
    Команда форума Модератор

    С нами с:
    27 сен 2007
    Сообщения:
    10.559
    Симпатии:
    632
    лучше хранить конфигурацию в ini-файле наверное
     
  4. MaXyC_Web_Studio

    MaXyC_Web_Studio Активный пользователь

    С нами с:
    31 дек 2006
    Сообщения:
    678
    Симпатии:
    3
    Адрес:
    Новоуральск
    нет. не лучше. когда много запросов к файловой системе будет у тебя, все рухнет. поэтому у нас все шаблоны хранятся в бд. отдача гораздо выше (все в памяти хранится), доступ к файловой системе минимален.
     
  5. [vs]

    [vs] Суперстар
    Команда форума Модератор

    С нами с:
    27 сен 2007
    Сообщения:
    10.559
    Симпатии:
    632
    таблицы типа MEMORY чтоли?
    дык а что мешает кэшировать конфиг в память? Суть в том, что для изменения конфигураций не нужно будет править скрипты.
     
  6. Vladson

    Vladson Старожил

    С нами с:
    4 фев 2006
    Сообщения:
    4.040
    Симпатии:
    26
    Адрес:
    Estonia, Tallinn
    Есть мнение что это преждевременная оптимизация...

    Т.е что сначала лучше хранить конфиг там где удобнее, а уже если будет нехватать, тогда уже перенести туда где будет быстрее.