2012-03-07 15 views
1

私は少し学習CMSプロジェクトをコーディングしています。私は次のステップを完了するのを止めているレンガの壁に当たっています。私はKISS(Keep It Simple、Stupid)を考慮する必要があることを知っていますが、階層的にページをグループ化することができればいいと思います。階層型CMSサイトを実装する方法は?

問題は、[root]->fruits->tropical->bananasページにこのURL(http://localhost/cms/fruits/tropical/bananas/)からのみアクセスできるようにすることです。今まで私が思いついたのは、cmsテーブルに親を指していることです。質問は、どのようにuriアドレスを解析し、できるだけ少ないクエリで効率的にDBから行を選択するのですか?

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

すべての助けと助言は親切に受け入れられます。

+4

http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/ –

+1

http://stackoverflow.com/questions/4048151/what-are-the-options-for-storing-hierarchical-データベース内のデータ – frail

答えて

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行でこれをテストし、それはまだ5つのスラグパスのために1ms以下で返さ -

<?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); 
+0

これは私の方法ほど高速ではありません。しかし、INNER JOINとは何かをもう少し説明できますか?私は通常、LEFT JOINを使用して隣接リストモデルを再帰的にクエリします。 – Bytemain

+0

これは実際にはパス内のページにのみアクセスするため、メソッドはテーブル内のすべてのページにアクセスするのに対し、メソッドよりもかなり高速です。事実、あなたの方法は、私が彼の質問を完全に誤解していない限り、OPが求めていることはしません。 [INNER JOINが何であるかわからない場合は、このページをお読みください(http://dev.mysql.com/doc/refman/5.0/ja/join.html)。 – nnichols

+0

私は感覚のないサイトへのポインタを得るためにあなたをアップアップしませんでした。あなた自身でINNER JOINを記述することはできますか、あるいは正式な回答が必要ですか?私は簡単にゴーグルできますか?また、INNER JOINを使用します。ベンチマークがありますか? – Bytemain

0

あなたはこの質問をCodeIgniterとタグ付けしました。これは、これに固有の答えです。

URLは強制的に要求された方法で処理しますが、それはrouteingの機能を使用することができます。最初の行は果物コントローラにCMS /フルーツで始まるすべてのURLを転送し、最初の変数として果物の種類を通過う

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

(多分果物:あなたが持っているだろうか

のようなものです2番目の変数としての名前も)。 2行目は、フルーツ名の世話をしなかった場合のフォールバックです。

これをconfigの基本パスと組み合わせると、URLに常にあるべきであれば、自動的に 'cms'をURLに設定することもできます。

0

この開発にcodeigniterを使用していますか?以下の回答は、codeigniterのWebアプリケーションフレームワーク に基づいています。 http://localhost/cms/fruits/tropical/bananas/:だからここに行く、


問題は、私は へページ[root]->fruits->tropical->bananasのみ、このURLからアクセス可能で欲しいということです。

したがって、機能名フルーツと2つのパラメータを持つコントローラでコントローラを作成しますか?例えば

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テーブルは、その親を指す親フィールド を持っているということです。質問は:どのようにurlアドレス を解析し、可能な限り少ないクエリ/効率でDBから行を選択するのですか?

Table cms: 
Id 
Slug 
ParentId 

Table cms_parent: 
Id 

のは、上に示した2例の表で説明してみましょう、CMSテーブルとCMS親テーブル。あなたは、あなたの質問が何を望んでいるのか、または質問結果が返ってくるのかについてあなたの質問に正確には言及していません。だから私の推測はあなたの質問の記述に基づいています。つまり、共通のキーを使って2つのテーブルを結合してから条件を適用します。

// 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

あなたはそれを完全に取得しません... DBに格納されているすべてのページはnページ以上の親にもなり、nレベルの親を持っています – JanL

+0

私がそれは完全に、**あなたが参照している部分ですか?あなたは具体的になりますか?私があなたのコメントから理解しているところから、私はデータベースのページ(Nレベルのページ)をモデル化し、データベースからそのページを取り出す方法に関する部分が理解できないと思いますか? – Jasonw

3

ページが唯一の問題を解決する最も簡単な方法は、それ自身のインデックスフィールドでの完全なURLのハッシュを保存することであろう1つのURLに存在する場合:ものの

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

を階層ルートを下ろす場合は、次のような便利な点があります。 http://mikehillyer.com/articles/managing-hierarchical-data-in-mysql/

+0

これらの人たちがどのように議論しているのか、あなたの3つの文の答えが最高です。ハッシングは速くてユニークです。ページが動いたらそれを更新するだけです。これらの他のすべての答えはコミックです。 – bkconrad

+2

@bkconrad - このハッシュソリューションは高速です(このコンテキストでは一意ではないが十分かもしれない)が、パス内のすべてのノードを返すことはできません。また、階層内を移動するときにブランチ内のすべてのアイテムを再ハッシュする必要があるという問題もあります。また、冗長データを格納する必要があります。 – nnichols

1

I - ここ

/cms/slug-1/slug-2/slug-3/slug-4/slug-5/は、私は、適切なクエリを構築することが出ているPHPコードです

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

Recursively grab all data based on a parent id

:特に階層のビルKarwinさん( https://stackoverflow.com/users/20860/bill-karwin)プレゼンテーションなどの

彼の解決策をクロージャーテーブルで見てください。私はそれが特に有用であると感じました。それはあなたが必要とするものかもしれません。

関連する問題