我正在编写一个小型的学习CMS项目,而且我遇到了阻碍我完成下一步的砖墙。我知道我应该考虑KISS(保持简单,愚蠢),但我认为能够对层次结构页面进行分组会很好。
问题是我希望只能通过此网址[root]->fruits->tropical->bananas
访问网页http://localhost/cms/fruits/tropical/bananas/
。到目前为止我想出的是cms表有一个指向其父级的父字段。问题是:如何解析uri地址并从DB中选择一行尽可能少的查询/
Table structure:
Id
Slug
...
...
...
ParentId
欢迎所有帮助和建议。
答案 0 :(得分:4)
这是我用来测试它的表结构 -
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)上的唯一键。这是从以下查询中获得最佳性能的关键。我用50k行测试了它,并且它仍然在不到1ms的时间内返回了5个slug路径 - /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);
答案 1 :(得分:3)
如果页面只存在于一个网址上,解决问题的最简单方法是将完整网址的哈希值存储在自己的索引字段中:
SELECT * FROM table WHERE page = MD5('http://localhost/cms/fruits/tropical/bananas/')
虽然如果您要沿着分层路线走下去,您可能会发现以下内容: http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/
答案 2 :(得分:1)
我特别喜欢Bill Karwin(https://stackoverflow.com/users/20860/bill-karwin)关于等级制度的演讲:
http://en.slideshare.net/billkarwin/models-for-hierarchical-data
Recursively grab all data based on a parent id
您应该使用闭包表查看他的解决方案。我发现它特别有用。这可能就是你所需要的。
答案 3 :(得分:0)
您已将此问题标记为CodeIgniter,因此这是一个特定的答案。
您可以使用它的routeing功能来强制使用URL,但可以按要求的方式处理请求。
你会得到的是:
$route['cms/fruit/(:any)'] = 'fruit/$1';
$route['cms/fruit/(:any)/(:any)'] = 'fruit/$1/$2';
第一行会将所有以cms / fruit开头的URL转发到水果控制器,并将水果类型作为第一个变量传递(也可以将水果名称作为第二个变量)。第二行是在没有处理水果名称的情况下退回。
将此与配置中的基本路径相结合,您也可以在URL中自动设置“cms”,如果它应该始终位于URL中。
答案 4 :(得分:0)
您是否正在使用codeigniter进行此开发?以下答案基于 来自codeigniter的Web应用程序框架。所以这里,
问题是我想要页面
[root]->fruits->tropical->bananas
只能从此网址访问: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表有一个父字段 这指向其父母。问题是:如何解析uri地址 并使用尽可能少的查询/ DB从DB中选择一行?
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();
}