Синглтон-класс MySQL конфликтует с рекурсивной функцией (PHP) - PullRequest
2 голосов
/ 26 июля 2010

У меня есть одноэлементный класс MySQL, написанный на PHP.Его код указан ниже:

class Database {
    private $_result = NULL;
    private $_link = NULL;
    private $_config = array();
    private static $_instance = NULL; 

    // Return singleton instance of MySQL class
    public static function getInstance(array $config = array()) {
        if (self::$_instance === NULL) {
            self::$_instance = new self($config);
        }

        return self::$_instance;
    }

    // Private constructor
    private function __construct(array $config) {
        if (count($config) < 4) {
            throw new Exception('Invalid number of connection parameters');  
        }

        $this->_config = $config;
    } 

    // Prevent cloning class instance
    private function __clone() {}

    // Connect to MySQL
    private function connect() {
        // Connect only once
        static $connected = FALSE;

        if ($connected === FALSE) {
            list($host, $user, $password, $database) = $this->_config;

            if ((!$this->_link = mysqli_connect($host, $user, $password, $database))) {
                throw new Exception('Error connecting to MySQL : ' . mysqli_connect_error());
            }

            $connected = TRUE;

            unset($host, $user, $password, $database);      
        }
    } 

    // Perform query
    public function query($query) {
        if (is_string($query) and !empty($query)) {
            $this->connect();
            if ((!$this->_result = mysqli_query($this->_link, $query))) {
                throw new Exception('Error performing query ' . $query . ' Message : ' . mysqli_error($this->_link));
            }
        }
    }

    // Fetch row from result set
    public function fetch() {
        if ((!$row = mysqli_fetch_object($this->_result))) {
            mysqli_free_result($this->_result);
            return FALSE;
        }

        return $row;
    }

    // Get insertion ID
    public function getInsertID() {
        if ($this->_link !== NUlL) {
            return mysqli_insert_id($this->_link); 
        }

        return NULL;  
    }

    // Count rows in result set
    public function countRows() {
        if ($this->_result !== NULL) {
           return mysqli_num_rows($this->_result);
        }

        return 0;
    }  

    // Close the database connection
    function __destruct() {
        is_resource($this->_link) AND mysqli_close($this->_link);
    }    
}

У меня также есть эта рекурсивная функция, которая должна возвращать полное дерево категорий (таблицу SQL и ее содержимое можно найти здесь ):

function getCategories($parent = 0) {
    $html = '<ul>';    
    $query = "SELECT * FROM `categories` WHERE `category_parent` = '$parent'";
    $database->query($query);    
    while($row = $database->fetch()) {
        $current_id = $row->category_id;
        $html .= '<li>' . $row->category_name;
        $has_sub = 0;
        $query = "SELECT `category_parent` FROM `categories` WHERE `category_parent` = '$current_id'";
        $database->query($query);
        $has_sub = $database->countRows();

        if ($has_sub > 0) {        
            $html .= getCategories($current_id);
        }

        $html .= '</li>';
    }

    $html .= '</ul>';
    return $html;
}

Теперь проблема в том, что функция возвращает только 3 категории, а не полное дерево.Я переписал функцию, используя обычные функции MySQL (mysql_query (), mysql_fetch_object () и т. Д.), И она возвращает правильный результат.

Итак, мой вывод, что с этим классом что-то не так.Обратите внимание, что я использовал этот класс в большинстве своих проектов, но никогда не сталкивался с этой проблемой.

Есть идеи?

Спасибо.

РЕДАКТИРОВАТЬ: Попытка заставить его вернуть ассоциативный массив

function getCategories($parent = 0) {
    global $database;
    $categories = array();

    $query = "SELECT * FROM `categories` WHERE `category_parent` = '$parent'";
    $database->query($query);    
    while($row = $database->fetch()) {
        $categories[] = array('id' => $row->category_id, 'name' => $row->category_name);        
    }

    for ($i = 0; $i < count($categories); $i++) {

        $categories[$i]['id']['children'] = getCategories($categories[$i]['id']);

    }

    return $categories;
}

Приведенный выше код возвращает следующий массив, но это не совсем нормально:

Array
(
    [0] => Array
        (
            [id] => 1
            [name] => Categoria 1
            [children] => Array
                (
                    [0] => Array
                        (
                            [id] => 4
                            [name] => Categoria 1.1
                            [children] => Array
                                (
                                    [0] => Array
                                        (
                                            [id] => 7
                                            [name] => Categoria 1.1.2
                                            [children] => Array
                                                (
                                                )

                                        )

                                )

                        )

                    [1] => Array
                        (
                            [id] => 5
                            [name] => Categoria 1.2
                            [children] => Array
                                (
                                )

                        )

                    [2] => Array
                        (
                            [id] => 6
                            [name] => Categoria 1.3
                            [children] => Array
                                (
                                )

                        )

                )

        )

    [1] => Array
        (
            [id] => 2
            [name] => Categoria 2
            [children] => Array
                (
                )

        )

    [2] => Array
        (
            [id] => 3
            [name] => Categoria 3
            [children] => Array
                (
                )

        )
)

Ответы [ 2 ]

3 голосов
/ 26 июля 2010

Проблема, с которой вы сталкиваетесь, заключается в том, что вы выполняете другие запросы поверх первого запроса. Поэтому после того, как вы выполните рекурсивный переход через первую ветвь, функция завершится сбоем, потому что вернется к исходному циклу which и обнаружит, что она уже находится в конце результирующих строк из первой ветки (3 уровня рекурсии в).

Быстрое решение проблемы - переписать вашу функцию, используя два цикла вместо одного. Таким же образом вы также можете оптимизировать функцию, сократив количество вызовов БД, которые вам нужно сделать.

Ваш первый запрос в порядке. Но в вашем цикле while просто возьмите соответствующие элементы id и name и сохраните их в массиве. Затем пересмотрите массив, который вы только что создали для рекурсии. Кроме того, вы можете исключить несколько запросов, не выполняя запрос «check». Просто повторить - и если нет элементов, массив id: name - возвращает пустую строку.

РЕДАКТИРОВАТЬ: пример (не проверено)

function getCategories($parent = 0) {
    $categories = array();
    $html = "<ul>";
    $query = "SELECT * FROM `categories` WHERE `category_parent` = '$parent'";
    $database->query($query);    
    while($row = $database->fetch()) {
        $categories[$row->category_id] = $row->category_name;
    }
    foreach($categories as $cid=>$category) {
        $html .= "<li>{$row->category_name}";
        $inner = getCategories($cid);
        if($inner != "<ul></ul>")
            $html .= $inner;
        $html .= "</li>"
    }
    $html .= "</ul>";
    return $html;
}
0 голосов
/ 27 июля 2010

Ваша основная проблема в том, что ваш объект базы данных является шизофреничным: это и объект соединения, и объект ресурса.Дополнительные запросы, выполненные до завершения предыдущего, перезапишут существующий дескриптор ресурса.

Решение простое: просто создайте второй класс, который инкапсулирует дескрипторы ресурса и возвращает данные.

...