簡體   English   中英

PHP:通過遞歸迭代構建鄰接表

[英]PHP: Building an Adjacency List through Recursive Iteration

我正在嘗試構建一個扁平化的數組,該數組可以保留來自CodeIgniter項目視圖中的棘手數組的元數據。 元數據就是標識符,深度和父節點之類的東西。

數據來自查詢構建器JavaScript庫,該庫允許用戶生成將在業務邏輯中使用的規則。 我需要保留這些數據,並且用來表示這些規則的樹狀性質的模型是鄰接表。

這就是我所擁有的,它在大多數情況下都可以使用,但是很丑陋,它是由泡泡糖和膠帶制成的,而且“大多數”情況並非“全部”情況。 閱讀了SPL文檔后,我懷疑RecursiveIteratorIterator可能更適合該問題。

對冗長的帖子感到抱歉,但是我敢肯定我的方法很糟糕。 有什么建議嗎?

這是輸入(例如,我不想去的地方),示例圖像也顯示了它的作用:

查詢生成器示例

stdClass Object
(
    [condition] => OR
    [rules] => Array
        (
            [0] => stdClass Object
                (
                    [id] => Any
                    [field] => Any
                    [type] => string
                    [input] => select
                    [operator] => not equal
                    [value] => Any
                )
            [1] => stdClass Object
                (
                    [condition] => AND
                    [rules] => Array
                        (
                            [0] => stdClass Object
                                (
                                    [id] => Place
                                    [field] => Place
                                    [type] => string
                                    [input] => select
                                    [operator] => equal
                                    [value] => France
                                )
                            [1] => stdClass Object
                                (
                                    [id] => Month
                                    [field] => Month
                                    [type] => string
                                    [input] => select
                                    [operator] => equal
                                    [value] => January
                                )
                        )
                )
            [2] => stdClass Object
                (
                    [condition] => AND
                    [rules] => Array
                        (
                            [0] => stdClass Object
                                (
                                    [id] => Place
                                    [field] => Place
                                    [type] => string
                                    [input] => select
                                    [operator] => equal
                                    [value] => Rio
                                )
                            [1] => stdClass Object
                                (
                                    [id] => Month
                                    [field] => Month
                                    [type] => string
                                    [input] => select
                                    [operator] => equal
                                    [value] =>  August
                                )
                        )
                )
            [3] => stdClass Object
                (
                    [condition] => AND
                    [rules] => Array
                        (
                            [0] => stdClass Object
                                (
                                    [id] => Place
                                    [field] => Place
                                    [type] => string
                                    [input] => select
                                    [operator] => equal
                                    [value] => Liberia
                                )
                            [1] => stdClass Object
                                (
                                    [id] => Month
                                    [field] => Month
                                    [type] => string
                                    [input] => select
                                    [operator] => equal
                                    [value] => July
                                )
                            [2] => stdClass Object
                                (
                                    [condition] => OR
                                    [rules] => Array
                                        (
                                            [0] => stdClass Object
                                                (
                                                    [id] => Year
                                                    [field] => Year
                                                    [type] => string
                                                    [input] => select
                                                    [operator] => equal
                                                    [value] => 2014
                                                )
                                            [1] => stdClass Object
                                                (
                                                    [id] => Year
                                                    [field] => Year
                                                    [type] => string
                                                    [input] => select
                                                    [operator] => equal
                                                    [value] => 2015
                                                )
                                        )
                                )
                        )
                )
        )
)

這是持久性所需的輸出。 (有關元數據的重要位,請參見每個條目最右邊的值)。

Array
(
    stdClass Object ( [id] => Any [field] => Any [type] => string [input] => select [operator] => not equal [value] => Any [condition] => OR [subgroup] => 0 [parent_subgroup] => )
    stdClass Object ( [id] => Place [field] => Place [type] => string [input] => select [operator] => equal [value] => France) [condition] => AND [subgroup] => 1 [parent_subgroup] => 0 )
    stdClass Object ( [id] => Month [field] => Month [type] => string [input] => select [operator] => equal [value] => January [condition] => AND [subgroup] => 1 [parent_subgroup] => 0 )
    stdClass Object ( [id] => Place [field] => Place [type] => string [input] => select [operator] => equal [value] => Rio [condition] => AND [subgroup] => 2 [parent_subgroup] => 0 )
    stdClass Object ( [id] => Month [field] => Month [type] => string [input] => select [operator] => equal [value] => August[condition] => AND [subgroup] => 2 [parent_subgroup] => 0 )
    stdClass Object ( [id] => Place [field] => Place [type] => string [input] => select [operator] => equal [value] => Liberia [condition] => AND [subgroup] => 3 [parent_subgroup] => 0 )
    stdClass Object ( [id] => Month [field] => Month [type] => string [input] => select [operator] => equal [value] =>  July[condition] => AND [subgroup] => 3 [parent_subgroup] => 0 )
    stdClass Object ( [id] => Year [field] => Year [type] => string [input] => select [operator] => equal [value] => 2014 [condition] => OR [subgroup] => 4 [parent_subgroup] => 3 )
    stdClass Object ( [id] => Year [field] => Year [type] => string [input] => select [operator] => equal [value] => 2015 [condition] => OR [subgroup] => 4 [parent_subgroup] => 3 )    
)

注意:正確解析。 如果我更改了子組2和3的順序,則會出現問題,因為具有規則(年= 2014或年= 2015)的組3的子組具有不同的嵌套級別,並且嚴重干擾了我的遞歸。

這是我的代碼:

function deserialize_criteria_group($criteria, $subgroup = null) {
    $array = array();

    if ($subgroup == null) {
        $first_run = true;
        $subgroup = 0;
        $condition = $criteria->condition;
        $criteria = $criteria->rules;
    }

    foreach ($criteria as $rule) {
        if ($rule->rules) {
            $subgroup++;
            $children = $this->deserialize_criteria_group($rule->rules, $subgroup);
            foreach($children as $child) {
                if ($child->condition == null) {
                    $child->condition = $rule->condition;
                }
                if ($child->parent_subgroup == null) {
                    $child->parent_subgroup = $first_run ? 0 : $subgroup - 1;
                }
                    array_push($array, $child);
            }
        } else {
            $rule->condition = $condition;
            $rule->subgroup = $subgroup;
            $rule->parent_subgroup = null;
            array_push($array, $rule);
        }

    }

    if ($first_run) {
        //Ensure a root node exists, if not stub one out. 
        $criteria_group = json_decode(json_encode($array), true);
        $root_encountered = $criteria_group[0]['subgroup'] > 0 ? false : true;
        if (!$root_encountered) {
            $root = array(  'subgroup'          => 0, 
                            'parent_subgroup'   => null, 
                            'condition'         => $condition);
            array_unshift($criteria_group, $root); 
            array_unshift($array, $root);
        }

        //Ensure the ALM is not broken. 
        $subgroup = 0;
        foreach($criteria_group as $c) {
            if($c['subgroup'] > $subgroup + 1) {
                $msg = "Bad Order. Halting execution.";
                print $msg; 
                log_message('error', $msg); 
                log_message('debug', 'expected: ' . $subgroup . ' actual: ' . $c['subgroup']);
                log_message('debug', print_r($criteria_group, true));
                die;
            }
            $subgroup = $c['subgroup'];
        }
    }
    return $array;
}

感謝Rocket Hazmat的協助。

編輯:好像我錯過了一些代碼,抱歉。

編輯2:這種方法還有一些其他問題。 我在下面顯示更正。

解:

<?php
class CriteriaIterator implements RecursiveIterator{
    private $data, $counter, $condition, $subgroup, $parent_subgroup;

    public function __construct($criteriaGroup, $condition, $parent_subgroup=null){
            $this->condition = $condition;
            $this->subgroup = $GLOBALS['highest_subgroup'];
            $this->parent_subgroup = $parent_subgroup;
            $this->data = is_array($criteriaGroup) ? $criteriaGroup : array($criteriaGroup);
    }

    public function current(){
            $row = $this->data[$this->counter];
            if ($row->id) {
                    return (object) array(
                            'id' => $row->id,
                            'field' => $row->id,
                            'operator' => $row->operator,
                            'value' => $row->value,
                            'condition'=> $this->condition,
                            'subgroup' => $GLOBALS['highest_subgroup'],
                            'parent_subgroup' => $this->parent_subgroup
                    );
            }
    }

    public function key(){
            return $this->counter;
    }

    public function next(){
            $this->counter++;
    }

    public function rewind(){
            $this->counter = 0;
    }

    public function valid(){
        return $this->counter < count($this->data);
    }

    public function hasChildren(){
        $row = $this->data[$this->counter];
        return isset($row->rules);
    }

    public function getChildren(){    
        $GLOBALS['highest_subgroup']++;
        $row = $this->data[$this->counter];
        return new self($row->rules, $row->condition, $this->subgroup);
    }
}

之后調用並清除,如下所示:(最后有點懶了,改裝到運行PHP 5.3的CodeIgniter中)

$records = new RecursiveIteratorIterator(
    new CriteriaIterator($a['criteria_group'], $a['criteria_group']->condition),
    RecursiveIteratorIterator::SELF_FIRST);

$criteria = array();
$parent_encountered = false;

// cleanup
foreach($records as $row) {
    if($row != null) {
        $row->parent_subgroup = $row->parent_subgroup == - 1 ? null : $row->parent_subgroup;
        if($row->parent_subgroup === null) {
            $parent_encountered = true;
        }
        array_push($criteria, $row);
    }
}

if(!$parent_encountered) {
    $row = array(
        'subgroup' => 0,
        'parent_subgroup' => null,
        'condition' => $a['criteria_group']->condition
    );
    array_unshift($criteria, json_decode(json_encode($row)));
}

與此相關的問題出現在小組成員上。 我的檢索方法使用廣度優先搜索來創建要傳遞到腳本中的json對象。 不幸的是,有了嵌套級別,重新保存時事情就一發不可收拾。

這是可能導致混淆的設置示例。 值之前的天數顯示預期的子組。

描述一個打破鄰接表的組

可能可以在遞歸迭代器類中進行修復,但Rocket Hazmat建議使該類非常簡單。 我在清理過程中實施了一個修復程序:

        $records = new RecursiveIteratorIterator(
                new CriteriaIterator($a['criteria_group'], $a['criteria_group']->condition), 
                RecursiveIteratorIterator::SELF_FIRST);

        $criteria = array();
        $root_encountered = false;

        // cleanup
        foreach($records as $row) {
            if($row != null) {
                if($row->parent_subgroup == - 1) {
                    $row->parent_subgroup = null;
                    $row->subgroup = 0;
                } 
                if($row->parent_subgroup === null) {
                    $root_encountered = true;
                }
                array_push($criteria, $row);
            }
        }

        if(!$root_encountered) {
            $row = (object) array(
                    'subgroup' => 0,
                    'parent_subgroup' => null,
                    'condition' => $a['criteria_group']->condition 
            );
            array_unshift($criteria, $row);
        }

        //strategy: keep a record keyed by subgroups of where they are rooted. 
        //if an entry exists for a previous subgroup but the parent subgroup conflicts
        //use the subgroup of the LAST subgroup rooted there. 
        //else update array

        $adjacency = array(0 => null); //parent
        foreach($criteria as $row) {
            if (isset($adjacency[$row->subgroup]) && $adjacency[$row->subgroup] != $row->parent_subgroup) {
                $preserved = array_reverse($adjacency, true); //need LAST subgroup rooted there
                foreach($preserved as $key=>$val) {
                    if ($val == $row->parent_subgroup) {
                        $row->subgroup = $key;
                        break;
                    }
                }
            } else {
                $adjacency[$row->subgroup] = $row->parent_subgroup;
            }
        }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM