Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I'm using Bootstrap 3 to build a tab-panel component, based on information coming from the end-user. I don't like how there's a second foreach loop, but I don't know how else to print the HTML correctly.

Given an array structured like this:

[
    {
        "title": "Title 1",
        "content": "Lorem Ipsum"
    },
    {
        "title": "Title 2",
        "content": "Lorem Ipsum"
    }
]

And a "view" template structured like this:

<?php $panels = rwmb_meta("ssc_tabpanels") ?>
<?php if( !empty($panels) ): ?>
    <div id="program-details">
        <ul class="nav nav-pills nav-stacked" role="tablist">
            <?php foreach($panels as $i => $p): ?>
            <li role="presentation">
                <a href="#tab-<?php echo $i ?>" data-toggle="tab"><?php echo $p['title'] ?></a>
            </li>
            <?php endforeach; ?>
        </ul>

        <div class="tab-content">
            <?php foreach($panels as $i => $p): ?>
            <div class="tab-pane" id="tab-<?php echo $i ?>"><?php echo $p['content'] ?></div>
            <?php endforeach; ?>
        </div>
    </div>
<?php endif; ?>

What suggestions do you have for removing the second foreach loop while still maintaining the correct bootstrap code?

share|improve this question
1  
As we all want to make our code more efficient or improve it in one way or another, try to write a title that summarizes what your code does, not what you want to get out of a review. For examples of good titles, check out Best of Code Review 2014 - Best Question Title Category You may also want to read How to get the best value out of Code Review - Asking Questions. –  Quill Aug 20 at 13:56

3 Answers 3

One way would be to build the value of the tab contents in a variable during the first loop, and just print it once the loop ends. I have not written PHP in a while nor tested this code, so use as reference.

<?php $panels = rwmb_meta("ssc_tabpanels") ?>
<?php $tabContentHtm = ''; ?>
<?php if( !empty($panels) ): ?>
<div id="program-details">
    <ul class="nav nav-pills nav-stacked" role="tablist">
        <?php
        //build the tabs    
        foreach($panels as $i => $p):
        ?>
        <li role="presentation">
            <a href="#tab-<?php echo $i ?>" data-toggle="tab"><?php echo $p['title'] ?></a>
        </li>
        <?php
        // build content displayed on click of each tab
        $tabContentHtm .= '<div class="tab-pane" id="tab-'. $i .'">'. $p['content'] .'</div>\r\n';
        endforeach; 
        ?>
    </ul>

    <div class="tab-content">
        <?php echo $tabContentHtm; ?>
    </div>
</div>
<?php endif; ?>

The rendered code should look something like this

<div id="program-details">
    <ul class="nav nav-pills nav-stacked" role="tablist">
        <li role="presentation">
            <a href="#tab-1" data-toggle="tab">Title 1</a>
        </li>
        <li role="presentation">
            <a href="#tab-2" data-toggle="tab">Title 2</a>
        </li>
    </ul>

    <div class="tab-content">
        <div class="tab-pane" id="tab-1">Lorem Ipsum of Title 1</div>
        <div class="tab-pane" id="tab-2">Lorem Ipsum of Title 2</div>
    </div>
</div>
share|improve this answer

PHP also has a short open tag that I would use for echo.

<?= $p['content'] ?>
share|improve this answer
    
I'm stuck supporting 5.3 at the moment, so I can't guarantee that that it's available. 5.4 guarantees that. Thanks for the suggestion though! –  Goldentoa11 Aug 21 at 13:59

What I ended up doing was creating a function that re-ordered the array into a structure like

{
    "list-items": [ "<li ...><a ...>Title</a></li>", ... ],
    "pane-items": [ "<div ...>Content</div>", ... ]
}

And then in the theme I used implode("\n", $items['list-items']) and implode("\n", $items['pane-items']) where appropriate.

Code

/**
 * @param array $panels
 * @return array
 */
function restructure_panels( array $panels )
{

    $result = array(
        "list-items" => array(),
        "pane-items" => array()
    );

    $active = true;
    array_map(function ($k, $v) use (&$result, &$active) {

        if (!empty($v['title']) && !empty($v['content'])) {

            $classes = "";
            if ($active)
                $classes = " active";

            $result['list-items'][] = "<li role=\"presentation\" class=\"{$classes}\"><a href=\"#tab-{$k}\" data-toggle=\"tab\">{$v['title']}</a></li>";
            $result['pane-items'][] = "<div class=\"tab-pane {$classes}\" id=\"tab-{$k}\">{$v['content']}</div>";

            $active = false;
        }

    }, array_keys($panels), $panels);

    return $result;

}

// in single-program.php

<?php $panels = restructure_panels( rwmb_meta("ssc_tabpanels") ) ?>

<?php if( !empty($panels) ): ?>
    <div id="program-details" class="row">
        <div class="col-md-5">
            <ul class="nav nav-pills nav-stacked" role="tablist">
                 <?php echo implode( "\n", $panels['list-items'] ); ?>
            </ul>
        </div>

        <div class="col-md-7">
            <div class="tab-content">
                <?php echo implode( "\n", $panels['pane-items'] ); ?>
            </div>
        </div>
     </div>
<?php endif; ?>
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.