array('id' => 1, 'name' => 'Alfa Romeo','url' => 'alfa-romeo', 'image' => ''), 2 => array('id' => 2, 'name' => 'Audi', 'url' => 'audi', 'image' => 'audi.png'), 3 => array('id' => 3, 'name' => 'Bentley', 'url' => 'bentley', 'image' => 'bentley.png'), 4 => array('id' => 4, 'name' => 'BMW', 'url' => 'bmw', 'image' => 'bmw.png'), 5 => array('id' => 5, 'name' => 'Cadillac', 'url' => 'cadillac', 'image' => 'cadillac.png'), 55 => array('id' => 55, 'name' => 'Changan', 'url' => 'changan', 'image' => 'changan.png'), 6 => array('id' => 6, 'name' => 'Chery','url' => 'chery', 'image' => 'chery.png'), 7 => array('id' => 7, 'name' => 'Chevrolet', 'url' => 'chevrolet', 'image' => 'chevrolet.png'), 8 => array('id' => 8, 'name' => 'Chrysler', 'url' => 'chrysler', 'image' => 'chrysler.png'), 9 => array('id' => 9, 'name' => 'Citroen', 'url' => 'citroen', 'image' => 'citroen.png'), //10 => array('id' => 10, 'name' => 'Daewoo','url' => 'daewoo', 'image' => ''), 11 => array('id' => 11, 'name' => 'Dodge', 'url' => 'dodge', 'image' => 'dodge.png'), 56 => array('id' => 56, 'name' => 'Dongfeng', 'url' => 'Dongfeng', 'image' => 'dongfeng.png'), 54 => array('id' => 54, 'name' => 'Exeed', 'url' => 'exeed', 'image' => 'exeed.png'), 12 => array('id' => 12, 'name' => 'Ferrari', 'url' => 'ferrari', 'image' => 'ferrari.png'), 13 => array('id' => 13, 'name' => 'Fiat', 'url' => 'fiat', 'image' => 'fiat.png'), 53 => array('id' => 53, 'name' => 'Ford', 'url' => 'ford', 'image' => 'ford.png'), 57 => array('id' => 57, 'name' => 'GAC', 'url' => 'gac', 'image' => 'gac.png'), 14 => array('id' => 14, 'name' => 'Geely', 'url' => 'geely', 'image' => 'geely.png'), //15 => array('id' => 15, 'name' => 'GMC','url' => 'gmc', 'image' => ''), 16 => array('id' => 16, 'name' => 'Great Wall','url' => 'great-wall', 'image' => 'great-wall.png'), 17 => array('id' => 17, 'name' => 'Haval', 'url' => 'haval', 'image' => 'haval.png'), 18 => array('id' => 18, 'name' => 'Honda', 'url' => 'honda', 'image' => 'honda.png'), 19 => array('id' => 19, 'name' => 'Hummer', 'url' => 'hummer', 'image' => 'hummer.png'), 20 => array('id' => 20, 'name' => 'Hyundai', 'url' => 'hyundai', 'image' => 'hyundai.png'), 58 => array('id' => 58, 'name' => 'Haima', 'url' => 'haima', 'image' => 'haima.png'), 21 => array('id' => 21, 'name' => 'Infiniti', 'url' => 'infiniti', 'image' => 'infiniti.png'), 22 => array('id' => 22, 'name' => 'Jaguar', 'url' => 'jaguar', 'image' => 'jaguar.png'), 59 => array('id' => 59, 'name' => 'JAC', 'url' => 'jac', 'image' => 'jac.png'), 60 => array('id' => 60, 'name' => 'Jaecoo', 'url' => 'jaecoo', 'image' => 'jaecoo.png'), 61 => array('id' => 61, 'name' => 'Jetour', 'url' => 'jetour', 'image' => 'jetour.png'), 62 => array('id' => 62, 'name' => 'Jetta', 'url' => 'jetta', 'image' => 'jetta.png'), 23 => array('id' => 23, 'name' => 'Jeep', 'url' => 'jeep', 'image' => 'jeep.png'), 63 => array('id' => 63, 'name' => 'JMC', 'url' => 'jmc', 'image' => 'jmc.png'), 64 => array('id' => 64, 'name' => 'Kaiyi', 'url' => 'kaiyi', 'image' => 'kaiyi.png'), 24 => array('id' => 24, 'name' => 'Kia', 'url' => 'kia', 'image' => 'kia.png'), 25 => array('id' => 25, 'name' => 'Land Rover', 'url' => 'land-rover', 'image' => 'land-rover.png'), 26 => array('id' => 26, 'name' => 'Lexus', 'url' => 'lexus', 'image' => 'lexus.png'), 27 => array('id' => 27, 'name' => 'LiXiang','url' => 'lixiang', 'image' => 'LiXiang.png'), 65 => array('id' => 65, 'name' => 'Livan', 'url' => 'livan', 'image' => 'livan.png'), 28 => array('id' => 28, 'name' => 'Mazda', 'url' => 'mazda', 'image' => 'mazda.png'), 29 => array('id' => 29, 'name' => 'Mercedes-Benz', 'url' => 'mercedes-benz', 'image' => 'mercedes-benz.png'), 30 => array('id' => 30, 'name' => 'Mini', 'url' => 'mini', 'image' => 'mini.png'), 31 => array('id' => 31, 'name' => 'Mitsubishi', 'url' => 'mitsubishi', 'image' => 'mitsubishi.png'), 32 => array('id' => 32, 'name' => 'Nissan', 'url' => 'nissan', 'image' => 'nissan.png'), 66 => array('id' => 66, 'name' => 'OMODA', 'url' => 'omoda', 'image' => 'omoda.png'), 33 => array('id' => 33, 'name' => 'Opel', 'url' => 'opel', 'image' => 'opel.png'), 34 => array('id' => 34, 'name' => 'Peugeot', 'url' => 'peugeot', 'image' => 'peugeot.png'), 35 => array('id' => 35, 'name' => 'Porsche', 'url' => 'porsche', 'image' => 'porsche.png'), 36 => array('id' => 36, 'name' => 'Renault', 'url' => 'renault', 'image' => 'renault.png'), //37 => array('id' => 37, 'name' => 'Saab','url' => 'saab', 'image' => 'saab.png'), 38 => array('id' => 38, 'name' => 'Scania', 'url' => 'scania', 'image' => 'scania.png'), //39 => array('id' => 39, 'name' => 'Seat','url' => 'seat', 'image' => ''), 40 => array('id' => 40, 'name' => 'Skoda', 'url' => 'skoda', 'image' => 'skoda.png'), 41 => array('id' => 41, 'name' => 'Smart', 'url' => 'smart', 'image' => 'smart.png'), //42 => array('id' => 42, 'name' => 'SsangYong','url' => 'ssangyong', 'image' => 'ssangyong.png'), 43 => array('id' => 43, 'name' => 'Subaru', 'url' => 'subaru', 'image' => 'subaru.png'), 44 => array('id' => 44, 'name' => 'Suzuki', 'url' => 'suzuki', 'image' => 'suzuki.png'), 67 => array('id' => 67, 'name' => 'Tank', 'url' => 'tank', 'image' => 'tank.png'), 45 => array('id' => 45, 'name' => 'Tesla', 'url' => 'tesla', 'image' => 'tesla.png'), 46 => array('id' => 46, 'name' => 'Toyota', 'url' => 'toyota', 'image' => 'toyota.png'), 47 => array('id' => 47, 'name' => 'Volkswagen', 'url' => 'volkswagen', 'image' => 'volkswagen.png'), 48 => array('id' => 48, 'name' => 'Volvo', 'url' => 'volvo', 'image' => 'volvo.png'), 68 => array('id' => 68, 'name' => 'VOYAH', 'url' => 'voyah', 'image' => 'voyah.png'), 49 => array('id' => 49, 'name' => 'Zeekr','url' => 'zeekr', 'image' => 'Zeekr.png'), 50 => array('id' => 50, 'name' => 'ГАЗ', 'url' => 'gaz', 'image' => 'gaz.png'), 51 => array('id' => 51, 'name' => 'Лада (ВАЗ)', 'url' => 'lada-vaz', 'image' => 'lada-vaz.png'), 52 => array('id' => 52, 'name' => 'Мототехника', 'url' => 'mototehnika', 'image' => 'moto.png'), //53 => array('id' => 53, 'name' => 'УАЗ','url' => 'uaz', 'image' => ''), ); private $_root_url = 'tuning-centr/'; private $_tree; /** * @var array */ private $_services; public function count($filters) { $filter = $this->_prepare_filters($filters); $this->db->query("SELECT COUNT(DISTINCT p.id) as count FROM __pages p" . $filter->where); return (int)$this->db->result('count'); } public function all($filters) { $filter = $this->_prepare_filters($filters); $this->db->query( "SELECT *" . " FROM __pages p" . $filter->where . $filter->order . $filter->limit ); return $this->db->results(); } /** * Возвращает данные страницы услуг по её id или url * * @param string|int $id ID или url страницы данные которой необходимо получить * @return object */ public function get($id) { if (empty($id)) { //главная страница услуг $id = $this->root_id; $conditions = '1'; } else { $conditions = 'menu_id=' . $this->menu_id; } if (is_string($id)) { $conditions .= $this->db->placehold(' AND url=?', $id); } else { $conditions .= $this->db->placehold(' AND id=?', intval($id)); } $query = "SELECT * FROM __pages WHERE $conditions LIMIT 1"; $this->db->query($query); return $this->db->result(); } public function get_root_url() { return $this->_root_url; } /** * Возвращает краткие данные "брендовых" страниц услуги * @param int $id ID услуги * @param array $filter Фильтры получаемых данных, в виде колонка=>значение * @return array */ public function get_brands_pages($id, $filter = array()) { $this->_build_tree(); if (!isset($this->_services[$id])) return array(); $conditions = $this->db->placehold('parent=? AND brand_id>0', intval($id)); foreach ($filter as $col => $val) { $conditions .= $this->db->placehold(" AND $col=?", $val); } $this->db->query("SELECT `id`, `brand_id`, `url`, `parent`, `visible`, `name` FROM __pages WHERE $conditions ORDER BY `position`"); return $this->db->results(); } /** * Возвращает данные всех предков услуги. * Url каждой услуги включает корень. * @param int $id ID услуги * @return array */ public function get_path_to($id) { $this->_build_tree(); $results = array(); if(array_key_exists($id, $this->_services)) { $breadcrumbs = $this->_services[$id]->path; foreach ($breadcrumbs as $breadcrumb) { $results[] = (object)array( 'id' => $breadcrumb->id, 'parent' => $breadcrumb->parent, 'name' => $breadcrumb->name, 'url' => intval($breadcrumb->id) !== $this->root_id ? $this->_root_url . $breadcrumb->url : trim($this->_root_url, '/'), ); } } return $results; } public function get_all_brands() { $results = array(); foreach ($this->services_brands as $brand) { $results[] = (object)$brand; } return $results; } /** * Возвращает дерево всех страниц услуг * @return array */ public function get_tree() { $this->_build_tree(); return $this->_tree; } public function get_all_services() { $this->_build_tree(); return $this->_services; } /** * Возвращает услуги привязанные к главной странице. * Форматирование возвращаемых данных зависит от параметра $with_roots. * @param $visible_only * @param bool $with_roots Флаг определяющий будут ли услуги возвращены как дети их корневых услуг или просто списком * @return array */ public function get_home_services($visible_only = false, $with_roots = false) { $results = array(); $items = $this->services->all(array('show_home' => 1, 'visible' => 1)); foreach ($items as $item) { if ($visible_only && !$this->is_visible($item->id)) continue; if ($with_roots) { $root = $this->_services[$item->id]->path[1]; if (!isset($results[$root->id])) { $results[$root->id] = $this->get(intval($root->id)); $results[$root->id]->children = array(); } if(!array_key_exists($item->id, $results)) $results[$root->id]->children[] = $item; } else $results[] = $item; } return $results; } /** * Возвращает услуги главной страницы услуг (отмеченные как "в услугах"). * Форматирование возвращаемых данных зависит от параметра $with_roots. * @param $visible_only * @param bool $with_roots Флаг определяющий будут ли услуги возвращены как дети их корневых услуг или просто списком * @return array */ public function get_main_services($visible_only = false, $with_roots = false) { $results = array(); $items = $this->services->all(array('show_service' => 1, 'visible' => 1)); foreach ($items as $item) { if ($visible_only && !$this->is_visible($item->id)) continue; if ($with_roots) { $root = $this->_services[$item->id]->path[1]; if (!isset($results[$root->id])) { $results[$root->id] = $this->get(intval($root->id)); $results[$root->id]->children = array(); } if(!array_key_exists($item->id, $results)) $results[$root->id]->children[] = $item; } else $results[] = $item; } return $results; } public function get_brand($brand_id) { if (array_key_exists($brand_id, $this->services_brands)) return (object)$this->services_brands[$brand_id]; return false; } /** * Возвращает данные брендов привязанных к странице услуги * @param int $id ID услуги * @return array */ public function get_service_brands($id) { $conditions = $this->db->placehold('p.parent=? AND p.brand_id>0', intval($id)); $this->db->query("SELECT * FROM __pages p WHERE $conditions "); $results = array(); foreach ($this->db->results() as $page) { if (array_key_exists($page->brand_id, $this->services_brands)) { $brand = (object)$this->services_brands[$page->brand_id]; $brand->page_id = $page->id; $brand->page_url = $page->url; $brand->page_visible = $page->visible; $results[] = $brand; } } return $results; } /** * Создаёт новые или удаляет существующие "брендовые" страницы услуги. * К услуге будут привязаны только бренды ID которых перечислены в $brands. * Если массив $brands не содержит элементов, то будут удалены все "брендовые" страницы услуги. * @param int $id ID страницы услуги * @param array $brands ID брендов, для которых должны быть сгенерированы страницы. * @return void */ public function create_brands_pages($id, $brands) { $delete_conditions = $this->db->placehold('menu_id=? AND parent=? AND brand_id>0', $this->menu_id, intval($id)); if (is_array($brands) && !empty($brands)) { $ids = array(); foreach ($brands as $brand_id) { $ids[] = intval($brand_id); $this->create_brand_page($id, intval($brand_id)); } $delete_conditions .= $this->db->placehold(' AND brand_id NOT IN (?@)', $ids); } //Удаляем все брендовые страницы, которые не были перечислены в $brands $this->db->query("DELETE FROM __pages WHERE $delete_conditions"); } public function create_brand_page($id, $brand_id) { // страница услуги if (!($page = $this->services->get(intval($id)))) return; //проверяем существует ли "брендовая" страница услуги $conditions = $this->db->placehold('parent=? AND brand_id=?', intval($id), intval($brand_id)); if ($this->count(array('where' => $conditions)) === 0) {// страницы не существует if (array_key_exists($brand_id, $this->services_brands)) { $brand = (object)$this->services_brands[$brand_id]; $service = array( 'parent' => $id, 'brand_id' => $brand_id, 'menu_id' => $this->menu_id, 'url' => $page->url . '/' . $brand->url, 'name' => $brand->name, 'header' => $page->name . ' ' . $brand->name, 'meta_title' => $page->name . ' ' . $brand->name . ' в Санкт-Петербурге | Тюнинг центр' ); $this->pages->add_page($service); } } } public function update($id, $service) { $old_service = $this->get(intval($id)); $query = $this->db->placehold("UPDATE __pages SET ?% WHERE id=? LIMIT 1", $service, intval($id)); $this->db->query($query); $service = (object)$service; //был изменён родитель или позиция if((isset($service->parent) && intval($old_service->parent) !== intval($service->parent)) || (isset($service->position)) && intval($old_service->position) !== intval($service->position)) $this->fix_positions(); //была изменен статус видимости if(isset($service->visible) && intval($old_service->visible) !== intval($service->visible)) $this->set_visible($id, $service->visible); return $id; } /** * Удаляет все услуги с указанными id, а также всех их потомков * @param $ids * @param bool $recursive Разрешено ли удалять узлы имеющие детей (по умолчанию - запрещено) * @return void */ public function delete($ids, $recursive = false) { $ids = (array)$ids; foreach ($ids as $id) { $service = $this->get(intval($id)); if (!empty($service)) { // получаем ID детей $this->db->query('SELECT id FROM __pages WHERE parent=' . $service->id); $children = $this->db->results('id'); $has_children = count($children) > 0; // если есть дети и удаление не рекурсивное - пропускаем узел if (!$recursive && $has_children) continue; if ($has_children) { // рекурсивно удаляем потомков $this->delete($children, $recursive); } else { // узел без детей - удаляем $this->_delete_internal(intval($id)); $this->_unset_tree(); } } } } public function move($id, $options) { $this->_build_tree(); $id = intval($id); $new_parent = intval($options['parent']); $relative_position = intval($options['position']); if (isset($id, $this->_services) && isset($id, $this->_services[$new_parent])) { $service = $this->_services[$id]; // новый родитель $parent = $this->_services[$new_parent]; // нод место которого должен занять перемещаемый if (isset($parent->children[$relative_position])) { $target_node = $parent->children[$relative_position]; $insert_position = intval($target_node->position); } else { // добавление в самую нижнюю позицию $target_node = null; $insert_position = $parent->position + (isset($parent->descendants) ? count($parent->descendants) : 1); } if (intval($service->parent) !== $new_parent) { // у нода будет другой родитель $conditions = $this->db->placehold('parent=?, position=? WHERE id=?', $new_parent, $insert_position, intval($service->id)); $this->db->query('UPDATE __pages SET ' . $conditions); if ($target_node) $this->db->query('UPDATE __pages SET position=' . $insert_position + 1 . ' WHERE id=' . intval($target_node->id)); } else { // родитель не меняется $i = 0; foreach ($parent->children as $node) { //$this->db->query('UPDATE __pages SET position=position+? WHERE id=?'); if ($node->id !== $service->id) { $i = ($i === $relative_position) ? $relative_position + 1 : $i; $this->db->query('UPDATE __pages SET position=? WHERE id=?', $i, intval($node->id)); ++$i; } else { $this->db->query('UPDATE __pages SET position=? WHERE id=?', $relative_position, intval($node->id)); } } } $this->fix_positions(); $this->_unset_tree(); return true; } return false; } /** * Возвращает реальный статус видимости услуги, в зависимости от статуса её родителей * @param $id * @return bool */ public function is_visible($id) { $this->_build_tree(); if (!array_key_exists($id, $this->_services) || !$this->_services[$id]->visible) return false; foreach ($this->_services[intval($id)]->path as $serv) { if (!$serv->visible) return false; } return true; } /** * Есть ли дети у указанной услуги * @param int $id * @return bool */ public function has_children($id) { $this->_build_tree(); return array_key_exists($id, $this->_services) && !empty($this->_services[$id]->children); } /** * Включает и отключает услугу * @param $id * @param $status * @return void */ public function set_visible($id, $status) { $this->_build_tree(); $visible = intval(boolval($status)); $service = $this->get(intval($id)); if($service) { $query = 'UPDATE __pages SET visible=' . $visible . ' WHERE id='; //если услуга не брендовая, включаем/отключаем всех её родителей/потомков if(!$service->brand_id) { $target_id = intval($service->id); if ($visible) { //включение услуги foreach ($this->_services[$target_id]->path as $serv) $this->db->query($query . $serv->id); } else { //отключение услуги foreach ($this->_services[$target_id]->descendants as $s_id) $this->db->query($query . $s_id); } } else { //брендовая услуга $target_id = intval($service->parent); if ($visible) { //включение предков родителя foreach ($this->_services[$target_id]->path as $serv) $this->db->query($query . $serv->id); //включение самого родителя $this->db->query($query . $target_id); } } // обновляем статус самой услуги $this->db->query( $query . $service->id); } $this->_unset_tree(); } public function fix_positions($tree = null, &$pos = 0, $level=1) { if ($tree === null) { $this->_unset_tree(); $tree = $this->_build_tree(); $pos = 0; $level = 1; } foreach ($tree as $node) { $this->db->query('UPDATE __pages SET level='. $level .', position=' . ++$pos . ' WHERE id=' . intval($node->id)); if ($node->children) { $this->fix_positions($node->children, $pos, $level+1); } } } private function _delete_internal($id) { //удаляем связанные объекты $this->db->query('DELETE FROM __pages_objects WHERE page_id=' . $id); //удаляем саму страницу $this->db->query('DELETE FROM __pages WHERE id=' . $id); } private function _build_tree() { if (!is_null($this->_tree)) return $this->_tree; $this->db->query( "SELECT p.id, p.parent, p.brand_id, p.name, p.url, p.visible, p.position" . " FROM __pages p" . " WHERE (menu_id=" . $this->menu_id . " OR p.id=" . $this->root_id . ") AND p.brand_id=0" . " ORDER BY p.parent, p.position" ); $services = $this->db->results(); if (empty($services)) { return array(); } // Дерево категорий $tree = $services[0]; unset($services[0]); $tree->children = array(); $pointers = array(); $pointers[$this->root_id] = &$tree; $pointers[$this->root_id]->path = array($tree); $finish = false; // строим дерево while (!empty($services) && !$finish) { $flag = false; foreach ($services as $k => $service) { if (isset($pointers[$service->parent])) { $pointers[$service->parent]->children[] = ($pointers[$service->id] = $service); $curr = $pointers[$service->id]; $pointers[$service->id]->path = array_merge((array)$pointers[$service->parent]->path, array($curr)); unset($services[$k]); $flag = true; } } if (!$flag) $finish = true; } // добавляем ID всех потомков к нодам $ids = array_reverse(array_keys($pointers)); foreach ($ids as $id) { if ($id != $this->root_id) { $pointers[$id]->descendants[] = $id; if (isset($pointers[$pointers[$id]->parent]->descendants)) $pointers[$pointers[$id]->parent]->descendants = array_merge($pointers[$id]->descendants, $pointers[$pointers[$id]->parent]->descendants); else $pointers[$pointers[$id]->parent]->descendants = $pointers[$id]->descendants; } } unset($ids); // получаем полный url к каждой странице попутно подчищая лишние ID и устанавливая реальный статус видимости элемента foreach ($pointers as $service) { if (!isset($service->children)) unset($service->descendants); /* $current_url = $service->url; foreach ($service->path as $serv) { if (substr($serv->url, -1) !== '/') { $current_url .= $serv->url . '/'; } else { $current_url = $serv->url; } } $service->url = $current_url;*/ } $this->_tree = $tree->children; $this->_services = $pointers; return $this->_tree; } private function _unset_tree() { unset($this->_tree); unset($this->_services); } private function _prepare_filters($filter) { $filters = new stdClass(); $where = 'menu_id=' . $this->menu_id; if (array_key_exists('where', $filter)) { $where = $filter['where']; } else { if (array_key_exists('ids', $filter)) { if (is_array($filter['ids']) && !empty($filter['ids'])) $where .= $this->db->placehold(' AND p.id IN (?@)', $filter['ids']); else $where .= $this->db->placehold(' AND p.id IN (NULL)'); } if (array_key_exists('visible', $filter)) {// активные/архивные контракты $where .= $this->db->placehold(' AND p.visible=?', intval($filter['visible'])); } if (array_key_exists('branded', $filter)) { $where .= ' AND p.brand_id>0'; } if (array_key_exists('show_service', $filter)) { $where .= ' AND p.show_service=' . intval($filter['show_service']); } if (array_key_exists('show_home', $filter)) { $where .= ' AND p.show_home=' . intval($filter['show_home']); } if (array_key_exists('parent', $filter)) { $where .= $this->db->placehold(' AND p.parent=?', intval($filter['parent'])); } if (array_key_exists('brand_id', $filter)) { $where .= $this->db->placehold(' AND p.brand_id=?', intval($filter['brand_id'])); } // поисковый запрос if (isset($filter['keyword'])) { $keywords = explode(' ', $filter['keyword']); foreach ($keywords as $keyword) { $escaped_keyword = $this->db->escape(trim($keyword)); if (!empty($escaped_keyword)) { $where .= ' AND (p.name LIKE "%' . $escaped_keyword . '%" OR p.header LIKE "%' . $escaped_keyword . '%")'; } } } } $filters->where = ' WHERE ' . $where; if (isset($filter['order']) && is_string($filter['order'])) { $order = $filter['order']; } else{ // сортировка по-умолчанию $order = 'level, position'; } $filters->order = !empty($order) ? ' ORDER BY ' . $order : ''; // навигация $filters->limit = ''; if (isset($filter['limit'])) { $limit = max(1, intval($filter['limit'])); if (isset($filter['page'])) { $page = max(1, intval($filter['page'])); $limit = $this->db->placehold('?, ? ', ($page - 1) * $limit, $limit); } $filters->limit = ' LIMIT ' . $limit; } return $filters; } }