Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Improve my function: generate SEO friendly title

I am using this function to generate SEO friendly titles, but I think it can be improved, anyone want to try? It does a few things: cleans common accented letters, check against a "forbidden" array, and check optionally against a database of titles in use.

    /**
    * Recursive function that generates a unique "this-is-the-title123" string for use in URL.
    * Checks optionally against $table and $field and the array $forbidden to make sure it's unique.
    * Usage: the resulting string should be saved in the db with the object. 
    */
    function seo_titleinurl_generate($title, $forbidden = FALSE, $table = FALSE, $field = FALSE)
    {
        ## 1. parse $title
        $title = clean($title, "oneline"); // remove tags and such

        $title = ereg_replace(" ", "-", $title); // replace spaces by "-"
        $title = ereg_replace("á", "a", $title); // replace special chars
        $title = ereg_replace("í", "i", $title); // replace special chars
        $title = ereg_replace("ó", "o", $title); // replace special chars
        $title = ereg_replace("ú", "u", $title); // replace special chars
        $title = ereg_replace("ñ", "n", $title); // replace special chars
        $title = ereg_replace("Ñ", "n", $title); // replace special chars

        $title = strtolower(trim($title)); // lowercase
        $title = preg_replace("/([^a-zA-Z0-9_-])/",'',$title); // only keep standard latin letters and numbers, hyphens and dashes

        ## 2. check against db (optional)
        if ($table AND $field)
        {
            $sql = "SELECT * FROM $table WHERE $field = '" . addslashes($title) . "'";
            $res = mysql_debug_query($sql);
            if (mysql_num_rows($res) > 0)
            {
                // already taken. So recursively adjust $title and try again.
                $title = append_increasing_number($title);
                $title = seo_titleinurl_generate($title, $forbidden, $table, $field);
            }
        }

        ## 3. check against $forbidden array
        if ($forbidden)
        {
            while (list ($key, $val) = each($forbidden))
            {
                // $val is the forbidden string
                if ($title == $val)
                {
                    $title = append_increasing_number($title);
                    $title = seo_titleinurl_generate($title, $forbidden, $table, $field);
                }
            }
        }
        return $title;
    }
    /**
    * Function that appends an increasing number to a string, for example "peter" becomes "peter1" and "peter129" becomes "peter130".
    * (To improve, this function could be made recursive to deal with numbers over 99999.)
    */
    function append_increasing_number($title)
    {
        ##. 1. Find number at end of string.
        $last1 = substr($title, strlen($title)-1, 1);
        $last2 = substr($title, strlen($title)-2, 2);
        $last3 = substr($title, strlen($title)-3, 3);
        $last4 = substr($title, strlen($title)-4, 4);
        $last5 = substr($title, strlen($title)-5, 5); // up to 5 numbers (ie. 99999)

        if (is_numeric($last5))
        {
            $last5++; // +1
            $title = substr($title, 0, strlen($title)-5) . $last5;
        } elseif (is_numeric($last4))
        {
            $last4++; // +1
            $title = substr($title, 0, strlen($title)-4) . $last4;
        } elseif (is_numeric($last3))
        {
            $last3++; // +1
            $title = substr($title, 0, strlen($title)-3) . $last3;
        } elseif (is_numeric($last2))
        {
            $last2++; // +1
            $title = substr($title, 0, strlen($title)-2) . $last2;
        } elseif (is_numeric($last1))
        {
            $last1++; // +1
            $title = substr($title, 0, strlen($title)-1) . $last1;
        } else 
        {
            $title = $title . "1"; // append '1'    
        }

        return $title;
    }
like image 278
PeterV Avatar asked Dec 20 '25 02:12

PeterV


2 Answers

There appears to be a race condition because you're doing a SELECT to see if the title has been used before, then returning it if not (presumably the calling code will then INSERT it into the DB). What if another process does the same thing, but it inserts in between your SELECT and your INSERT? Your insert will fail. You should probably add some guaranteed-unique token to the URL (perhaps a "directory" in the path one level higher than the SEO-friendly name, similar to how StackOverflow does it) to avoid the problem of the SEO-friendly URL needing to be unique at all.

I'd also rewrite the append_increasing_number() function to be more readable... have it programmatically determine how many numbers are on the end and work appropriately, instead of a giant if/else to figure it out. The code will be clearer, simpler, and possibly even faster.

like image 86
rmeador Avatar answered Dec 22 '25 17:12

rmeador


The str_replace suggestions above are excellent. Additionally, you can replace that last function with a single line:

function append_increasing_number($title) {
  return preg_replace('@([0-9]+)$@e', '\1+1', $title);
}

You can do even better and remove the query-in-a-loop idea entirely, and do something like

"SELECT MAX($field) + 1 FROM $table WHERE $field LIKE '" . mysql_escape_string(preg_replace('@[0-9]+$@', '', $title)) . "%'";

Running SELECTs in a loop like that is just ugly.

like image 38
Frank Farmer Avatar answered Dec 22 '25 18:12

Frank Farmer



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!