Как реализовать иерархические CMS сайты? - PullRequest
1 голос
/ 08 марта 2012

Я пишу небольшой учебный проект CMS и натолкнулся на кирпичную стену, которая мешает мне выполнить следующий шаг.Я знаю, что должен учитывать KISS (Keep It Simple, Stupid), но я думаю, что было бы неплохо иметь возможность группировать страницы иерархически.

Проблема в том, что я хочу, чтобы страница [root]->fruits->tropical->bananas быладоступно только с этого URL: http://localhost/cms/fruits/tropical/bananas/.До сих пор я придумал, что таблица cms имеет родительское поле, которое указывает на его родителя.Вопрос в том, как разобрать адрес и выбрать строку в БД с минимальным количеством запросов / максимально эффективно?

Table structure:
Id
Slug
...
...
...
ParentId

Любая помощь и совет любезно принимаются.

Ответы [ 5 ]

4 голосов
/ 16 марта 2012

Вот структура таблицы, которую я использовал для проверки этого -

CREATE TABLE  `test`.`pages` (
    `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
    `slug` varchar(45) NOT NULL,
    `title` varchar(45) NOT NULL,
    `content` text NOT NULL,
    `parent_id` int(10) unsigned DEFAULT NULL,
    PRIMARY KEY (`id`),
    UNIQUE KEY `UQ_page_parent_id_slug` (`parent_id`,`slug`),
    CONSTRAINT `FK_page_parent_id` FOREIGN KEY (`parent_id`) REFERENCES `pages` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Обратите внимание на уникальный ключ на (parent_id, slug). Это ключ к получению максимальной производительности из следующего запроса. Я проверил это с 50 тыс. Строк, и он все равно вернулся менее чем за 1 мс для пути с пятью слагами - /cms/slug-1/slug-2/slug-3/slug-4/slug-5/

Вот код PHP, который я придумал для создания подходящего запроса -

<?php

// I will assume the rest of the url has already been stripped away
$url = '/fruits/tropical/bananas/';

// lets just make sure we don't have any leading or trailing /
$url = trim($url, '/');

// now let's split the remaining string based on the /
$aUrl = explode('/', $url);

/**
* Now let's build the query to retrieve this
*/

// this array stores the values to be bound to the query at the end
$aParams = array();

$field_list = 'SELECT p1.* ';
$tables = 'FROM pages p1 ';
$where = "WHERE p1.parent_id IS NULL AND p1.slug = ? ";

// this array stores the values to be bound to the query at the end
$aParams[] = $aUrl[0];

// if we have more than one element in our array we need to add to the query
$count = count($aUrl);

for ($i = 1; $i < $count; $i++) {

    // add another table to our query
    $table_alias = 'p' . ($i + 1);
    $prev_table_alias = 'p' . $i;
    $tables .= "INNER JOIN pages $table_alias ON {$prev_table_alias}.id = {$table_alias}.parent_id ";

    // add to where clause
    $where .= "AND {$table_alias}.slug = ? ";
    $aParams[] = $aUrl[$i];

    // overwrite the content of $field_list each time so we
    // only retrieve the data for the actual page requested
    $field_list = "SELECT {$table_alias}.* ";

}

$sql = $field_list . $tables . $where;

$result = $this->db->query($sql, $aParams);
3 голосов
/ 15 марта 2012

Если страница существует только по одному URL-адресу, самый простой способ решить эту проблему - сохранить хэш полного URL-адреса в своем проиндексированном поле:

SELECT * FROM table WHERE page = MD5('http://localhost/cms/fruits/tropical/bananas/')

Хотя, если вы собираетесь пойти по иерархическому маршруту, вам может пригодиться следующее: http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/

1 голос
/ 28 февраля 2014

Мне особенно нравится представление Билла Карвина (https://stackoverflow.com/users/20860/bill-karwin) об иерархиях:

http://en.slideshare.net/billkarwin/models-for-hierarchical-data

Рекурсивно получить все данные на основе родительского идентификатора

Вы должны взглянуть на его решение с помощью таблицы закрытия. Я нашел это особенно полезным. Это может быть то, что вам нужно.

0 голосов
/ 14 марта 2012

Вы используете codeigniter для этой разработки? Этот ответ ниже основан на фреймворк веб-приложения от codeigniter. Итак,


Проблема в том, что я хочу, чтобы страница [root]->fruits->tropical->bananas быть доступным только из этого URL: http://localhost/cms/fruits/tropical/bananas/.

Итак, создать функцию в вашем контроллере с именем функции fruits и с двумя параметрами? Например

class Cms extends CI_Controller {
  ...
  ...
  ...
  public function __construct() {
     $this->load->model('cms_model');
  }

  public function fruits($tropical, $bananas) {

     $string = $this->cms_model->getPage($tropical, $bananas);

     // load the view you want.
     $this->load->view('');
  }
  ...
  ...
  ...

}

До сих пор я придумал, что таблица cms имеет родительское поле это указывает на его родителя. Вопрос в том, как разобрать адрес и выбрать строку из БД с минимальным количеством запросов / максимально эффективно?

Table cms:
Id
Slug
ParentId

Table cms_parent:
Id

Давайте опишем на примере двух приведенных выше таблиц: таблицу cms и родительскую таблицу cms. Вы не указали точно в своем вопросе о том, что хочет ваш запрос или результат запроса. Ниже приведено мое предположение, основанное на описании вашего вопроса: запросите две таблицы, соединив их с помощью общего ключа, а затем примените условие где.

// select * from cms t1 join cms_parent t2 on t1.ParentId = t2.Id where t1.Id = '' and t2.ParentId = 'level1';
public function getPage($level0, $level1) {
    $this->db->select('*');
    $this->db->from('cms');
    $this->db->join('cms_parent', 'cms.ParentId = cms_parent.Id');
    $this->db->where('cms.Id', $level0);
    $this->db->where('cms.ParentId', $level1);

    $query = $this->db->get();

    // return one row from database.
    return $query->row();
}
0 голосов
/ 13 марта 2012

Вы пометили этот вопрос как CodeIgniter, так что это ответ, специфичный для этого.

Вы можете использовать возможности маршрутизации для форсирования URL, но обрабатывать запрос требуемым способом.

То, что вы бы получили, это что-то вроде:

$route['cms/fruit/(:any)'] = 'fruit/$1';
$route['cms/fruit/(:any)/(:any)'] = 'fruit/$1/$2';

В первой строке все URL-адреса, начинающиеся с cms / fruit, будут перенаправлены на контроллер фруктов и переданы через тип фруктов в качестве первой переменной (может быть, имя фрукта также как вторая переменная). Вторая строка - это отступление на случай, если название плода не позаботилось.

Объедините это с базовым путем в конфигурации, и вы также можете автоматически установить 'cms' в URL, если это то, что всегда должно быть в URL.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...