对于工作中的客户,我们建立了一个网站。该网站有一个提供页面,可以包含相同类型/构建的变体,因此它们遇到了双重清理网址的问题。
刚才我写了一个函数,通过在URL上附加一个数字来防止这种情况发生。如果那个清洁网址也存在,那就算了。
E.g。
domain.nl/product/machine
domain.nl/product/machine-1
domain.nl/product/machine-2
已更新! return $ clean_url;在递归和返回时
我写的功能很好,但我想知道我是否采取了正确的方法,是否可以改进。这是代码:
public function prevent_double_cleanurl($cleanurl)
{
// makes sure it doesnt check against itself
if($this->ID!=NULL) $and = " AND product_ID <> ".$this->ID;
$sql = "SELECT product_ID, titel_url FROM " . $this->_table . " WHERE titel_url='".$cleanurl."' " . $and. " LIMIT 1";
$result = $this->query($sql);
// if a matching url is found
if(!empty($result))
{
$url_parts = explode("-", $result[0]['titel_url']);
$last_part = end($url_parts);
// maximum of 2 digits
if((int)$last_part && strlen($last_part)<3)
{
// if a 1 or 2 digit number is found - add to it
array_pop($url_parts);
$cleanurl = implode("-", $url_parts);
(int)$last_part++;
}
else
{
// add a suffix starting at 1
$last_part='1';
}
// recursive check
$cleanurl = $this->prevent_double_cleanurl($cleanurl.'-'.$last_part);
}
return $cleanurl;
}
答案 0 :(得分:1)
根据多次使用“清洁网址”的可能性,您的方法可能不是最佳选择。假设有“foo”到“foo-10”,你将10次调用数据库。
您似乎也没有清理您输入SQL查询的数据。您使用mysql_real_escape_string(或其mysqli,PDO,无论兄弟)?
修订代码:
public function prevent_double_cleanurl($cleanurl) {
$cleanurl_pattern = '#^(?<base>.*?)(-(?<num>\d+))?$#S';
if (preg_match($cleanurl_pattern, $base, $matches)) {
$base = $matches['base'];
$num = $matches['num'] ? $matches['num'] : 0;
} else {
$base = $cleanurl;
$num = 0;
}
// makes sure it doesnt check against itself
if ($this->ID != null) {
$and = " AND product_ID <> " . $this->ID;
}
$sql = "SELECT product_ID, titel_url FROM " . $this->_table . " WHERE titel_url LIKE '" . $base . "-%' LIMIT 1";
$result = $this->query($sql);
foreach ($result as $row) {
if ($this->ID && $row['product_ID'] == $this->ID) {
// the given cleanurl already has an ID,
// so we better not touch it
return $cleanurl;
}
if (preg_match($cleanurl_pattern, $row['titel_url'], $matches)) {
$_base = $matches['base'];
$_num = $matches['num'] ? $matches['num'] : 0;
} else {
$_base = $row['titel_url'];
$_num = 0;
}
if ($base != $_base) {
// make sure we're not accidentally comparing "foo-123" and "foo-bar-123"
continue;
}
if ($_num > $num) {
$num = $_num;
}
}
// next free number
$num++;
return $base . '-' . $num;
}
我不知道您的清洁网址的可能值。上次我做了这样的事情,我的基础可能看起来像some-article-revision-5
。 5
是实际项目符号的一部分,而不是重复索引。为了区分它们(并允许LIKE
过滤掉误报),我将clean-urls看作$base--$num
。双破折号只能在基数和重复索引之间发生,使事情变得更简单......
答案 1 :(得分:0)
我没办法测试这个,所以它就在你身上,但这就是我要做的。我在那里放了大量的评论来解释我的推理和代码的流程。
基本上,递归是不必要的,会导致更多的数据库查询。
<?
public function prevent_double_cleanurl($cleanurl)
{
$sql = sprintf("SELECT product_ID, titel_url FROM %s WHERE titel_url LIKE '%s%%'",
$this->_table, $cleanurl);
if($this->ID != NULL){ $sql.= sprintf(" AND product_ID <> %d", $this->ID); }
$results = $this->query($sql);
$suffix = 0;
$baseurl = true;
foreach($results as $row)
{
// Consider the case when we get to the "first" row added to the db:
// For example: $row['titel_url'] == $cleanurl == 'domain.nl/product/machine'
if($row['title_url'] == $cleanurl)
{
$baseurl = false; // The $cleanurl is already in the db, "this" is not a base URL
continue; // Continue with the next iteration of the foreach loop
}
// This could be done using regex, but if this works its fine.
// Make sure to test for the case when you have both of the following pages in your db:
//
// some-hyphenated-page
// some-hyphenated-page-name
//
// You don't want the counters to get mixed up
$url_parts = explode("-", $row['titel_url']);
$last_part = array_pop($url_parts);
$cleanrow = implode("-", $url_parts);
// To get into this block, three things need to be true
// 1. $last_part must be a numeric string (PHP Duck Typing bleh)
// 2. When represented as a string, $last_part must not be longer than 2 digits
// 3. The string passed to this function must match the string resulting from the (n-1)
// leading parts of the result of exploding the table row
if((is_numeric($last_part)) && (strlen($last_part)<=2) && ($cleanrow == $cleanurl))
{
$baseurl = false; // If there are records in the database, the
// passed $cleanurl isn't the first, so it
// will need a suffix
$suffix = max($suffix, (int)$last_part); // After this foreach loop is done, $suffix
// will contain the highest suffix in the
// database we'll need to add 1 to this to
// get the result url
}
}
// If $baseurl is still true, then we never got into the 3-condition block above, so we never
// a matching record in the database -> return the cleanurl that was passed here, no need
// to add a suffix
if($baseurl)
{
return $cleanurl;
}
// At least one database record exists, so we need to add a suffix. The suffix we add will be
// the higgest we found in the database plus 1.
else
{
return sprintf("%s-%d", $cleanurl, ($suffix + 1));
}
}
我的解决方案利用SQL通配符(%
)将 n 中的查询数量减少到1。
确保您确保在第14-20行中描述的有问题的情况按预期工作。机器名称中的连字符(或其他任何东西)可能会发生意外情况。
我还使用sprintf
来格式化查询。确保清理以字符串形式传递的任何字符串(例如$cleanurl
)。
正如@rodneyrehm指出的那样,PHP对于它认为是数字字符串的内容非常灵活。您可以考虑为is_numeric()
切换ctype_digit()
,看看它是如何运作的。