php - 大家是如何实现当前位置的??

 小新爱神起的小媳妇 发布于 2022-11-13 08:25

先看一下实际效果:

我的实现方式是:

1. 功能点API

2. 具体实现

3. 相关实现代码

    /*
     * 解析 URL 成菜单表中能够识别的 模块 + 子模块(或动作名称)名称
     */
    public function parseUrlToEazyName(){
        $controller = $GLOBALS['controller'];
        $action        = $GLOBALS['action'];
        $result     = [];
        $names      = [];
        
        $part = function($str = '' , $s_idx = 0) use (&$result , &$part , &$count){
            // 首字母小写:避免第一个大写,被单独取出
            $search = preg_match('/[A-Z]/' , $str , $rel , PREG_OFFSET_CAPTURE , $s_idx + 1); 
            $str_len = mb_strlen($str);
        

            if ($search === 0) {
                $e_idx = $str_len;
            } else {
                $e_idx = $rel[0][1];
            }
        

            $result[] = mb_substring($str , $s_idx , $e_idx);

            if ($str_len !== $e_idx) {
                $part($str , $e_idx);
            }

            return $result;
        };
    
        // 控制器
        $part($controller , 0);
        
        $names['controller'] = strtolower(implode('_' , $result));

        // 动作
        $result = [];
        
        $part($action , 0);

        $names['action']    = strtolower(implode('_' , $result));

        return $names;
    }

    // 当前位置
    public function getCurPos(){
        $names  = $this->parseUrlToEazyName();
        $action = $names['action'];
        $data   = [];
        $parent_list = [];

        // 找到当前元素所在 单元
        $find = function($name = '' , Array $list = []){
            $result = false;
                
            $do = function(Array $list = []) use(&$name , &$result , &$do){
                foreach ($list as $v)
                    {
                        if (!empty($v)) {
                            if ($v['name'] === $name) {
                                $result = $v;
                                return ;
                            } else {
                                if ($v['has_child']) {
                                    $do($v['child']);
                                }    
                            }
                        }
                    }
            };

            $do($list);

            return $result;
        };

        // 通过 底层项 找到整个链条
        $findParentTree = function(Array $item = [] , Array $list = []) use(&$data , &$find , &$parent_list){
            $data            = $item;
            $parent            = $item['parent'];
            $parent_list[]  = $item['name'];

            if (!empty($parent)) {  
                $parent_list[]  = $parent;
            }

            // 由于是最底层的项,所以即使其有子项,也必须清空,但是 has_child 不修改。
            $data['child']  = [];
            $result            = [];

            while ($parent)
                {    
                    $result = $find($parent , $list);

                    if (!empty($result)) {
                        $result['has_child'] = true;
                        $result['child']     = $data;

                        $data    = $result;
                        $item   = $data;
                        $parent = $data['parent'];
                        $parent_list[]  = $parent;
                    }
                }

            return $data;
        };

        // 通过给定的菜单名称,找到其所有的子元素,如果没有的话,返回空数组
        $findTopModuleNextTree = function($name = '' , Array $list = []){
            foreach ($list as $v)
                {
                    if ($v['name'] === $name) {
                        return $v['child'];
                    }
                }

            return false;
        };

        // 生成 HTML 
        $genHTML = function(Array $list = []){
            $html = '';

            $gen = function(Array $item = []) use(&$html , &$gen){
                $html .= "" . $item['cn_explain'] . "/";

                if (!empty($item['child'])) {
                    $gen($item['child']);
                }
            };
            
            $gen($list);

            return $html;
        };
        
        $result = $find($action , $GLOBALS['menu']);
        
        if ($result === false) {
            throw new \Exception('菜单中找不到当前动作路径');
        }

        $result = $findParentTree($result , $GLOBALS['menu']);
        
        // 将当前选中的顶级模块赋值进模板
        $this->assign('top_module' , $result);
    
        $top_module_next_tree = $findTopModuleNextTree($result['name'] , $GLOBALS['menu']);

        // 当前模块的对应的二级菜单模块
        foreach ($parent_list as $v)
            {
                foreach ($top_module_next_tree as $v1)
                    {
                        if (!empty($v1)) {
                            if (isset($v1['name']) && $v === $v1['name']) {
                                // 将顶级模块的二级模块赋值进模板
                                $this->assign('c_menu' , $v);
                                break;
                            }
                        }
                    }
            }

        $html   = $genHTML($result);
        
        $lc = mb_substr($html , mb_strlen($html) - 1);
        
        if ($lc === '/') {
            $html = mb_substr($html , 0 , -1);
        }

        return $html;
    }

上面的实现方式有几个缺陷:

  1. 由于不是使用 数字型,而是使用 文字型,导致在功能点命名上要求绝对不能有重复名称的。

  2. 如果API链接上要求带上动态参数,则无法实现。

  3. 功能点多的情况下,显得好庞杂的样子(是采用存入数据库表的方式,还是直接像我一样另起一个文件保存??)

不知道大家是怎样实现这个功能的呢??

因为这实际上跟架构有很大的关系,劳烦有经验的大牛,亮出你的 style3q


感谢:BinotaLIU 的回答,补充说明下函数的相关作用...

1. parseUrlToEazyName 函数的作用:

由于我的 URL模式PATHINFO 的,所以,他的表现形式是:index.php/Module/Controller/Action ,具体例子是:index.php/ControlPannel/rootIndex ,表示执行的是 ControlPannel类的rootIndex方法,但需要实现 当前位置 的这个功能的时候,大家也看到了我的 menu.php 中的内容,他里面的命名方法是 control_pannel 或 root_index 这种的,所以,为了在 menu.php 中找到对应的项,需要对路径做一个解析。这就是 parseUrlToEazyName 的作用了。

2. getCurPos 函数的作用:,他有多个细化的功能点:

1. $find 函数,从 menu.php 中查找到当前提供功能对应的项。
2. $findParentTree 函数,有两个作用:返回当前功能所在链条的顶级项($top_module) + 生成当前功能的所有父级项($parent_list)
3. $findTopModuleNextTree 函数,实际上就是获取当前项的所有子项
4. $genHTML ,生成的是当前位置的HTML:`活动管理\喵喵抢购\活动列表\产品列表`

$findParentTree 他的具体功效是,其一,是无论你深入到一个模块的哪一个层级,顶级模块始终会展开(返回$top_module):

$findParentTree 他的具体功效是,其二,由于左边的菜单属于二级菜单(不能无限极分类),所以需要找到顶级模块的下一级对应模块,我想到办法是,找到底层项的所有父级项($parent_list),找到顶级项的所有子项,在在子项看存在哪个父项(绝对只有一个存在),那个就是对应的二级菜单项了,选中:

2 个回答
  • 可能因为我是前端,这部分由前端做的比较多。

    • 如果是SPA(当然问题中并不是),跟前端路由匹配。

    • 如果不同的controller对应不同的layout的话,根据action区分显示就行。

    • 如果没有layout这个概念,就根据url地址做正则匹配,或是根据controller和action来显示

    2022-11-13 08:26 回答
  • 你的实现方法很有趣,但并不实际。

    首先你写的代码实在很难读,问问题又不写清楚,并不是所有人都有时间慢慢读你的代码来理解你的问题。
    或许可以先试着说明一下 1. 自己的代码做了哪些事情,2. 目前实现了哪些功能,3. 期望改进或增加什么功能

    首先先来针对你提出的两个函数提出一些个人的想法,接着再说点整体的点评吧!

    parseUrlToEazyName

    想了想,$GLOBALS['controller'] 应该是一组小驼峰法的字符串。
    既然都用正则来处理了,是否该用更有效率的方法呢?

      public function parseUrlToEazyName()
      {
          $converCamelStr = function ($str) {
              //若首字母大写会被转成 _ 开头的字符串,这时候 ltrim 可以清除开头的 _
              return ltrim(strtolower(preg_replace('/([A-Z])/', '_$1', $str)), '_');
          };
    
          $names = [];
          $names['controller'] = $convertCamelStr($GLOBALS['controller']);
          $names['action'] = $convertCamelStr($GLOBALS['action']);
    
          return $names;
      }

    getCurPos

    你写的实在太复杂了,注解又不够多,看了好多遍还是不懂你在做什么。
    你自己解释一下你的代码实现了什么。

    我想这里出现了几个问题:

    1. 你是通过 parent 这项的字符串来找出父级元素的,这样其实很危险,因为若要修改父级元素的 name,就必须把所有子项的 parent 都改掉,既然都使用了巢状结构的 array,其实并不需要再特地表示出 parent,因为这并不是一个 RMDB 的数据库。

    2. $findParentTree() 当中,如果 while($parent) 中的 $result 为空会怎么样呢?=> 没做例外处理

    3. 你不觉得你的代码会跑很多次回圈吗?

    整体点评

    • 分工做的并不好,我的意思是说,你或许需要了解一下 SOLID 原则。各个方法间的耦合度太高了。

    • 类似下列的代码:

      foreach ($list as $i) {
        if (!empty($i)) {
          // 做点复杂的事情
        }
      }
      其实是可以改成这样的:
      foreach ($list as $i) {
        if (empty($i)) continue;
        // 做点复杂的事情
      }

      如此一来就可以避免一个 if 的 block 太过肥大,也可以避免最后出现太多层的花括号。

    • 对 PHP 的原生函数或许还不熟,加油!

    针对追问

    你们在定义 功能API(系统功能层级) 时,是采用存入数据库表的方式(增删功能点不是很方便),还是用一个文件存起来(方便快捷,就是庞杂,看起来心累)??

    通常是存数据库的,这样才能在后台修改。改文件的话会有很多问题。

    跑很多次回圈??指的是很多次循环吗??我看过好几遍了...没有多余的步骤啊....估计我眼睛蒙圈了,没发现....大写的 SOS。。。。。

    意思是说,在 getCurPos 里有太多的 foreach 了,只是判断个链接,还需要把整个 $manu 遍历好多次,这样很没效率。

    由于这些功能API定义的链接都是固定的,如果有些模块需要带上动态参数的,例如:活动管理喵喵抢购活动列表产品列表 中,产品列表 这个功能的链接中是需要带上参数的(活动ID),查看的是某个活动的产品列表,这个问题怎么破??

    我想这个问题大部分都是交给 Framework 在处理的,依照你目前的架构也很难修改。建议你还是找点轻量级的 Framework 来研究一下。

    2022-11-13 08:26 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有