Recent Topics

nice URLs for categories

Started by on Jan 23, 2007 – Contents updated: Jan 23, 2007

Jan 23, 2007 17:38    

I'm running 1.91.1-dev for some time now and everything is going fine.

To the point: t would be really, really nice to be able to have clean URLs or nice URLs for categories, in a similar fashion as we have clean URLs for posts Wordpress has it, and tags also, but that's not my motivation). Right now, categories are accessed going to urls of the form 'example.com/stub?cat=n' where 'n' is the 'cat_ID'. What I would like to have is something of the sort: 'example.com/stub/cat/cat_nice_name'; where 'cat_nice_name' is the category name corresponding to cat_ID=n.

I guess I could do something like that using the .htaccess to setup rewriting rules that translate the the 'cat_nice_name' to the correct cat_ID numbers, but this suffers from several problems:

1.When you have more than a bunch of categories, it is cumbersome to mantain the mappings and prone to errors.
2.The list of categories in the sidebar and in the post footer would still be in the old 'cat=n' format.

Inspecting the database, there are a couple of columns in the 'evo_categories' table that are not being used, namely 'cat_description' and 'cat_longdesc' and that could be used to store the categories' cat_nice_name, in a similar way the post_urltitle does the job for individual posts.

We would need to have a several functions to translate cat_nice_names into cat_IDs and also we have to deal with it when resolving the extra-path information. Also we would need to produce the 'nice_cat_names' when creating categories in the backoffice, in a similar way the post_urltitle are automagically created when posting.

From the distance it seems a not so difficult problem. But I don't know where to start.

This feature I think would help improve our positions in the search engine results rankings and is overall more user-friendly.

Any ideas? help? comments?

Jan 23, 2007 18:15

Search the Docs section of the site and/or the forum for things regarding the stub files. You'll find the anser to youre wuestion there.

Good luck

Jan 23, 2007 21:27

Not really, Afwas. Categories aren't displayed nicely in the URL yet.
But this already implemented in 2.0-dev (the latest CVS).

Jan 23, 2007 21:41

I have a semi-working-preliminary solution to the problem.

What I did:

1.- insert into the cat_description column in the evo_categories table suitable short names for each category (I used phpMyAdmin to accomplish this). In the example blog I'm working on I have: cat_ID = 15, 16, 17 and cat_description = "anuncios", "propuestas", "en_la_prensa", respectively.

2.- modify inc/_blog_main.inc.php in the following way, at line 303:

elseif ( preg_match( "#^categorias$#", $path_elements[$i] ) )
                        {
                                $cat_sql = "SELECT cat_ID FROM evo_categories WHERE cat_description = '" . $path_elements[$i+1] . "'";
                                $cat_row = $DB->get_row($cat_sql) ;
                                if ( empty( $cat_row ) )
                                {
                                        $cat = 0 ;
                                }
                                else
                                {
                                        $cat = $cat_row->cat_ID ;
                                }

                                $Debuglog->add( 'Setting category from extra path info. $cat=' . $cat , 'params' );
                        }

In the original extra-path decoding routine, the first thing checked after "index.php" is a numeric string, corresponding to the year. If this fails, the code returns a 404 Not Found response code. What I'm doing is allowing for this extra-path component to be also a string, in this case "categorias" (categories in spanish). Then I get the next path_element and look it up in evo_categories to see if there's a matching cat_ID, which is then set as the $cat parameter.

YET TO DO:
1.- automatic asignment of the "cat_description" from the "cat_name" field when the category is created in the backoffice, in a similar fashion as is done when one is creating a post with the "post_urltitle" column of the evo_posts table.
2.- Clean the code, sanitize input before feeding it to the SQL query and stuff like that, about which I have no Idea.
3.- Modify the Categories plugin to display the proper urls in the sidebar.
4.- Modify the evo_categories table to ensure the cat_description field is unique, as the "post_urltitle".

What else?

Yo can see it in action here (I have debugging enabled in case someone wants to check it out)

http://ningunopresidente.com/index.php/categorias/anuncios (cat_ID=15)
http://ningunopresidente.com/index.php/categorias/propuestas (cat_ID=16)
http://ningunopresidente.com/index.php/categorias/en_la_prensa (cat_ID=17)

Jan 24, 2007 20:43

Looks fine. But you should use $DB->quote() or $DB->escape() around $path_elements[$i+1] in the SQL!

Jan 24, 2007 23:47

Ok, I really don't believe it, but it seems to be working.

Following blueyed advice I used $DB->escape for the SQL query, now it reads:

$cat_sql = "SELECT cat_ID FROM evo_categories WHERE cat_description = '" . $DB->escape( $path_elements[$i+1] ) . "'";

The next thing I did was to modify the function categories(), which displays links to each of the categories a particular item belongs to. This is located in inc/MODEL/items/_item.class.php and changed line 678 where it reads:

$cat_name = '<a href="'.url_add_param( $lBlog->get('blogurl'), cat='.$cat_ID ).'" title="'.$link_title.'">'.$cat_name.'</a>';

and instead put this:

$cat_name = '<a href="' . $lBlog->get('blogurl') . '/categorias/'. $this->get_category_niceurl( $cat_ID ) .'" title="'.$link_title.'">'.$cat_name.'</a>';

The function get_category_niceurl() (probably it should have a different name?) I defined at the end of the same file:

function get_category_niceurl ( $cat_ID )
	{
		global $DB ;
		$cat_sql = "SELECT cat_description FROM evo_categories WHERE cat_ID = " . $cat_ID ;
		$cat_row = $DB -> get_row( $cat_sql ) ;
		return ( empty( $cat_row ) ? '' : $cat_row -> cat_description ) ;
	}

And that does the trick. If you go to http://ningunopresidente.com/ yu will see that the category links below the item titles are of the form I wanted at the beginning, i.e. http://ningunopresidente.com/index.php/categorias/en_la_prensa

So everything wonderfull up to know.

And I guess I will stop it there, since this will be implemented in 2.0 and I just have to wait some time.

But since I'm curious by nature and I thought I was learning one thing or two, I will ask anyway, and demonstrate my sheer ignorance of OO programing. I tried to use The function I defined before, get_category_niceurl, inside the Categories plugin (/plugins/_categories.plugin.php) to produce the category listing in the sidebar and then I get the error

Fatal error: Call to a member function on a non-object in /home/libenie2/public_html/ninguno/plugins/_categories.plugin.php on line 288

How could I solve this?

Jan 25, 2007 00:32

Austriaco wrote:

Fatal error: Call to a member function on a non-object in /home/libenie2/public_html/ninguno/plugins/_categories.plugin.php on line 288

How could I solve this?

What's line 288? The problem is that you're calling it on a non-object.

E.g. $Foo->bar() won't work, if $Foo is undefined.

You may want to enable PHP notices (e.g. error_reporting = E_ALL). Then you would get a notice before, e.g. "Undefined variable $Foo".

Jan 25, 2007 00:38

Austriaco wrote:

The function get_category_niceurl() (probably it should have a different name?) I defined at the end of the same file:

function get_category_niceurl ( $cat_ID )
	{
		global $DB ;
		$cat_sql = "SELECT cat_description FROM evo_categories WHERE cat_ID = " . $cat_ID ;
		$cat_row = $DB -> get_row( $cat_sql ) ;
		return ( empty( $cat_row ) ? '' : $cat_row -> cat_description ) ;
	}

I'm betting you put it just above "?>" so it looks like this :-

}
function get_category_niceurl ( $cat_ID )
	{
		global $DB ;
		$cat_sql = "SELECT cat_description FROM evo_categories WHERE cat_ID = " . $cat_ID ;
		$cat_row = $DB -> get_row( $cat_sql ) ;
		return ( empty( $cat_row ) ? '' : $cat_row -> cat_description ) ;
	}
?>


It needs to be "inside" the last } to be part of the class ;)

function get_category_niceurl ( $cat_ID )
	{
		global $DB ;
		$cat_sql = "SELECT cat_description FROM evo_categories WHERE cat_ID = " . $cat_ID ;
		$cat_row = $DB -> get_row( $cat_sql ) ;
		return ( empty( $cat_row ) ? '' : $cat_row -> cat_description ) ;
	}
}
?>

Jan 25, 2007 01:17

Sorry, I was not explicit enough.

in plugins/_categories.plugin.php line 288 was:

$r .= url_add_param( get_bloginfo('blogurl'), 'cat='.$cat_ID );

I put instead:

$r .= get_bloginfo('blogurl'). '/categorias/' . $Item->get_category_niceurl( $cat_ID );

Since I assume my newly created function get_category_niceurl belongs to the Item class. I defined it in inc/MODEL/items/_item.class.php and its definition is before the matching '}' corresponding to

class Item extends DataObject
{

At least that's the curly brace matched by Vim :)

As I said I don't know anything about OO programing, so you can skip all this. Besides this stuff is already implemented in 2.0, right?

Anyway is fun still.

Jan 25, 2007 01:22

Austriaco wrote:

$r .= get_bloginfo('blogurl'). '/categorias/' . $Item->get_category_niceurl( $cat_ID );

As said, $Item (a variable) is not defined here.
You could try the following:


global $ItemCache;
$Item = & $ItemCache->get_by_ID($cat_ID);
$r .= get_bloginfo('blogurl'). '/categorias/' . $Item->get_category_niceurl( $cat_ID );


(adding the two lines in front of it to get the Item object you want)

Oh.. You could also call it statically:

$r .= get_bloginfo('blogurl'). '/categorias/' . $Item::get_category_niceurl( $cat_ID );


(which is possible, because you are not using $this in the method itself and it probably what you've intended initially).

Or.. you could just make a function out of it (putting it outside the Item class scope, as it makes no sense there really anyway - it's not related to the object). Add it to _item.funcs.php then for example.

Jan 25, 2007 01:43

Unbelievable! Wundershön!

It works!

The third option does the trick. I moved the function get_category_niceurl() to _item.funcs.php and then called it from _categories.plugin.php like this (line 287):

$r .= get_bloginfo('blogurl'). '/categorias/' . get_category_niceurl( $cat_ID );

And also from the categories() function in _item.class.php (line 679)

$cat_name = '<a href="' . $lBlog->get('blogurl') . '/categorias/'. get_category_niceurl( $cat_ID ) .'" title="'.$link_title.'">'.$cat_name.'</a>';

You guys rock!

So, now that I'm in awe after this, back to planet Earth. I'm sure that the form this is implemented in 2.0 is completely different, and I will have to undo all this after the next upgrade (I have an aversion to manipulating my database manually). Am I right?

Jan 25, 2007 21:46

Very well. That looks much better than what I was trying. Thanks anyway, I learned two or three things in the process.


Form is loading...

Secure CMS – This forum is powered by b2evolution CMS, a complete engine for your website.