无限分类与树型论坛的实现方法_PHP
无限分类与树型论坛的实现方法
――浮点型字段排序法
Joe Teng 2005.6.12
在此我不想讨论其他实现方法的利与弊。
既然是使用字段排序,那么我们便设一个名为order的字段。问题是,在这里是使用整数还是使用浮点数类型呢?考虑到会有在两个连续order值中间插入新值的可能,自然是需要使用浮点类型了。
建一个menus表,我们还需要以下字段:
id : 类别编号
mainid : 主分类编号,但不作具体分类使用。如果在树型论坛里,它代表的是主题id
parentid : 父类编号
level : 类别级别,作用其实是方便显示的时候作其他处理
info : 类别名称等。
由此可以得到menus的表结构:
CREATE TABLE `menus` (
`id` INT( 10 ) UNSIGNED NOT NULL AUTO_INCREMENT ,
`mainid` INT( 10 ) UNSIGNED NOT NULL ,
`parentid` INT( 10 ) UNSIGNED NOT NULL ,
`order` FLOAT UNSIGNED NOT NULL ,
`level` SMALLINT( 5 ) UNSIGNED NOT NULL ,
`info` VARCHAR( 128 ) NOT NULL ,
INDEX ( `mainid` , `parentid` , `order` , `level` ) ,
UNIQUE (
`id`
)
) TYPE = MYISAM ;
很容易可以看出,输入的时候是如此简单便实现树结构了:
SELECT * FROM `menus` ORDER BY `mainid` ASC, `order` ASC ;
前提是添加类别的时候,order能正确排序。
添加根分类:
很简单,取得上一个主类的mainid, 如A_mainid,则新根分类的mainid则为A_mainid + 1。parentid 为 0 , order 为0, level也为0, info则自行设定。
添加子分类:
核心思想是,取得新增子分类的前一个分类的order以及它后一个分类的order。
取得前一个分类的order是这里的难点,因为涉及到同级与非同级的情况。非同级的情况很简单,新增别类的前一个order其实就是它的父类的order。如果有同级分类,情况就很复杂了,因为它前面的同级分类有可能会拥有子分类,子分类下又可能还会有子分类,如此下来,要取得前一个order就很难了。
解决的办法有两个:
1.取得新增类同级的前一个类别,如类别A的ID,使用递归的方法,直到取得A类别下最后最小分类的order,那便是要新增分类的前一个order了。这种方法的缺点是,如果A类别下有很多子分类,那么递归需要一定的时间。这种方法适用于普通的分类处理,不适用于树型论坛。不过总体来说,因为是添加类别的时候才使用递归,输出类别的时候跟前面一样,效率还是很高的。
2.作一个记录,记录着与A有关联的最后order。于是我们就需要增加一个表,建利关系树。这种关系树做起来很简单。表结构如下:
CREATE TABLE `menu_tree` (
`mainid` int(10) unsigned NOT NULL default '0',
`tree` text NOT NULL,
`order` float unsigned NOT NULL default '0',
KEY `mainid` (`mainid`,`order`),
FULLTEXT KEY `tree` (`tree`)
) TYPE=MyISAM;
(构建方式请看我后面给出的源码)
取得前一个order之后,要取得后一个order就很简单了。取同mainid下大于前一个order的最小order便是了。如果存在后一个order,那么新增order就取前一个order与后一个order的平均值。如果不存在后一个order,那说明新增类别是main下的最小order,取大于前一个order的最小整数就行了。
主要实现方法便如上面说的。
处理方法
/*
* ID: class FreeRoad
* Author: Joe Teng
* Notice: Infinite category maker.
*/
(
"host" => 'localhost', "user" => 'root', "password" => '123456', "dbname" => 'test'
);
$resDbc = mysql_connect ( $arrDatabase["host"], $arrDatabase["user"], $arrDatabase["password"] );
mysql_select_db( $arrDatabase['dbname'] );
if ( ! class_exists ( "FreeRoad" ))
{
class FreeRoad
{
var $resDbc ;
var $strDatabase ;
var $strMenuTable ;
var $strMenuTreeTable ;
var $strFiled_id = 'id' ;
var $strFiled_mainid = 'mainid' ;
var $strFiled_parentid = 'parentid' ;
var $strFiled_order = 'order' ;
var $strFiled_level = 'level' ;
function FreeRoad ( $resDbc , $strDatabase , $strMenuTable , $strMenuTreeTable , $arrSetFileds = array() )
{
$this->resDbc = $resDbc ;
$this->strDatabase = $strDatabase;
$this->strMenuTable = $strMenuTable ;
$this->strMenuTreeTable = $strMenuTreeTable ;
if ( sizeof ( $arrSetFileds ) > 0 )
{
$this->strFiled_id = $arrSetFileds['id'] ;
$this->strFiled_mainid = $arrSetFileds['mainid'] ;
$this->strFiled_parentid = $arrSetFileds['parentid'] ;
$this->strFiled_order = $arrSetFileds['order'] ;
$this->strFiled_level = $arrSetFileds['level'] ;
}
}
function get_new_mainid ()
{
mysql_select_db ( $this->strDatabase , $this->resDbc ) ;
$strQuery = " SELECT `$this->strFiled_mainid`
FROM `$this->strMenuTable`
WHERE `$this->strFiled_parentid` = 0
ORDER BY `$this->strFiled_id` DESC LIMIT 0 , 1 " ;
$resResult = mysql_query ( &$strQuery , $this->resDbc ) ;
while ( $arrRow = mysql_fetch_array ( $resResult ) )
{
$intLastedMainId = $arrRow[0] ;
}
$intLastedMainId = intval ( $intLastedMainId );
mysql_free_result ( $resResult ) ;
return $intLastedMainId + 1 ;
}
function get_level_lastest_id ( $intParentId )
{
mysql_select_db ( $this->strDatabase , $this->resDbc ) ;
$strQuery = " SELECT `$this->strFiled_id`
FROM `$this->strMenuTable`
WHERE `$this->strFiled_parentid` = $intParentId
ORDER BY `$this->strFiled_id` DESC LIMIT 0 , 1 " ;
$resResult = mysql_query ( &$strQuery , $this->resDbc ) ;
while ( $arrRow = mysql_fetch_row ( $resResult ) )
{
$intLevelLastestId = $arrRow[0] ;
}
mysql_free_result ( $resResult ) ;
return $intLevelLastestId ;
}
function get_level_lastest_order ( $intParentId )
{
mysql_select_db ( $this->strDatabase , $this->resDbc ) ;
$strQuery = " SELECT `$this->strFiled_order`
FROM `$this->strMenuTable`
WHERE `$this->strFiled_id` = $intParentId " ;
$resResult = mysql_query ( &$strQuery , $this->resDbc ) ;
while ( $arrRow = mysql_fetch_row ( $resResult ) )
{
$floSelectItemOrder = $arrRow[0] ;
}
mysql_free_result ( $resResult ) ;
$strQuery = " SELECT `$this->strFiled_order`
FROM `$this->strMenuTreeTable`
WHERE BINARY ( `tree`) LIKE '%|$intParentId|%'
ORDER BY `$this->strFiled_order` DESC LIMIT 0 , 1 " ;
//echo $strQuery ;
$resResult = mysql_query ( &$strQuery , $this->resDbc ) ;
while ( $arrRow = mysql_fetch_row ( $resResult ) )
{
$floSelectItemLastestOrder = $arrRow[0] ;
}
mysql_free_result ( $resResult ) ;
if ( ! $floSelectItemLastestOrder ) $floSelectItemLastestOrder = $floSelectItemOrder ;
return $floSelectItemLastestOrder ;
}
function get_elements ( $intParentId = 0 )
{
mysql_select_db ( $this->strDatabase , $this->resDbc ) ;
if ( $intParentId == 0 )
{
$intMainId = $this->get_new_mainid ( );
return array ( "mainid" => $intMainId , "order" => 0 , "level" => 0 ) ;
}
$strQuery = " SELECT `$this->strFiled_mainid` , `$this->strFiled_order` , `$this->strFiled_level`
FROM `$this->strMenuTable`
WHERE `$this->strFiled_id` = $intParentId " ;
$resResult = mysql_query ( &$strQuery , $this->resDbc ) ;
while ( $arrRow = mysql_fetch_row ( $resResult ) )
{
$intMainId = $arrRow[0] ;
$floOrder = $arrRow[1] ;
$intLevel = $arrRow[2] ;
}
mysql_free_result ( $resResult ) ;
if ( ! $intMainId ) return false ;
$intLevelLastestId = $this->get_level_lastest_id ( $intParentId ) ;
// get pre order
if ( $intLevelLastestId )
{
$floPreOrder = $this->get_level_lastest_order ( $intLevelLastestId );
// echo $floPreOrder ;exit;
}
else
{
$floPreOrder = $floOrder ;
}
// get next order
$strQuery = " SELECT `$this->strFiled_order`
FROM `$this->strMenuTable`
WHERE `$this->strFiled_mainid` = $intMainId AND `$this->strFiled_order` > $floPreOrder
ORDER BY `$this->strFiled_order` ASC LIMIT 0 , 1 " ;
$resResult = mysql_query ( &$strQuery , $this->resDbc ) ;
while ( $arrRow = mysql_fetch_row ( $resResult ) )
{
$floNextOrder = $arrRow[0] ;
}
mysql_free_result ( $resResult ) ;
if ( ! $floNextOrder )
{
$floNewOrder = floor ( $floPreOrder + 1 ) ;
}
else
{
$floNewOrder = number_format ( ( $floPreOrder + $floNextOrder ) / 2 , 14 ) ;
}
$intNewLevel = $intLevel + 1 ;
return array ( "mainid" => $intMainId , "order" => $floNewOrder , "level" => $intNewLevel ) ;
}
function update_tree ( $intMainId , $intParentId , $floOrder )
{
if ( !$intParentId ) return false ;
mysql_select_db ( $this->strDatabase , $this->resDbc ) ;
$strQuery = " SELECT `tree`
FROM `$this->strMenuTreeTable`
WHERE `mainid` = $intMainId AND BINARY ( `tree`) LIKE '%|$intParentId|'
ORDER BY `order` DESC LIMIT 0 , 1 " ;
$resResult = mysql_query ( &$strQuery , $this->resDbc ) ;
while ( $arrRow = mysql_fetch_row ( $resResult ) )
{
$strTree = $arrRow[0] ;
}
mysql_free_result ( $resResult ) ;
if ( ! $strTree )
{
$strQuery = " SELECT `$this->strFiled_parentid`
FROM `$this->strMenuTable`
WHERE `$this->strFiled_id` = $intParentId " ;
$resResult = mysql_query ( &$strQuery , $this->resDbc ) ;
while ( $arrRow = mysql_fetch_row ( $resResult ) )
{
$intPreParentId = $arrRow[0] ;
}
mysql_free_result ( $resResult ) ;
if ( ! $intPreParentId )
{
$strPreTree = '';
}
else
{
$strQuery = " SELECT `tree`
FROM `$this->strMenuTreeTable`
WHERE `mainid` = $intMainId AND BINARY ( `tree`) LIKE '%|$intPreParentId|'
ORDER BY `order` DESC LIMIT 0 , 1 " ;
$resResult = mysql_query ( &$strQuery , $this->resDbc ) ;
while ( $arrRow = mysql_fetch_row ( $resResult ) )
{
$strPreTree = $arrRow[0] ;
}
mysql_free_result ( $resResult ) ;
}
$strNewTree = $strPreTree . '|'. $intParentId . '|' ;
$strQuery = " INSERT INTO `$this->strMenuTreeTable`
VALUES ( $intMainId, '$strNewTree', $floOrder ) " ;
$resResult = mysql_query ( &$strQuery , $this->resDbc ) ;
@mysql_free_result ( $resResult ) ;
return true ;
}
else
{
$strQuery = " UPDATE `$this->strMenuTreeTable`
SET `order` = $floOrder
WHERE `mainid` = $intMainId AND `tree` = '$strTree' " ;
$resResult = mysql_query ( &$strQuery , $this->resDbc ) ;
@mysql_free_result ( $resResult ) ;
return true ;
}
}
}
}
/*
$pFreeRoad = new FreeRoad ( $resDbc , $arrDatabase["dbname"] , 'menus' , 'menu_tree' ) ;
$info = 'change here';
$intParentId = change here ;
$arrItems = $pFreeRoad->get_elements( $intParentId ) ;
$intMainId = $arrItems['mainid'] ;
$floOrder = $arrItems['order'] ;
$intLevel = $arrItems['level'] ;
$strQuery = " INSERT INTO `menus` VALUES ( '' , $intMainId , $intParentId , $floOrder , $intLevel, '$info' ) " ;
$resResult = mysql_query ( &$strQuery , $resDbc ) ;
$pFreeRoad->update_tree ( $intMainId , $intParentId , $floOrder ) ;
@mysql_close( $resDbc ) ;
*/
?>
include "freeroad.class.php";
$strQuery = " SELECT * FROM `menus` ORDER BY `mainid` ASC , `order` ASC ";
$resResult = mysql_query ( &$strQuery , $resDbc ) ;
while ( $arrRows = mysql_fetch_array ( $resResult ))
{
$intLevel = $arrRows["level"] ;
$strSpace = '' ;
for ( $i = 0 ; $i $intLevel ; $i++ )
{
$strSpace .= " ";
}
if ( $i>1 )
{
$strSpace .= '--';
}
echo $strSpace . $arrRows["id"] . $arrRows["info"] ."
";
}
if ( $_GET["action"] == 'add' )
{
$pFreeRoad = new FreeRoad ( $resDbc , $arrDatabase["dbname"] , 'menus' , 'menu_tree' ) ;
$info = 'F1';
$intParentId = 1 ;
$arrItems = $pFreeRoad->get_elements( $intParentId ) ;
$intMainId = $arrItems['mainid'] ;
$floOrder = $arrItems['order'] ;
$intLevel = $arrItems['level'] ;
$strQuery = " INSERT INTO `menus` VALUES ( '' , $intMainId , $intParentId , $floOrder , $intLevel, '$info' ) " ;
$resResult = mysql_query ( &$strQuery , $resDbc ) ;
$pFreeRoad->update_tree ( $intMainId , $intParentId , $floOrder ) ;
@mysql_close( $resDbc ) ;
}
?>
容易可以看出,输出的时候是如此简单便实现树结构了:
SELECT * FROM `menus` ORDER BY `mainid` ASC, `order` ASC ;
前文输出写成输入了~~~ 晕倒。。
输出的时候,根据level来做树结构。

ホットAIツール

Undresser.AI Undress
リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover
写真から衣服を削除するオンライン AI ツール。

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

Video Face Swap
完全無料の AI 顔交換ツールを使用して、あらゆるビデオの顔を簡単に交換できます。

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

SublimeText3 中国語版
中国語版、とても使いやすい

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

SublimeText3 Mac版
神レベルのコード編集ソフト(SublimeText3)

ホットトピック









トマト ノベルは非常に人気のある小説閲覧ソフトウェアです。トマト ノベルでは、新しい小説や漫画を読むことができます。どの小説も漫画もとても面白いです。小説を書きたい友達もたくさんいます。お小遣いを稼いで、小説の内容を編集することもできます。 「テキストに文章を書きたいです。それで、小説はどうやって書くのですか?友達は知らないので、一緒にこのサイトに行きましょう。小説の書き方の入門を少し見てみましょう。」 Tomato Novels を使用して小説を書く方法に関するチュートリアルを共有します。 1. まず、携帯電話で Tomato Free Novels アプリを開き、パーソナル センター - ライター センターをクリックします。 2. Tomato Writer Assistant ページに移動し、次の場所で [新しい本の作成] をクリックします。小説の終わり

Colorful マザーボードは中国国内市場で高い人気と市場シェアを誇っていますが、Colorful マザーボードのユーザーの中には、設定のために BIOS を入力する方法がまだ分からない人もいます。この状況に対応して、編集者はカラフルなマザーボード BIOS に入る 2 つの方法を特別に提供しました。ぜひ試してみてください。方法 1: U ディスク起動ショートカット キーを使用して、U ディスク インストール システムに直接入ります。ワンクリックで U ディスクを起動する Colorful マザーボードのショートカット キーは ESC または F11 です。まず、Black Shark インストール マスターを使用して、Black Shark インストール マスターを作成します。 Shark U ディスク起動ディスクを選択し、コンピュータの電源を入れます。起動画面が表示されたら、キーボードの ESC キーまたは F11 キーを押し続けて、起動項目を順次選択するウィンドウに入ります。「USB」の場所にカーソルを移動します。 」と表示され、その後

残念ながら、WeChat は広く使用されているソーシャル ソフトウェアであり、何らかの理由で特定の連絡先を誤って削除してしまうことがよくあります。ユーザーがこの問題を解決できるように、この記事では、削除された連絡先を簡単な方法で取得する方法を紹介します。 1. WeChat の連絡先削除メカニズムを理解します。これにより、削除された連絡先を取得できるようになります。WeChat の連絡先削除メカニズムでは、連絡先がアドレス帳から削除されますが、完全には削除されません。 2. WeChat の組み込みの「連絡先帳復元」機能を使用します。WeChat には、この機能を通じて以前に削除した連絡先をすばやく復元できる「連絡先帳復元」機能が用意されています。 3. WeChat 設定ページに入り、右下隅をクリックし、WeChat アプリケーション「Me」を開き、右上隅にある設定アイコンをクリックして設定ページに入ります。

携帯電話が人々の日常生活において重要なツールになるにつれて、フォント サイズの設定は重要なパーソナライゼーション要件になりました。さまざまなユーザーのニーズを満たすために、この記事では、簡単な操作で携帯電話の使用体験を向上させ、携帯電話のフォントサイズを調整する方法を紹介します。携帯電話のフォント サイズを調整する必要があるのはなぜですか - フォント サイズを調整すると、テキストがより鮮明で読みやすくなります - さまざまな年齢のユーザーの読書ニーズに適しています - フォント サイズを使用すると、視力の悪いユーザーにとって便利です携帯電話システムの設定機能 - システム設定インターフェイスに入る方法 - 設定インターフェイスで「表示」オプションを見つけて入力します。 - 「フォント サイズ」オプションを見つけて、サードパーティでフォント サイズを調整します。アプリケーション - フォント サイズの調整をサポートするアプリケーションをダウンロードしてインストールします - アプリケーションを開いて、関連する設定インターフェイスに入ります - 個人に応じて

Huawei 携帯電話にデュアル WeChat ログインを実装するにはどうすればよいですか?ソーシャルメディアの台頭により、WeChatは人々の日常生活に欠かせないコミュニケーションツールの1つになりました。ただし、多くの人は、同じ携帯電話で同時に複数の WeChat アカウントにログインするという問題に遭遇する可能性があります。 Huawei 社の携帯電話ユーザーにとって、WeChat の二重ログインを実現することは難しくありませんが、この記事では Huawei 社の携帯電話で WeChat の二重ログインを実現する方法を紹介します。まず第一に、ファーウェイの携帯電話に付属するEMUIシステムは、デュアルアプリケーションを開くという非常に便利な機能を提供します。アプリケーションのデュアルオープン機能により、ユーザーは同時に

テクノロジーの発展に伴い、モバイルゲームは人々の生活に欠かせないものになりました。かわいいドラゴンエッグの画像と面白い孵化過程で多くのプレイヤーの注目を集めており、その中でも注目を集めているゲームの一つがモバイル版ドラゴンエッグです。プレイヤーがゲーム内で自分のドラゴンをより適切に育成し成長させることができるように、この記事ではモバイル版でドラゴンの卵を孵化させる方法を紹介します。 1. 適切な種類のドラゴン エッグを選択する プレイヤーは、ゲーム内で提供されるさまざまな種類のドラゴン エッグの属性と能力に基づいて、自分に適したドラゴン エッグの種類を慎重に選択する必要があります。 2. 孵化機のレベルをアップグレードします。プレイヤーはタスクを完了し、小道具を収集することで孵化機のレベルを向上させる必要があります。孵化機のレベルは孵化速度と孵化成功率を決定します。 3. プレイヤーはゲームに参加する必要がある孵化に必要なリソースを収集します。

今日の社会において、携帯電話は私たちの生活に欠かせないものとなっています。私たちの日常のコミュニケーション、仕事、生活のための重要なツールとして、WeChat はよく使用されます。ただし、異なるトランザクションを処理する場合は 2 つの WeChat アカウントを分離する必要がある場合があり、そのためには携帯電話が 2 つの WeChat アカウントへの同時ログインをサポートする必要があります。有名な国内ブランドとして、ファーウェイの携帯電話は多くの人に使用されていますが、ファーウェイの携帯電話で 2 つの WeChat アカウントを開設する方法は何でしょうか?このメソッドの秘密を明らかにしましょう。まず、Huawei 携帯電話で 2 つの WeChat アカウントを同時に使用する必要があります。最も簡単な方法は次のとおりです。

プログラミング言語 PHP は、さまざまなプログラミング ロジックやアルゴリズムをサポートできる、Web 開発用の強力なツールです。その中でも、フィボナッチ数列の実装は、一般的で古典的なプログラミングの問題です。この記事では、PHP プログラミング言語を使用してフィボナッチ数列を実装する方法を、具体的なコード例を添付して紹介します。フィボナッチ数列は、次のように定義される数学的数列です。数列の最初と 2 番目の要素は 1 で、3 番目の要素以降、各要素の値は前の 2 つの要素の合計に等しくなります。シーケンスの最初のいくつかの要素
