Body classes based on URL

A bit of forewarning, I’m a relative newcomer to Yii and hence not sure if something like this already exists, whether it be an extension or even part of the core - if it is, please let me know.

I come from a bit of a Drupal background, and I really like hey they add classes to your body tag based on the page you’re visiting. For instance, if you’re on a page with URI /page/episode/01, your body tag may look like:


<body class="html not-front not-logged-in no-sidebars page-episode page-episode- page-episode-01 episode" >

It lets you effectively style different views, content types, etc. individually all from the one stylesheet without effecting different components. I had a quick search for something in Yii, but unable to find something, quickly threw something together.

In protected/components/controller.php, add:


	/**

	 * @var string the classes that should be displayed in the body element of each page.

	 */

	public $classes;


	/**

	 * Make sure we run the parent's constructor method, and call the function to calculate

	 * what classes to use.

	 */

	public function  __construct($id,$module=null) {

		parent::__construct($id, $module);

		$this->getBodyClasses();

	}


	/** 

	 * For easier styling, let's insert some classes from the URI in to our body element

	 */

	public function getBodyClasses() {

		if (! empty(Yii::app()->baseUrl)) {

			$uri = explode(Yii::app()->baseUrl, Yii::app()->request->requestUri);

			unset($uri[0]);

			$components = explode("/", implode(Yii::app()->baseUrl, $uri));

		}

		else {

			$components = explode("/", Yii::app()->request->requestUri);

		}


		foreach ($components as $id => $component) {

			if (empty($component)) {

				unset($components[$id]);

			}

		}

		ksort($components);


		$class = '';

		for ($x = 1; $x <= sizeOf($components); $x++) {

			if ($x <> 1) {

				$class .= '-';

			}

			$class .= $components[$x];

			$classes[] = strip_tags($class);

		}


		if (isset($classes)) {

			$this->classes = implode(' ', $classes);

		}

	}

And then in your layout view just add:

[html]<body class="<?php echo isset($this->classes) ? $this->classes : ‘’ ?>">[/html]

And then if you were on:


example.com/forum/example/news/welcome-topic-1

Your body tag would look like:


<body class="forum forum-example forum-example-news forum-mixxi-news-welcome-mixxi-1">

Allowing you to style elements on the forum, or for that particular parent forum (and child forum), or even the topic if you wanted.

Again, if anybody can think of a more efficient way, please let me know.

Hi [member=‘artificial’], I like that Drupal adds classes to the body tag that are extremely useful for theming.

In fact, most people accuse Drupal of ‘classitis’ - the habit of adding multiple classes to every tag!

I made a few modifications to your code.

Modify the layout view, so that it includes a class on the front/home page too.


<body class="<?php echo isset($this->classes) ? $this->classes : 'front' ?>">

I also made a modification to the generation of $components


       

        /**

         * For easier styling, let's insert some classes from the URI in to our body element

         */

        public function getBodyClasses() {

                if (! empty(Yii::app()->baseUrl))

                {

                        $uri = explode(Yii::app()->baseUrl, Yii::app()->request->requestUri);

                        unset($uri[0]);

                        $pattern = '/[:\/?=]/';

                        $standardDelimiter = ':';

                        $string = implode(Yii::app()->baseUrl, $uri);

                        // replace delimiters with standard delimiter, also removing redundant delimiters

                        $string = preg_replace(array($pattern, "/{$standardDelimiter}+/s"), $standardDelimiter, $string);

                        $components = explode($standardDelimiter, $string);

                }

                else

                /*

                 * Rest of the code

                 */



This way, if you were on


example.com/site/page?view=about

your body tag would be

[html]<body class="site site-page site-page-view site-page-view-about">[/html]

rather than

[html]<body class="site site-page?view-about">[/html]

Hey guys,

I found this really useful, so I turned it into a behavior. More details on my GitHub page:

I was going to add it as an extension, but I don’t have enough posts on the forum to allow me to do that yet :-/ I’ll put it up at some point …

Oh, and thanks for the code!

@Nick

I’m glad that someone found it useful. I’ll soon update my original post with your amendments (and of course give you credit) in case anybody else wants to take that approach. I actually never bother with that URL structure which explains why I overlooked it :P

@fr0d0z

It looks great. I’ll take a look when I get home and if it all works I’ll throw a link at the top of my post, as it’s obviously a better way to go about it.

I just write better ext for this task. You can see the code there: http://www.yiiframework.com/extension/yii-body-classes/

Nice idea :)