Extension:Dynamic Article List/CategoryTravelerBase.php

From MediaWiki.org
Jump to: navigation, search

Extension:Dynamic Article List

<?php
require_once( 'CategoryNode.php' );
/**
* Base Class of Category Traveler
*
* @package MediaWiki
* @author Zeng Ji(zengji@gmail.com)
*/
abstract class CategoryTravelerBase {
var $dbr;
var $sPageTable;
var $sCategorylinksTable;
// All category nodes organized by a list. Key is CategoryName, Content is CategoryNode.
// This variable is used to quick-orientation while constructing category tree.
var $categoryList;
// All category nodes organized by a tree. Key is CategoryName, Content is CategoryNode.
var $categoryTree;
function __construct() {
$this->dbr =& wfGetDB( DB_SLAVE );
$this->sPageTable = $this->dbr->tableName( 'page' );
$this->sCategorylinksTable = $this->dbr->tableName( 'categorylinks' );
$this->categoryList = array();
$this->categoryTree = array();
}
// Allow user to define which category should the traveler start from.
// If not defined, traveler will start from all level 1 (no parent) categories in datebase.
function buildCategoryTree($categoryRoot=false) {
unset($this->categoryList);
unset($this->categoryTree);
$this->categoryList = array();
$this->categoryTree = array();
$fname = get_class($this) . '::BuildCategoryTree';
$dbr = $this->dbr;
// Construct all category nodes, and fill in categoryList.
$sql = $this->genSQLcategoryAll();
$res = $dbr->query($sql, $fname);
while( $obj = $dbr->fetchObject( $res ) ) {
$cId = $obj->categoryId;
$cName = $obj->categoryName;
$this->categoryList[$cName] = new CategoryNode($cId, $cName);
}
$dbr->freeResult( $res );
if ($categoryRoot == false) {
// Get level 1 (no parent) categories to travel from.
$sql = $this->genSQLcategoryTopLevel($this->getNotTopCategoryList());
} else {
// Get defined category to travel from.
$categoryRoot = str_replace( ' ', '_', $categoryRoot);
$sql = $this->genSQLcategoryByName($categoryRoot);
}
$res = $dbr->query($sql, $fname);
while( $obj = $dbr->fetchObject( $res ) ) {
$cId = $obj->categoryId;
$cName = $obj->categoryName;
// Generate first level categories in tree.
$categoryNode = $this->categoryList[$cName];
if (isset($categoryNode)) {
$this->categoryTree[$cName] = $categoryNode;
}
// Recursion to build children tree.
$this->buildOneCategory(false, $cName);
}
$dbr->freeResult( $res );
}
// Recursion Function£ºProcess current category and all its children.
// $cParent: The parent of current processing category node.
// If current processing category node is top level node, "cParent" should be set to false.
// $cName: The name of current processing category node.
function buildOneCategory($cParent, $cName) {
$categoryNode = $this->categoryList[$cName];
if (isset($categoryNode)) {
// Avoid the same category node to be travelled more than once.
// For view of "CategoryTravelerBase", all categories should only has one parent or not.
// Although maybe in fact some categories will have more than one parents.
$categoryNode->hasTravelled = true;
// Get all children categories, establish children/parent relationship.
$categoryNode->parent = $cParent;
$fname = get_class($this) . '::buildOneCategory';
$dbr = $this->dbr;
$sql = $this->genSQLcategoryChildren($cName);
$res = $dbr->query($sql, $fname);
if ($dbr->numRows( $res ) == 0) {
// No children categories, recursion ends here.
$dbr->freeResult( $res );
return;
}
$categoryNode->children = array();
while( $obj = $dbr->fetchObject( $res ) ) {
$childName = $obj->categoryName;
$childNode = $this->categoryList[$childName];
if ($childNode->hasTravelled == true)
continue;
$categoryNode->children[$childName] = $childNode;
$this->buildOneCategory($childNode, $childName);
}
$dbr->freeResult( $res );
}
}
// Deep recursion to build category tree.
function travelCategoryTree() {
$this->travelStart();
// "0" means "Travel from Root"
$this->travelOneCategory(0);
$this->travelEnd();
}
function travelOneCategory($level, $categoryNode=false) {
if ($level==0) {
// Get TOP Level Categories
$categoryList = $this->categoryTree;
} else {
// Get Next Level Categories
$categoryList = $categoryNode->children;
}
// Process Current Category
if ($categoryNode != false)
$this->travel($level, $categoryNode);
// Process Children of Current Category
if ($categoryList != false) {
$categoryCount = count($categoryList);
$index = 0;
foreach($categoryList as $cName => $cNode) {
if ($index == 0)
$this->travelBeforeFirst($level+1, $cNode);
$this->travelOneCategory($level+1, $cNode);
$index = $index + 1;
if ($index == $categoryCount)
$this->travelAfterLast($level+1, $cNode);
}
}
}
abstract function travelStart();
abstract function travelEnd();
abstract function travelBeforeFirst($level, $categoryNode); // Before travel the first category in one level
abstract function travel($level, $categoryNode);
abstract function travelAfterLast($level, $categoryNode); // After travel the last category in one level
/////////// Utility Function ////////////////
// Generate SQL: Retrieve category information by name
function genSQLcategoryByName($cName) {
$sql = "SELECT page_id AS categoryId, page_title AS categoryName ";
$sql.= "FROM $this->sPageTable ";
$sql.= "WHERE page_namespace=" . NS_CATEGORY . " AND page_title=\"" . $cName . "\"";
return $sql;
}
// Generate SQL: Retrieve children categories by name
function genSQLcategoryChildren($cName) {
$sql = "SELECT cl.cl_from AS categoryId, page_title AS categoryName ";
$sql.= "FROM $this->sPageTable INNER JOIN $this->sCategorylinksTable AS cl ";
$sql.= "ON page_id=cl.cl_from AND (page_namespace=" . NS_CATEGORY . ") ";
$sql.= "AND (cl.cl_to='" . $cName . "') ";
$sql.= "ORDER BY page_title";
return $sql;
}
// Generate SQL: Retrieve all "non-TOP level" categories.
// "non-TOP level" means has parents.
function genSQLcategoryNotTopLevel() {
$sql = "SELECT cl.cl_from AS categoryId, page_title AS categoryName ";
$sql.= "FROM $this->sPageTable INNER JOIN $this->sCategorylinksTable AS cl ";
$sql.= "ON page_id=cl.cl_from AND (page_namespace=" . NS_CATEGORY . ") ";
$sql.= "ORDER BY page_title";
return $sql;
}
// Generate SQL: Retrieve all categories.
function genSQLcategoryAll() {
$sql = "SELECT page_id AS categoryId, page_title AS categoryName ";
$sql.= "FROM $this->sPageTable ";
$sql.= "WHERE page_namespace=" . NS_CATEGORY . " ";
$sql.= "ORDER BY page_title";
return $sql;
}
// Generate SQL: Retrieve all "TOP level" categories.
// Use "NOT IN" clause in SQL query. "excludeList" should be all "non-TOP level" categories' ID.
function genSQLcategoryTopLevel($excludeList) {
$sql = "SELECT page_id AS categoryId, page_title AS categoryName ";
$sql.= "FROM $this->sPageTable ";
if ($excludeList) {
$sql.= "WHERE page_namespace=" . NS_CATEGORY . " AND (page_id NOT IN (" . $excludeList . "))";
} else {
$sql.= "WHERE page_namespace=" . NS_CATEGORY;
}
return $sql;
}
// Return a string which contains all "non-TOP level" categories' ID.
function getNotTopCategoryList() {
$fname = get_class($this) . '::getNotTopCategoryList';
$sql = $this->genSQLcategoryNotTopLevel();
$dbr = $this->dbr;
$res = $dbr->query($sql, $fname);
// Query result is blank.
if ($dbr->numRows( $res ) == 0) {
$dbr->freeResult( $res );
return false;
}
// Generate a string of categories' ID list.
// For example: 1234,3721,4567
$ret = '';
while( $obj = $dbr->fetchObject( $res ) ) {
if( isset( $obj->categoryId ) ) {
$ret .= $obj->categoryId . ',';
}
}
$ret = substr($ret, 0, strlen($ret)-1); // Delete tail comma
$dbr->freeResult( $res );
return $ret;
}
}
?>
Personal tools
Namespaces
Variants
Actions
Site
Support
Download
Development
Communication
Print/export
Toolbox