doumaqing6652 2016-04-13 13:26
浏览 34

CodeIgniter - 3级菜单的数组

My main developer is away so I'm stretching my capabilities as I'm not a programmer, competent to a level but not great, so bear with me.

We have a 2-level menu builder. It needs a 3rd level. Framework is CI.

DB table is 'pages_nav' Fields = navid (unique) | pageid | parentid | entry_type | target | order

Relates to table called 'pages' - I join pages_nav and pages to get URL and title

Entry_type is 1, 2 or 3 (top level, sub item or menu divider (horizonal rule/break between menu items)

DB lookup is:

$menu = $this->db
      ->select('P.id, P.title, P.url, N.entry_type, N.target, N.order, N.parentid, N.navid')
      ->join('pages P', 'P.id = N.pageid', 'left')
      ->order_by('N.parentid, N.order')
      ->get('pages_nav N')
      ->result_array();

Then I'm getting an array from this (all in the model), looping and passing back if parent or child like this:

if(!$menu) return [];
    $menu_out = [];
    foreach($menu as $item) {
      if($item['parentid'] == 0) {
        $menu_out[$item['id']] = ['details'=>$item, 'children'=>[] ];
      } else {
        $menu_out[$item['parentid']]['children'][] = $item;
      }
    }
    return $menu_out;

In the controller I'm just calling the model and passing $data['menu_items'] to the view as $data.

In the view i've got a foreach inside a foreach to print out the parents and any children, like this:

<?php if(!empty($menu_items)): ?>
    <?php foreach($menu_items as $item): ?>
        [ PRINT OUT ROWS: title, URL, Order etc]
        <?php if(count($item['children'])): ?>
            <?php foreach($item['children'] as $child): ?>
                [ PRINT OUT ROWS of child items: title, URL, Order etc]
            <?php endforeach; ?>
        <?php endif; ?>
    <?php endforeach; ?>
<?php else: ?>
    [ no entries found text ]
<?php endif; ?>

I don't want to change the DB with exception of adding a column if necessary. I don't want to totally re-build the code and I don't need recursive of any more levels than 3 (100% definite on that) so what I need is a stress-free, simple edit to what I have to allow a 3rd level but whatever I try I'm failing miserably so I'm looking for some direction.

What I've been looking at is having entry_type = 4 for 3rd level as an identifier but I'm struggling...

EDIT -- Now adding my View in to help:

<?php if(!empty($menu_items)): ?>
    <?php foreach($menu_items as $item): ?>
            <?php
            if($item['details']['target']==0)
                $link_target = "Current";
            else
                $link_target = "New";
            ?>
            <tr class="parent">
                <td><span class="anchor" data-id="<?php echo $item['details']['id']; ?>"><i class="fa fa-chevron-down <?php echo count($item['children']) ? '' : 'off'; ?>"></i> <?php echo $item['details']['title']; ?></span></td>
                <td><?php echo $item['details']['url']; ?></td>
                <td><?php echo $item['details']['order']; ?></td>
                <td><?php echo $link_target; ?> Tab</td>
            </tr>
            <?php if(count($item['children'])): ?>
                <?php foreach($item['children'] as $child): ?>
                    <?php
                    if($child['target']==0)
                        $link_target = "Current";
                    else
                        $link_target = "New";
                    ?>
                    <tr class="child">
                        <?php if($child['entry_type'] == 3): ?>
                            <td colspan>-- DIVIDER --</td>                                  
                        <?php else: ?>
                            <td><?php echo $child['title']; ?></td>
                        <?php endif; ?>
                        <td><?php echo $child['url']; ?></td>
                        <td><?php echo $child['order']; ?></td>
                        <td><?php echo $link_target; ?> Tab</td>
                    </tr>
                <?php endforeach; ?>
            <?php endif; ?>
    <?php endforeach; ?>
<?php else: ?>
    <tr>
        <td colspan="6">No navigation entries</td>
    </tr>
<?php endif; ?>

Here's the entries in the db. The last one is the 3rd level item, relating to two rows above (2nd level), which relates to rows above that (parent/top level).

enter image description here

So there's 3 top level items, the last of which has 3 children (1 is a divider line), and one of those children has a sub-child (or child itself).

  • 写回答

1条回答 默认 最新

  • duandong9195 2016-04-13 14:46
    关注

    I imagine you're looking at something like the following:

    Model

    class Db_lookup extends CI_Model {
        function __construct() { parent::__construct(); }
        function callDBMenuItems() {
            //  this is simply your call to the database
            //  it retrieves *rows* (array entries) of items that will be like:
            //      index (0 based) [ 'id', 'title', 'url', 'entry_type', 'target', 'order', 'parentid', 'navid' ]
            $menu = $this->db
                ->select('P.id, P.title, P.url, N.entry_type, N.target, N.order, N.parentid, N.navid')
                ->join('pages P', 'P.id = N.pageid', 'left')
                ->order_by('N.parentid, N.order')
                ->get('pages_nav N')
                ->result_array();
        }
        function getMenuItems() {
            $menu = $this->callDBMenuItems();
            if(!$menu) return [];
            //  menu items were found, create an array to put results in
            $menu_out = [];
            foreach($menu as $item) {   //  loops through array
                //  check if adding first entry
                //  by the looks, I assume all top level (1st level)
                //  items have a parent id of '0'
                //  i'm guessing this is to signify they dont have a parent
                if($item['parentid'] == 0) {
                    //  add entry to menu_out array, with 2 values
                    //  'details' will contain the entire item
                    //  'children' looks like it is preped for the else statement
                    $menu_out[$item['id']] = ['details'=>$item, 'children'=>[] ];
                } 
                else {
                    //  here, it appears we're adding sublevel (level 2) values
                    //  level 2 items obviously have a parent id that is not 0
                    //  and relates to the item id of an item that 
                    //  had a parent id of 0 (aka, this item's parent)
                    //  again, but to different calue, this everty's 
                    //  value of 'children'.
                    //  will gain an array entry of the entire item
                    //  this will create and array of children all containing entire items
                    $menu_out[$item['parentid']]['children'][] = $item;
                }
            }
            //  our final results, if items found, would in turn look something like:
            /*
                array $menu_out => [
                    'id0' => [
                        'details' => [ 'id', 'title', 'url', 'entry_type', 'target', 'order', 'parentid', 'navid' ],
                        'children' => [
                            [0] => [ 'id', 'title', 'url', 'entry_type', 'target', 'order', 'parentid', 'navid' ],
                            [1] => [ 'id', 'title', 'url', 'entry_type', 'target', 'order', 'parentid', 'navid' ]
                        ]
                    ],
                    'id1' => [
                        'details' => [ 'id', 'title', 'url', 'entry_type', 'target', 'order', 'parentid', 'navid' ],
                        'children' => [
                            [0] => [ 'id', 'title', 'url', 'entry_type', 'target', 'order', 'parentid', 'navid' ],
                            [1] => [ 'id', 'title', 'url', 'entry_type', 'target', 'order', 'parentid', 'navid' ]
                        ]
                    ]
                ]
            */
            return $menu_out;
        }
    }
    

    Controller

    class Page extends CI_Controller {
        function index() {
            //  simmply loads the previously created model,
            // however, you might not see this if your programmer 
            //  was smart enough to add it to the auto-load feature of CI
            $this->load->model('Db_lookup');
    
            /*  this is the array used to pass php items to the view
                each item can be called in the view by using it's
                associative name
                for example, if the array has an entry like [ 'bob' => 'bill' ],
                then in the view, you can get 'bob' simply by calling:
                <?php echo $bob; ?>
                just think of each entry as a declaration of a PHP variable, 
                without the $ sign. You then simply add the $ sign to that item's name
                in the view in order to get that item.  */
            $data = [];
            //  here we add an item to our array that will be passed to the view
            //  note how the entry is 'named'. This is the value we'll 
            //  use to call it in the view
            $data['menu_items'] = $this->Db_lookup->getMenuItems();
            //  load the 'view' (aka, the page you see in the browser)
            //  and pass our data array to it
            $this->load->view('pageName', $data);
        }
    }
    

    View

    <!--    as noted before, you can call items from the data array passed through
        simply by adding $ sign to the begining of the item's index name.   -->
    <?php if(!empty($menu_items)): ?>
        <ul>
        <!--    since we know `menu_items` is an array, as previously defined in our model,
                then we know we can loop for it based on the creation of it in our model    -->
        <?php foreach($menu_items as $item): ?>
            <!--    [ PRINT OUT ROWS: title, URL, Order etc]    -->
            <?php if(count($item['children'])): ?>
                <!--    I don't see where you've created actual HTML yet, 
                    but I assume you're looking for something like: -->
                <li id="order_<?php echo $item['order']; ?>">
                    <a href="<?php echo $item['url']; ?>"><?php echo $item['title']; ?></a>
                    <ul>
                    <?php foreach($item['children'] as $child): ?>
                        <!--    [ PRINT OUT ROWS of child items: title, URL, Order etc] -->
                        <!--    Again, I don't see any HTML you've created, 
                            but this may be something like: -->
                        <li id="order_<?php echo $child['order']; ?>">
                            <a href="<?php echo $child['url']; ?>"><?php echo $child['title']; ?></a>
                        </li>
                    <?php endforeach; ?>
                    </ul>
                </li>
            <?php endif; ?>
        <?php endforeach; ?>
        </ul>
    <?php else: ?>
        <!--    [ no entries found text ]   -->
        <p>Sorry, No Menu Found</p>
    <?php endif; ?>
    

    The Fix

    In trying to think of the most "headache free" way to handle this, I decided you'd need to make a few small changes throughout the process.

    First of all, you'll need to make some small changes to expected array. Not sure if that model is used in other places (i imagine it is), so we won't change the model. Instead, we'll start at changing the data in the controller. This way, you can pass a new array, with 3 levels of data, to the view.

    Controller

    function index() {
        $this->load->model('Db_lookup');
    
        //  first let's get the uncut menu_items
        $menu_items = $this->Db_lookup->getMenuItems();
        //  now let's break them down and build a new
        //  array to pass to the view.
        //  this could be done even more easy by changing
        //  the model, however, it's unknown (to me at least)
        //  if that model is used in other places.
        //  it likely is, so let's just modify here instead.
        //  first i'll setup 3 simple arrays. This isn't
        //  the only way to do this, and could be made more
        //  condensed, but for sake of less headache,
        //  i'm breaking it into a visually easy to see array
        //  of the items we want.
        $parent_items = [];
        //  let's make this a little easier, 
        //  if you look back to the array returned from the model,
        //  you'll notice each item should already have and index ID,
        //  so I'll use that in the foreach loop
        foreach ($menu_items as $id => $item) {
            //  at this point, the following 'if' should always be true
            if(isset($item['details']) && $item['details']['parentid'] == 0) {
                //  setup array with easily referable id
                //  mimicking original setup
                $parent_items[$id] = [
                    'details' => $item['details'],
                    'children' => []    //  empty children, since we want to change how children is handled
                    //  by adding a level 3 component
                ];
                //  first, let's get this parent's child items,
                //  but we only want the one's that don't have
                //  `entry_type=4`
                if (!empty($item['children'])) {
                    //  let's make this easier to follow
                    $uncutChildren = $item['children'];
                    //  and make an easy child array to work with,
                    //  that will later add back to parent
                    $newChildren = [];
    
                    //  first let's add the level 2 (children) only
                    //  using an $i loop may not have been best approach,
                    //  so instead, I'll make a retlational ID,
                    //  based on info within the child item
                    foreach ($uncutChildren as $child) {
                        //  $child should look like:
                        //  [ 'id', 'title', 'url', 'entry_type', 'target', 'order', 'parentid', 'navid' ]
    
                        //  if i'm reading the output of the view you posted correctly,
                        //  entry_type 3 is always between your expected children,
                        //  so I assume, every child between 3's should be a level 2,
                        //  OR a level 3 of the previously pulled 2,
                        $childID = count($newChildren);
                        //   with that in mind, try this
                        switch ($child['entry_type']) {
                            case 2:
                            case 3: // adds new entry
                                $newChildren[$childID] = $child;
                                break;
                            case 4:
                                if (!empty($newChildren[$childID-1])) $newChildren[$childID-1]['level3'] = $child;
                                break;
                        }
                    }
    
                    //  now add in our new children
                    $parent_items[$id]['children'] = $newChildren;
                }
            }
        }
        // this should help make a new array that looks like:
        /*
            array $menu_out => [
                'id0' => [
                    'details' => [ 'id', 'title', 'url', 'entry_type', 'target', 'order', 'parentid', 'navid' ],
                    'children' => [
                        [0] => [ 'id', 'title', 'url', 'entry_type', 'target', 'order', 'parentid', 'navid',
                            'level3' => [ 'id', 'title', 'url', 'entry_type', 'target', 'order', 'parentid', 'navid' ]
                        ]
                    ]
                ]
            ]
        */
        $data = [ 'menu_items' => $parent_items ];
        $this->load->view('pageName', $data);
    }
    

    Now you should have a new level to work with in your view!

    <?php if(!empty($menu_items)): ?>
        <?php foreach($menu_items as $item): ?>
                <?php $link_target = $item['details']['target']==0 ? "Current" : "New"; ?>
                <tr class="parent">
                    <td>
                        <span class="anchor" data-id="<?php echo $item['details']['id']; ?>">
                            <i class="fa fa-chevron-down <?php echo count($item['children']) ? '' : 'off'; ?>"></i> <?php echo $item['details']['title']; ?>
                        </span>
                    </td>
                    <td><?php echo $item['details']['url']; ?></td>
                    <td><?php echo $item['details']['order']; ?></td>
                    <td><?php echo $link_target; ?> Tab</td>
                </tr>
                <?php if(count($item['children'])): ?>
                    <?php foreach($item['children'] as $child): ?>
                        <?php $link_target = $child['target']==0 ? "Current" : "New"; ?>
                <tr class="child">
                    <?php if($child['entry_type'] == 3): ?>
                        <td colspan>-- DIVIDER --</td>                                
                    <?php else: ?>
                        <td><?php echo $child['title']; ?></td>
                    <?php endif; ?>
                    <td><?php echo $child['url']; ?></td>
                    <td><?php echo $child['order']; ?></td>
                    <td><?php echo $link_target; ?> Tab</td>
                </tr>
                <!--    ¡¡¡NEW LEVEL!!! -->
                <!--    Not really sure where you want it, so I just created a new row,
                        with a colored background so you can "see" the change   -->
                        <? php if (!empty($child['level3'])): ?>
                            <?php foreach($child['level3'] as $lvl3): ?>
                            <?php $link_target = $lvl3['target']==0 ? "Current" : "New"; ?>
                <tr style="background: yellow;">
                    <td><?php echo $lvl3['title']; ?></td>
                    <td><?php echo $lvl3['url']; ?></td>
                    <td><?php echo $lvl3['order']; ?></td>
                    <td><?php echo $link_target; ?> Tab</td>
                </tr>
                            <?php endforeach; ?>
                        <?php endif; ?>
                    <?php endforeach; ?>
                <?php endif; ?>
        <?php endforeach; ?>
    <?php else: ?>
        <tr>
            <td colspan="6">No navigation entries</td>
        </tr>
    <?php endif; ?>
    

    I'm working back and forth between a bunch of my own real work and this, so just comment if something didn't quite turn out right, and i'll try to fix it! Good luck!

    评论

报告相同问题?

悬赏问题

  • ¥15 关于#matlab#的问题:在模糊控制器中选出线路信息,在simulink中根据线路信息生成速度时间目标曲线(初速度为20m/s,15秒后减为0的速度时间图像)我想问线路信息是什么
  • ¥15 banner广告展示设置多少时间不怎么会消耗用户价值
  • ¥16 mybatis的代理对象无法通过@Autowired装填
  • ¥15 可见光定位matlab仿真
  • ¥15 arduino 四自由度机械臂
  • ¥15 wordpress 产品图片 GIF 没法显示
  • ¥15 求三国群英传pl国战时间的修改方法
  • ¥15 matlab代码代写,需写出详细代码,代价私
  • ¥15 ROS系统搭建请教(跨境电商用途)
  • ¥15 AIC3204的示例代码有吗,想用AIC3204测量血氧,找不到相关的代码。