SEO Friendly URLS - potential resolution

For articles specific to version 6.x
Post Reply
mors3
Posts: 8
Joined: Wed Mar 13, 2013 11:41 am
Location: Salisbury, United Kingdom
Contact:

SEO Friendly URLS - potential resolution

Post by mors3 »

Ok, so we moved our implementation of ISC through to a windows server that has some VERY specific URL routing rules which ISC spectacularly blew up with.

So, if you're seeing issues with double encoding (%252d where it should only encode a %2d for a '-' etc) this was my resolution.
/lib/general.php

Code: Select all

/**
	 * Convert an already search engine friendly based string back to the normal text equivalent.
	 * (this is the incoming route)
	 *
	 * @param string The search engine friendly version of the string.
	 * @return string The normal textual version of the string.
	 */
	function MakeURLNormal($val)
	{
		if (strpos($val,'_')!==false){
			//$val = str_replace("%2d", " ", $val);
		}
		else {$val = str_replace("-", " ", $val);}

		//$val = str_replace("-", " ", $val);
		//$val = str_replace("–", "-", $val);
		$val = str_replace("%25a3", "%A3", $val);
		$val = str_replace("_", " ", $val);
		$val = urldecode($val);
		$val = str_replace("%2f", "/", $val);
		$val = str_replace("%2d", "-", $val);
		$val = str_replace("%2b", "+", $val);
		
		return $val;
	}

	/**
	 * Convert a text string in to a search engine friendly based URL.
	 * (this my adapted SEO Friendly implementation)
	 *
	 * @param string The text string to convert.
	 * @return string The search engine friendly equivalent.
	 */
	function MakeURLSafeNEW($val)
	{
		$val = str_replace(" ", "_", $val);
		
		$val = str_replace("/", "{47}", $val);
		
		$val = urlencode($val);
		$val = str_replace("£", "%A3", $val);
		$val = str_replace("-", "%2d", $val);
		$val = str_replace("+", "-", $val);
		return $val;
	}

	/**
	 * Convert a text string in to a search engine friendly based URL.
	 * (this is the original implementation)
	 *
	 * @param string The text string to convert.
	 * @return string The search engine friendly equivalent.
	 */
	function MakeURLSafe($val)
	{
		
		$val = str_replace("-", "%2d", $val);
		$val = str_replace("+", "%2b", $val);
		$val = str_replace("+", "%2b", $val);
		$val = str_replace("/", "%2f", $val);
		$val = urlencode($val);
		$val = str_replace("+", "-", $val);
		return $val;
	}
This gives you the core split routing, it ensures that your existing non SEO routes (product.php?product=XXXXX) remain untouched so your existing search results and client bookmarked links still work while you 301 them all up to the new format, which now routes like this: products/Men%27s_Crew_Neck_T-Shirts_-_Pack_of_2_%28White%29.html

Ok, so now we have an additional route for the SEO friendlies to be generated and parsed no incoming, time to add them in!

They get attached (same file) here:

Code: Select all

	/**
	 * Generate the link to a product.
	 *
	 * @param string The name of the product to generate the link to.
	 * @return string The generated link to the product.
	 */
	function ProdLink($prod)
	{
		if ($GLOBALS['EnableSEOUrls'] == 1) {
			return sprintf("%s/%s/%s.html", GetConfig('ShopPathNormal'), PRODUCT_LINK_PART, MakeURLSafeNEW($prod));
		} else {
			return sprintf("%s/products.php?product=%s", GetConfig('ShopPathNormal'), MakeURLSafe($prod));
		}
	}
and

Code: Select all

	/**
	 * Generate the link to a brand name.
	 *
	 * @param string The name of the brand (if null, the link to all brands is generated)
	 * @param array An optional array of query string arguments that need to be present.
	 * @param boolean Set to false to not separate query string arguments with & but use & instead. Useful if generating a link to use for a redirect.
	 * @return string The generated link to the brand.
	 */
	function BrandLink($brand=null, $queryString=array(), $entityAmpersands=true)
	{
		// If we don't have a brand then we're just generating the link to the "all brands" page
		if($brand === null) {
			if ($GLOBALS['EnableSEOUrls'] == 1) {
				$link = sprintf("%s/%s/", $GLOBALS['ShopPathNormal'], BRAND_LINK_PART, MakeURLSafeNEW($brand));
			} else {
				$link = sprintf("%s/brands.php", $GLOBALS['ShopPathNormal'], MakeURLSafe($brand));
			}
		}
		else {
			if ($GLOBALS['EnableSEOUrls'] == 1) {
				$link = sprintf("%s/%s/%s.html", $GLOBALS['ShopPathNormal'], BRAND_LINK_PART, MakeURLSafeNEW($brand));
			} else {
				$link = sprintf("%s/brands.php?brand=%s", $GLOBALS['ShopPathNormal'], MakeURLSafe($brand));
			}
		}

		if($entityAmpersands) {
			$ampersand = '&';
		}
		else {
			$ampersand = '&';
		}
		if(is_array($queryString) && !empty($queryString)) {
			if ($GLOBALS['EnableSEOUrls'] == 1) {
				$link .= '?';
			}
			else {
				$link .= $ampersand;
			}
			$qString = array();
			foreach($queryString as $k => $v) {
				$qString[] = $k.'='.urlencode($v);
			}
			$link .= implode($ampersand, $qString);
		}

		return $link;
	}
and

Code: Select all

	/**
	 * Generate the link to a category.
	 *
	 * @param int The ID of the category to generate the link to.
	 * @param string The name of the category to generate the link to.
	 * @param boolean Set to true to base this link as a root category link.
	 * @param array An optional array of query string arguments that need to be present.
	 * @return string The generated link to the category.
	 */
	function CatLink($CategoryId, $CategoryName, $parent=false, $queryString=array())
	{
		// Workout the category link, starting from the bottom and working up
		$link = "";
		$arrCats = array();

		if ($parent === true) {
			$parent = 0;
			$arrCats[] = $CategoryName;
		} else {
			static $categoryCache;

			if(!is_array($categoryCache)) {
				$categoryCache = array();
				$query = "SELECT catname, catparentid, categoryid FROM [|PREFIX|]categories order by catsort desc, catname asc";
				$result = $GLOBALS['ISC_CLASS_DB']->Query($query);
				while ($row = $GLOBALS['ISC_CLASS_DB']->Fetch($result)) {
					$categoryCache[$row['categoryid']] = $row;
				}
			}
			if(empty($categoryCache)) {
				return '';
			}
			if (isset($categoryCache[$CategoryId])) {
				$parent = $categoryCache[$CategoryId]['catparentid'];

				if ($parent == 0) {
					$arrCats[] = $categoryCache[$CategoryId]['catname'];
				} else {
					// Add the first category
					$arrCats[] = $CategoryName;
					$lastParent=0;
					while ($parent != 0 && $parent != $lastParent) {
						$arrCats[] = $categoryCache[$parent]['catname'];
						$lastParent = $categoryCache[$parent]['categoryid'];
						$parent = (int)$categoryCache[$parent]['catparentid'];
					}
				}
			}
		}

		$arrCats = array_reverse($arrCats);

if ($GLOBALS['EnableSEOUrls'] == 1) {
			for ($i = 0; $i < count($arrCats); $i++) {
				$link .= sprintf("%s/", MakeURLSafeNEW($arrCats[$i]));
			}
		} else {
			for ($i = 0; $i < count($arrCats); $i++) {
				$link .= sprintf("%s/", MakeURLSafe($arrCats[$i]));
			}
		}

		// Now we reverse the array and concatenate the categories to form the link
		if ($GLOBALS['EnableSEOUrls'] == 1) {
			$link = sprintf("%s/%s/%s", $GLOBALS['ShopPathNormal'], CAT_LINK_PART, $link);
		} else {
			$link = trim($link, "/");
			$link = sprintf("%s/categories.php?category=%s", $GLOBALS['ShopPathNormal'], $link);
		}

		if(is_array($queryString) && !empty($queryString)) {
			if ($GLOBALS['EnableSEOUrls'] == 1) {
				$link .= '?';
			}
			else {
				$link .= '&';
			}
			$link .= http_build_query($queryString);
		}

		return $link;
	}
Ok, so now the site internally generates URL's based on the MakeURLSafeNEW() function (assuming that you've enabled Friendly URL's in your shop admin) but the old routes still work.
This solved a major headache for us, we've still a LOT of 301 work to do to catch reference to any of the old links floating around on the net and on people's bookmark list, but when that is done and dusted we should be completely SEO URL friendly!
ISC Ultimate (et al) 6.1.8

Post Reply