php无限分类之网站导航

发布于:2023-01-04 ⋅ 阅读:(273) ⋅ 点赞:(0)

php无限分类也可以实现网站导航的效果,例如1688的官网头部导航:

测试数据:

    const arr = [
        ['id' => 1, 'pid' => 0, 'city' => '江西'],
        ['id' => 2, 'pid' => 0, 'city' => '广东'],
        ['id' => 3, 'pid' => 1, 'city' => '宜春'],
        ['id' => 4, 'pid' => 3, 'city' => '丰城'],
        ['id' => 5, 'pid' => 4, 'city' => '尚庄'],
        ['id' => 6, 'pid' => 2, 'city' => '深圳'],
        ['id' => 7, 'pid' => 6, 'city' => '翻身'],
        #以下是追加的同级元素数据,用来试验论证,也是增强干扰性
        ['id' => 8, 'pid' => 1, 'city' => '南昌'],
        ['id' => 9, 'pid' => 8, 'city' => '高新区'],
    ];

思路分析:

传入一个顶级pid  ,只有当pid为0时,才是顶级,故pid的值为0。

根据pid找到当前类别的数据,得到当前类别的id,以此当作当前类别的子类pid传入。

继续重复刚刚上面这个行为,直到找不到子类为止,则跳出当前类别的查找,继续下一个类别的查找。

也就是:传入pid = 0 ,只有id为1和id为2的江西和广东符合条件。

1.先处理江西,再找到宜春和南昌,

2.先处理宜春,再根据宜春找到丰城,再根据丰城找到尚庄,再根据尚庄去找发现没有子级,跳出宜春;

3.宜春处理完毕后,来处理与之同级的南昌;根据南昌找到高新区,根据高新区去找发现没有子级,跳出南昌来到江西。

4.此时发现江西的子类宜春和南昌处理完毕,跳出来到最顶级的广东。

4.根据广东,找到深圳,再根据深圳找到翻身,根据翻身去找发现没有子级,再跳出,发现所有数据处理完毕,程序结束。

根据上面的思路得知,这是一个可以无限调用自身的方法,也就是递归来处理。

代码实践: 

下列将逐渐迭代版本,以帮助理解。

唠叨一下:

网上的例子大多都是给出一个最终版叫人去看,不过我想如果你能看到我这篇文章,多半也和我一样看了也不明所以,所以还是自己写一个熟悉的层级关系数组来实践为好。

首先要明白的思路是:声明一个数组,用这个数组去存储每次得到的分类返回值,再将这个分类返回值以键名child在它的父级中以元素的形式插入该数据,如此往复,得到一个完整的层级关系大数组。

版本一:

public function test(array $data , $pid = 0){
		$array = [];
		foreach ($data as $key => $value) {
            //找到当前分类的下一级(子级)
			if($value['pid'] == $pid){
				$array[$value['id']] = $value;  
				$array[$value['id']]['child'] = $this->test($data,$value['id']);
			}
		}
		return $array;
	}

效果如下:

 

这一版需理解大概思路是:

1.传入了顶级数据(我此刻就是顶级数据,也就爷爷!) , 然后声明了$array这个数组存储每一个孙子的数据并返回

2.使用$array去存储当前分类子级(我儿子)的数据

3.(我)儿子可能不止一个,可能有很多个,所以需要使用每个儿子的id作为键名来存储每个儿子的值,保证每个儿子的数据不会乱套,即$array[$value['id']] = $value;

如果使用$pid或者$key去作键名存储,要意识到:这两玩意是会重复的不具备唯一性,这样就导致每个儿子的数据乱了,可能后面的儿子和前面的儿子键名都一样,后者把前者覆盖了,这就尴尬了!所以这两玩意不能作为键名存储儿子的数据。

4.那我有了儿子的数据,我也得有儿子的儿子(孙子)的数据哇。那孙子的数据怎么来呢?

这简单,把儿子的id继续往自身函数里一丢再返回出来不就得到了吗?于是此时递归产生了....

5.通过递归,我找到了孙子的数据,并且也把孙子的数据返回了出来;那我得把孙子的数据丢到儿子的数据里吧?孙子的名字在儿子那叫什么好呢?有了,就叫child吧!哈哈孩子!

所以就出现了$array[$value['id']]['child'] = $this->test($data,$value['id']);

6.诶?神奇的一幕发生了,我作为爷爷,拿到了儿子的数据,也通过递归拿到了孙子的数据。但是我的孙子还有儿子和孙子哇,所以在把儿子的id继续往自身一丢的时候,我的(每个)儿子就成了曾孙们的爷爷!再如此往复...我的曾孙们也成为了爷爷!

这样就出现了一个分类关系:我就是祖宗,我的儿子们是二代,我的孙子们是三代;

但是我的儿子和孙子们要想得到它们子辈数据,就得像我一样成为爷爷才行,也就是说:每一个分类都应该以child为键名存储它的子级(儿子)。

在第一次传入顶级pid=0,得到$array(子级) , $array[$value['id']]['child'](孙子级)

递归开始:

第二次传入:儿子id=1 ,得到$array(曾孙), $array[$value['id']]['child'](曾曾孙)

无限递归中....

第n次传入:没有子级了,好了,我已经是家族里目前最小的孩子了(没有比我更小的了!),我本身的数据是$array,该把$array塞到我的父级以child为键名的元素中去了...欸?我的父亲还有父亲,我的父亲要把它的数据给他的父亲(我的爷爷)...欸?我的爷爷还有父亲,我爷爷的父亲也该把数据给他的父亲(我的曾曾曾祖父)...如此往复...这样逐渐自下往上提交数据,最顶级的人就拿到了整个家族的数据(整个分类)了!

7.如果你觉得上面的关系太过复杂,可以看下列:

从1到100

 1 2 3 4 5 6 7 8 9 10 ... 100

我此刻是1,我的下一位数是2,我的下下位是3,再多我就不知道了。

也就是1['child'] = 2 , 2['child'] = 3

既然我不知道,那我就让我的儿子来呗,也就是让2来。

此刻我是2,我的下一位是3,我的下下位是4,再多我就不知道了。

也就是2['child'] = 3 , 3['child'] = 4

既然我不知道,那我就让我的儿子来呗,也就是让3来。

........

直到这样:

此刻我是99,我的下一位是100,我的下下位没有了!

也就是99['child'] = 100 , 100['child'] = ‘’

啊,原来在我们家100是尽头了啊...那我要开始汇报了

99['child'] =[

        100['child'] = ‘’

]

98['child'] =[

        99['child'] = [

        100['child'] = ‘’

    ]        

]

如此往复:

最终得到:

1['child'] =[

        2['child'] = [

             3['child'] = [

                 ..........

                100['child'] = ‘’

              ]

         ]        

]

与上面的版本一也是同理,传入顶级id,找到最小的分类,再从最小的分类开始自下而上逐级塞入child值,最后顶级id所在的数组就拿到了整个大数组数据。

版本二:

    public function test(array $data , $pid = 0){
		$array = [];
		foreach ($data as $key => $value) {
			if($value['pid'] == $pid){
				 $value['child'] = $this->test($data,$value['id']);
				 $array[]= $value;
			}
		}
		return $array;
	}

仔细查看版本一发现:最终都是将$value的数组塞入$array中,那只要得到当前分类以child为键名的子级数据再塞入$array中就好了呀,于是出现了

$value['child'] = $this->test($data,$value['id']);
 $array[]= $value;

 效果如下:

小影响:与第一版不同的是,键名元素都变成了0

 

版本三:

在版本二的基础上,加入了if判断是否有子级分类的数据,如果有才以child为键名,递归值为键值。

    public function test(array $data , $pid = 0){
		$array = [];
		foreach ($data as $key => $value) {
			if($value['pid'] == $pid){
				 $childData = $this->test($data,$value['id']); //得到孙子的数据
				 if(!empty($childData)){ //孙子的数据不为空,才把孙子的数据以child为键塞入儿子数组中
				 	$value['child'] = $childData;	
				 }
				 $array[]= $value;
			}
		}
		return $array;
	}

页面渲染完整代码:

最后因为网站导航一般都是双层关系,也就是只需要两个循环即可完成。一个循环一级分类,另一个循环子级分类,最后拼接html,返回前端渲染即可。

    <?php
/**
 * Created by phpStrom
 * User: Anbin
 * Date: 2022/8/31
 * Time: 16:48
 */


class Nav
{
    //测试数据
    const arr = [
        ['id' => 1, 'pid' => 0, 'city' => '江西'],
        ['id' => 2, 'pid' => 0, 'city' => '广东'],
        ['id' => 3, 'pid' => 1, 'city' => '宜春'],
        ['id' => 4, 'pid' => 3, 'city' => '丰城'],
        ['id' => 5, 'pid' => 4, 'city' => '尚庄'],
        ['id' => 6, 'pid' => 2, 'city' => '深圳'],
        ['id' => 7, 'pid' => 6, 'city' => '翻身'],
        #以下是追加的同级元素数据,用来试验论证,也是增强干扰性
        ['id' => 8, 'pid' => 1, 'city' => '南昌'],
        ['id' => 9, 'pid' => 8, 'city' => '高新区'],
    ];

    /**
     * 传入数据得到导航栏html
     * @param array $data
     * @return string
     */
    public function getNav(array $data):string
    {
        $str = '';
        foreach ($data as  $value) {
            $str .= '<h2>' . $value['city'] . '</h2>'; //导航一级菜单
            $str .= '<ul>';
            if (isset($value['child'])) { //二级菜单(若存在)
                foreach ($value['child'] as $k => $item) {
                    $str .= '<li>' . $item['city'] . '</li>';
                }
            }
            $str .= '</ul>';
        }
        return $str;
    }

    /**
     * 无限分类得到子孙关系大数组
     * @param array $data
     * @param int $pid
     * @return array
     */
    public function test(array $data, $pid = 0)
    {
        $array = [];
        foreach ($data as $value) {
            if ($value['pid'] == $pid) {
                $childData = $this->test($data, $value['id']); //得到孙子的数据
                if (!empty($childData)) { //孙子的数据不为空,才把孙子的数据以child为键塞入儿子数组中
                    $value['child'] = $childData;
                }
                $array[] = $value;
            }
        }
        return $array;
    }

    /**
     * 查看页面效果
     * @return string
     */
    public function index()
    {
        $res = $this->test(self::arr);
        if (!empty($res)) {
            return $this->getNav($res);
        }
    }
}
$a = new Nav();
echo $a->index();

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到