如何使用strpos($ _ SERVER ['REQUEST_URI']从url段生成活动类

We've found a lot of answers for this but don't fully understand them (or the strpos manual page) so sorry if this is already answered.


Setup:

Consider the following urls...

http://www.domain.com/whats-on/show-name/   
http://www.domain.com/whats-on/show-name/events

And how we're applying the .active class in the following list item markup...

<li>
    <a class="<?= (strpos($_SERVER['REQUEST_URI'], '/whats-on') === 0 ? 'active' : ''); ?>" href="<?= $ShowWhatsOn; ?>">What's on</a>
</li>
<li>
    <a class="<?= (strpos($_SERVER['REQUEST_URI'], '/events') === 0 ? 'active' : ''); ?>" href="<?= $ShowEvents; ?>">Events</a>
</li>

Issues:

  1. What's On gets the active class on the What's On page but also on the Events page because /whats-on is also in the url: /whats-on/show-name/events

  2. Events never receives the active class


Question:

  1. How do we get strpos to check specific url segments so the .active class is applied on the correct page?

We're trying to keep this short for the menu markup so hopefully there's a way to do this on one line?

Any help or pointers in the right direction would be much appreciated.

Cheers

Ben

strpos gives you the position of a string (needle) in another string (haystack). So by doing

(strpos($_SERVER['REQUEST_URI'], '/whats-on') === 0

You are checking if the REQUEST_URI starts with "/whats-on" (is at position 0). Since both URLs start with "/whats-on", the first item will always be active and the second never.

One fix would be to add a check for "/events":

<li>
    <a class="<?= (strpos($_SERVER['REQUEST_URI'], '/whats-on') === 0 && strpos($_SERVER['REQUEST_URI'], '/events') === false ? 'active' : ''); ?>" href="<?= $ShowWhatsOn; ?>">What's on</a>
</li>
<li>
    <a class="<?= (strpos($_SERVER['REQUEST_URI'], '/whats-on') === 0 && strpos($_SERVER['REQUEST_URI'], '/events') !== false ? 'active' : ''); ?>" href="<?= $ShowEvents; ?>">Events</a>
</li>

Although this gets a bit verbose for your template, so you may want to break the logic out into a function like isActive().

OK, for my future reference (and others) this is how I solved it.

1. Make two functions

I made this in the /helpers/ directory of my CMS and called it npe_nav.php (if using the Concrete5 CMS, don't name the file navigation.php as that name is taken by c5 so didn't work for me).

// Get URL segments
public function getURLSegments() {
    return explode("/", parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH));
}

// Get a specific URL segment
// Takes a number which corisponds to a segment in the URL e.g. consider:
// http://www.domain.com/whats-on/show-name/events
// $navHelper->getURLSegment(1); will return whats-on
// $navHelper->getURLSegment(3); will return events
public function getURLSegment($n) {
    $segs = $this->getURLSegments();
    return count($segs) > 0 && count($segs) >= ($n-1)?$segs[$n]:'';
}

2. Load the function in your template

In Concrete5, I loaded it like this (package-name refers to the package this tools is saved in within the CMS):

// Load nav helper
$navHelper = Loader::helper('npe_nav','package-name');

3. Call the function in your template

For the URL: http://www.domain.com/whats-on/show-name/

This also checks if the third segment is null before applying .active because /whats-on/ appears in all URLs in this section - so don't want it to be active on other pages.

<li>
    <a class="<?= (($navHelper->getURLSegment(1) == 'whats-on') && (is_null($navHelper->getURLSegment(3))) ? 'active' : ''); ?>" href="<?= $ShowWhatsOn; ?>">What's on</a>
</li>
For the URL: http://www.domain.com/whats-on/show-name/events

And other pages at segment 3

<li>
    <a class="<?= ($navHelper->getURLSegment(3) == 'events' ? 'active' : ''); ?>" href="<?= $ShowEvents; ?>">Events</a>
</li>