To use this website fully, you first need to accept the use of cookies. By agreeing to the use of cookies you consent to the use of functional cookies. For more information read this page.

Jamie Balfour'sPersonal blog

In the last few hours, I have been working on improving the information architecture and the URLs for my tutorials. For the last six or seven years the way the URLs to my tutorials have looked is like this:

/courses/web/css/1/2/

Now, with a bit of work, I've written a much more beautiful system, and that makes the URLs look like this:

/courses/web/css/using_css/

The new system uses URL rewrites alongside a clever automated (cron) generated JSON in which all of the titles are given friendly names:

JSON
[{
  "page_name": "What this tutorial is",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/1/1",
  "area": "1",
  "page": "1",
  "title": "what_this_tutorial_is",
  "friendly_path": "web/css/what_this_tutorial_is"
}, {
  "page_name": "What CSS is",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/2/1",
  "area": "2",
  "page": "1",
  "title": "what_css_is",
  "friendly_path": "web/css/what_css_is"
}, {
  "page_name": "Using CSS",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/2/2",
  "area": "2",
  "page": "2",
  "title": "using_css",
  "friendly_path": "web/css/using_css"
}, {
  "page_name": "Why CSS is used",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/2/3",
  "area": "2",
  "page": "3",
  "title": "why_css_is_used",
  "friendly_path": "web/css/why_css_is_used"
}, {
  "page_name": "Classes, IDs and tag selection",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/2/4",
  "area": "2",
  "page": "4",
  "title": "classes_ids_and_tag_selection",
  "friendly_path": "web/css/classes_ids_and_tag_selection"
}, {
  "page_name": "Display, positioning, floating and visibility",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/3/1",
  "area": "3",
  "page": "1",
  "title": "display_positioning_floating_and_visibility",
  "friendly_path": "web/css/display_positioning_floating_and_visibility"
}, {
  "page_name": "Width, height, padding, margins and overflow",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/3/2",
  "area": "3",
  "page": "2",
  "title": "width_height_padding_margins_and_overflow",
  "friendly_path": "web/css/width_height_padding_margins_and_overflow"
}, {
  "page_name": "Borders, box shadows, border radius and box sizing",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/3/3",
  "area": "3",
  "page": "3",
  "title": "borders_box_shadows_border_radius_and_box_sizing",
  "friendly_path": "web/css/borders_box_shadows_border_radius_and_box_sizing"
}, {
  "page_name": "Color, backgrounds, opacity and gradients",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/3/4",
  "area": "3",
  "page": "4",
  "title": "color_backgrounds_opacity_and_gradients",
  "friendly_path": "web/css/color_backgrounds_opacity_and_gradients"
}, {
  "page_name": "Font families, sizes, weights, variants, styles and alignment",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/3/5",
  "area": "3",
  "page": "5",
  "title": "font_families_sizes_weights_variants_styles_and_alignment",
  "friendly_path": "web/css/font_families_sizes_weights_variants_styles_and_alignment"
}, {
  "page_name": "Descendant selector",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/4/1",
  "area": "4",
  "page": "1",
  "title": "descendant_selector",
  "friendly_path": "web/css/descendant_selector"
}, {
  "page_name": "Child selector",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/4/2",
  "area": "4",
  "page": "2",
  "title": "child_selector",
  "friendly_path": "web/css/child_selector"
}, {
  "page_name": "Adjacenct sibling selector",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/4/3",
  "area": "4",
  "page": "3",
  "title": "adjacenct_sibling_selector",
  "friendly_path": "web/css/adjacenct_sibling_selector"
}, {
  "page_name": "General sibling selector",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/4/4",
  "area": "4",
  "page": "4",
  "title": "general_sibling_selector",
  "friendly_path": "web/css/general_sibling_selector"
}, {
  "page_name": "Universal selector",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/4/5",
  "area": "4",
  "page": "5",
  "title": "universal_selector",
  "friendly_path": "web/css/universal_selector"
}, {
  "page_name": "Attribute selector",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/4/6",
  "area": "4",
  "page": "6",
  "title": "attribute_selector",
  "friendly_path": "web/css/attribute_selector"
}, {
  "page_name": "An introduction to pseudo-selectors",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/5/1",
  "area": "5",
  "page": "1",
  "title": "an_introduction_to_pseudo-selectors",
  "friendly_path": "web/css/an_introduction_to_pseudo-selectors"
}, {
  "page_name": "Advanced CSS pseudo selectors",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/5/2",
  "area": "5",
  "page": "2",
  "title": "advanced_css_pseudo_selectors",
  "friendly_path": "web/css/advanced_css_pseudo_selectors"
}, {
  "page_name": "Shorthand CSS",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/6/1",
  "area": "6",
  "page": "1",
  "title": "shorthand_css",
  "friendly_path": "web/css/shorthand_css"
}, {
  "page_name": "Precedence and specificity",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/6/2",
  "area": "6",
  "page": "2",
  "title": "precedence_and_specificity",
  "friendly_path": "web/css/precedence_and_specificity"
}, {
  "page_name": "CSS rules",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/6/3",
  "area": "6",
  "page": "3",
  "title": "css_rules",
  "friendly_path": "web/css/css_rules"
}, {
  "page_name": "Optimising CSS delivery",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/6/4",
  "area": "6",
  "page": "4",
  "title": "optimising_css_delivery",
  "friendly_path": "web/css/optimising_css_delivery"
}, {
  "page_name": "Cross platform and browser compatibility",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/6/5",
  "area": "6",
  "page": "5",
  "title": "cross_platform_and_browser_compatibility",
  "friendly_path": "web/css/cross_platform_and_browser_compatibility"
}, {
  "page_name": "Server-side generated CSS",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/6/6",
  "area": "6",
  "page": "6",
  "title": "server-side_generated_css",
  "friendly_path": "web/css/server-side_generated_css"
}, {
  "page_name": "CSS and Sass",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/6/7",
  "area": "6",
  "page": "7",
  "title": "css_and_sass",
  "friendly_path": "web/css/css_and_sass"
}, {
  "page_name": "An introduction to responsive web design",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/7/1",
  "area": "7",
  "page": "1",
  "title": "an_introduction_to_responsive_web_design",
  "friendly_path": "web/css/an_introduction_to_responsive_web_design"
}, {
  "page_name": "Tablet and smartphone friendly CSS",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/7/2",
  "area": "7",
  "page": "2",
  "title": "tablet_and_smartphone_friendly_css",
  "friendly_path": "web/css/tablet_and_smartphone_friendly_css"
}, {
  "page_name": "CSS transform",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/8/1",
  "area": "8",
  "page": "1",
  "title": "css_transform",
  "friendly_path": "web/css/css_transform"
}, {
  "page_name": "CSS transitions",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/8/2",
  "area": "8",
  "page": "2",
  "title": "css_transitions",
  "friendly_path": "web/css/css_transitions"
}, {
  "page_name": "CSS counters",
  "category": "web",
  "category_name": "Web",
  "topic": "css",
  "topic_name": "CSS",
  "route": "/courses/web/css/8/3",
  "area": "8",
  "page": "3",
  "title": "css_counters",
  "friendly_path": "web/css/css_counters"
}]

On the front end, the page is found via its friendly_path value. To keep the order correct, the pages are not given a key. Although it would make it slightly faster to give them this key, it would mean that the order isn't correct any longer.

Changes to title names will actually break the system and any changes made will need to be followed by a running of the automated script (which happens every evening anyway).

I'm still testing this feature out, so if you find any bugs, let me know. 

Powered by DASH 2.0 (beta)
Code previewClose