关于php:递归数组搜索 – 保持正确的顺序并迭代数组

Recursive array search - keep proper order and iterate down array

当访问我正在编写的脚本时,在访问页面时将类别路径传递给它。然后,脚本将数据与实际类别数组或应与该类别关联的分支进行比较。

我把父母和他们所有的孩子设置成一个tree,然后到分支机构比较数据,以确保客户使用的是正确的URL。下面是代码工作原理的一个简单示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Customer is accessing from site.com/store/some-cat/some-othercat
// We pass those variables with the htaccess to script.php?var=$1,$2
// We then explode that to make an array on $var[0] and $var[1]
$categoryMap = explode(",", $_GET['var']);
$categoryID = array();
$categoryInfoMap = array();
foreach ($categoryMap as $a) {
    $categoryIDs[] = trim($a);
}
$getCategoryInfo = $db->fn->query("SELECT * FROM store_category");
....
// Inside while loop...
     $categoryInfoMap[] = $db->result[]; // stored whole results as array
// End of the while loop
$masterKey = $mainClass->findKeyInDbArray($categoryInfoMap, 'c.path', $categoryMap[0]);
if ((isset($masterKey) && $masterKey ==="0") || !empty($masterKey)) {
    $thisId = $categoryInfoMap[$masterKey]['c.id'];
    $thisPath = $categoryInfoMap[$masterKey]['c.path'];
    $thisName = $categoryInfoMap[$masterKey]['c.name'];
    $tree = $mainClass->buildTree($categoryInfoMap);
    $children = $tree['children'][$thisId];
    $childrenItems ="";
    foreach ($categoryIDs as $cid) {
        // One of the categories entered doesnt exist at all so we redirect,
        // else we will go through them and make sure theyre apart of the branch
        if (!$mainClass->recursive_array_search($cid, $tree)) {
            ... redirect them somewhere and die()
        } else {
            if (!$mainClass->recursive_array_search($cid, $children)) {
                ... redirect them somewhere and die()
            } else {
                !!!!!!!!!!!!============!!!!!!!!!!!!!!
                    THIS IS THE IMPORTANT PART HERE
                !!!!!!!!!!!!============!!!!!!!!!!!!!!
            }
        }
    }
}
... Rest of the script which works for now

下面是上面代码中使用的函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public function findKeyInDbArray($products, $field, $value) {
    foreach($products as $key => $product) {
        if ($product[$field] === $value) {
            return"$key";
        }
    }
    return NULL;
}
public function buildTree($arr) {
    $tree = array(
        'children' => array()
    );
    $index = array(0=>&$tree);
    foreach ($arr as $key => $val) {
        $parent = &$index[$val['c.parentcatid']];
        $node = $val;
        $parent['children'][$val['c.id']] = $node;
        $index[$val['c.id']] = &$parent['children'][$val['c.id']];
    }
    return $tree;
}
public function recursive_array_search($needle,$haystack) {
    foreach($haystack as $key=>$value) {
        $current_key=$key;
        if($needle===$value OR (is_array($value) && $this->recursive_array_search($needle,$value) !== false)) {
            return $current_key;
        }
    }
    return false;
}

下面是一个树数组的例子,从父节点向下。因能见度原因而短路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Array(
[c.id] => 1
[c.name] => Radios
[c.path] => radios
[c.parentcatid] => 0
[children] => (
    [2] => (
        [0] => 2
        ....
        [children] => (
            [3] => (
                [c.id] => 3
                ....
                [c.parentcatid] => 2
            ),
            [4] => (
                [c.id] => 4
                ....
                [c.parentcatid] => 2
            )
        )
    )
    ......
    [10] => (
        [0] => 10
        ....
        [c.parentcatid] => 1
    )
)

好的部分

现在,代码正在努力证明分支具有与其树匹配的变量。如果我们用来与url $var比较的变量item path匹配,那么它将继续工作。因此,如果分支中存在以下值:

1
2
array(c.path => 'foo'),
array(c.path => 'bar')

我以site.com/store/foo/bar的身份访问脚本,然后一切都很好。如果我以site.com/store/foo/notBar的身份访问该站点,那么它将失败,因为notbar变量不是该分支的成员。太好了,对吧?一切正常!但事实并非如此,而且有充分的理由。

这里的问题

如果项目在分支中匹配,那么它已经通过检查,并且是检查的结束。如果以错误的顺序传递项(如site.com/store/bar/foo),则不是这样,但从技术上讲,它在其中仍然有好的变量,但不应通过,因为结构的顺序与它从父数组中向下的顺序不符。同样地,如果在树上更远的地方有另一个枝条,比如说barwithNoChildren存在,我可以用它交换foobar然后仍然通过,即使不应该存在任何东西。

希望你们能理解我的要求,并能帮我提出解决办法。最近几天,我一直在这个系统上绞尽脑汁,因为他们想通过搜索引擎优化(seo)和其他原因获得更漂亮的URL,所以这比我计划的要困难得多。谢谢你的建议!


树的结构对于这个目的并没有真正的帮助。您应该考虑如何创建一个数据结构,使您很容易匹配输入。因为您的类别输入描述了树的一个分支,所以最好是构建一个数组,您可以使用该数组来有效地将这些分支描述与类别匹配。

让我们构建一个数组,其中键是每个类别的路径,如它们的slug所描述的那样,值是类别ID。然后,我们可以立即标识匹配的类别,或者如果路径不在数组中,则会失败。

这种类似面包屑的结构是另一种常见于类别的模式。除了树和平面ID图,您可以做几乎任何您需要的事情。关键是考虑用数据创建不同的结构来完成不同的任务。通常,创建一个易于使用的新结构比创建复杂的逻辑来尝试和使用一个不适合当前任务的现有结构更有效、更不容易出错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?php
//Mock category records, would come from the DB in the real world
$categoryRecords = [
    ['id' => 1, 'title' => 'Radios', 'slug' => 'radios', 'parent_id' => 0],
    ['id' => 2, 'title' => 'Accessories', 'slug' => 'misc', 'parent_id' => 1],
    ['id' => 3, 'title' => 'Motorola', 'slug' => 'motorola', 'parent_id' => 1],
    ['id' => 4, 'title' => 'Handheld', 'slug' => 'handheld', 'parent_id' => 3],
    ['id' => 5, 'title' => 'Mobile', 'slug' => 'mobile', 'parent_id' => 3]
];

//Create an array that maps parent IDs to primary keys
$idMap = [];
foreach ($categoryRecords as $currRecord)
{
    $idMap[$currRecord['id']] = $currRecord;
}

//Traverse the flat array and build the path lines
$paths = [];
$categoryIds = array_keys($idMap);
foreach ($categoryIds as $currLeafId)
{
    $currCategoryId = $currLeafId;

    $currLine = [];

    do
    {
        $currLine[]     = $idMap[$currCategoryId]['slug'];
        $currCategoryId = $idMap[$currCategoryId]['parent_id'];
    } while ($currCategoryId != 0);

    $currLine = array_reverse($currLine);
    $currPath = implode('/', $currLine);
    $paths[$currPath] = $currLeafId;
}

//Join your input - $_GET['var'] in your example
$inputPath = implode('/', ['radios', 'motorola', 'handheld']);

//Now you can see if the incoming path matched a category
if(array_key_exists($inputPath, $paths))
{
    $category = $categoryRecords[$paths[$inputPath]];

    echo 'Matched category: '.$category['title'].PHP_EOL;
}
else
{
    echo 'Invalid category path';
}