我有一个为页面标题创建唯一slug的功能。它会检查页面表中是否有slug然后通过相应地添加'-int'来创建一个独特的slug。该功能适用于前三个条目,例如,三次输入的'test slug'将创建'test-slug-1','test-slug-2'和'test-slug-3'。然后,我得到第四个条目的错误“致命错误:超过30秒的最大执行时间”。逻辑上应该有一些问题,任何人都可以帮我找到它。下面是代码:
function createSlug($title, $table_name, $field_name) {
global $db_connect;
$slug = preg_replace("/-$/","",preg_replace('/[^a-z0-9]+/i', "-", strtolower($title)));
$counter = 1;
do{
$query = "SELECT * FROM $table_name WHERE $field_name = '".$slug."'";
$result = mysqli_query($db_connect, $query) or die(mysqli_error($db_connect));
if(mysqli_num_rows($result) > 0){
$count = strrchr($slug , "-");
$count = str_replace("-", "", $count);
if($count > 0){
$length = count($count) + 1;
$newSlug = str_replace(strrchr($slug , "-"), '',$slug);
$slug = $newSlug.'-'.$length;
$count++;
}else{
$slug = $slug.'-'.$counter;
}
}
$counter++;
$row = mysqli_fetch_assoc($result);
}while(mysqli_num_rows($result) > 0);
return $slug;
}
答案 0 :(得分:31)
只需点击一次数据库,立刻抓住所有内容,这可能是最大的瓶颈。
$query = "SELECT * FROM $table_name WHERE $field_name LIKE '".$slug."%'";
然后将结果放入数组(假设为$slugs
)
//we only bother doing this if there is a conflicting slug already
if(mysqli_num_rows($result) !== 0 && in_array($slug, $slugs)){
$max = 0;
//keep incrementing $max until a space is found
while(in_array( ($slug . '-' . ++$max ), $slugs) );
//update $slug with the appendage
$slug .= '-' . $max;
}
我们使用in_array()
检查,好像slug是my-slug
LIKE
也会返回
my-slug-is-awesome
my-slug-is-awesome-1
my-slug-rules
等会导致问题,in_array()
检查确保我们只检查输入的确切slu ..
这是因为如果您有多个结果,并且删除了一些结果,那么您的下一个slu will可能会发生冲突。
E.g。
my-slug
my-slug-2
my-slug-3
my-slug-4
my-slug-5
删除-3和-5离开我们
my-slug
my-slug-2
my-slug-4
因此,这给了我们3个结果,下一个插入将是my-slug-4
已经存在。
ORDER BY
和LIMIT 1
?我们不能在查询中执行order by
因为缺少自然排序会使my-slug-10
排名低于my-slug-4
,因为它会比较字符按字符排列,4
高于1
E.g。
m = m
y = y
- = -
s = s
l = l
u = u
g = g
- = -
4 > 1 !!!
< 0 (But the previous number was higher, so from here onwards is not compared)
答案 1 :(得分:9)
只需使用一个查询即可为您完成所有繁重的工作......
$slug = preg_replace("/-$/","",preg_replace('/[^a-z0-9]+/i', "-", strtolower($title)));
$query = "SELECT COUNT(*) AS NumHits FROM $table_name WHERE $field_name LIKE '$slug%'";
$result = mysqli_query($db_connect, $query) or die(mysqli_error($db_connect));
$row = $result->fetch_assoc();
$numHits = $row['NumHits'];
return ($numHits > 0) ? ($slug . '-' . $numHits) : $slug;
答案 2 :(得分:2)
您可以选择编号最大的slug,然后用1:
增加$query = "SELECT $field_name FROM $table_name WHERE $field_name LIKE '".$slug."-[0-9]*' ORDER BY $field_name DESC LIMIT 1";
查询中的 [0-9]*
表示任意数量的数字。
此查询将选择开头为$slug
的行和最大数字。
之后,您可以解析结果获取数字并增加它。
在这种情况下,您将只有一个查询和大量未使用的性能。
<强>更新强>
这不起作用,因为slug-8
将比slug-11
“更大”。但不知道如何解决它。也许ORDER BY
id DESC
?
更新2
查询也可以按长度排序,它可以正常工作。感谢杰克:
$query = "SELECT $field_name FROM $table_name WHERE $field_name LIKE '".$slug."-[0-9]*' ORDER BY LENGTH($field_name), $field_name DESC LIMIT 1";
更新3
还添加了检查原始slu ..感谢Hailwood。
$query = "SELECT $field_name FROM $table_name WHERE $field_name = '".$slug."' OR $field_name LIKE '".$slug."-[0-9]*' ORDER BY LENGTH($field_name), $field_name DESC LIMIT 1";
答案 3 :(得分:1)
对于这一部分,我将创建一个对象来处理创建slug并处理数字的部分:
// generate new slug:
$slug = new NumberedSlug('Creating Unique Page Title Slugs in PHP');
echo $slug, "\n", $slug->increase(), "\n";
// read existing slug:
$slug = new NumberedSlug('creating-unique-page-title-slugs-in-php-44');
echo $slug->getNumber(), "\n";
输出:
creating-unique-page-title-slugs-in-php
creating-unique-page-title-slugs-in-php-1
44
对于另一部分,数据库,这已经大大简化了你的代码(请仔细检查,我已经快速完成了这个)。另请参阅您如何从实际拥有的Mysqli对象中受益(但不能按原样使用):
function createSlug($title, $table_name, $field_name, Mysqli $mysqli = NULL)
{
$mysqli || $mysqli = $GLOBALS['db_connect'];
$slug = new NumberedSlug($title);
do
{
$query = "SELECT 1 FROM $table_name WHERE $field_name = '" . $slug . "'";
if (!$result = $mysqli->query($query)) {
throw new RuntimeException(var_export($mysqli->error_list, true));
}
if ($result->num_rows) {
$slug->increase();
}
} while ($result->num_rows);
return $slug;
}
但是正如其他人已经写过的那样,你应该首先从数据库中获取一次编号的所有slug,然后在必要时选择一个唯一的slug。这将减少数据库调用的数量。代码也更加紧凑:
function createSlug2($title, $table_name, $field_name, Mysqli $mysqli = NULL)
{
$mysqli || $mysqli = $GLOBALS['db_connect'];
$slug = new NumberedSlug($title);
$query = "SELECT $field_name FROM $table_name WHERE $field_name LIKE '$slug-_%'";
if (!$result = $mysqli->query($query)) {
throw new RuntimeException(var_export($mysqli->error_list, true));
}
$existing = array_flip(call_user_func_array('array_merge', $result->fetch_all()));
$slug->increase();
while (isset($existing[$slug]))
{
$slug->increase();
}
return $slug;
}
答案 4 :(得分:1)
我对答案并不完全满意,所以我想出了一个略有不同的方法。
var m IModel
m = model{}
db.Table("model").
Joins("INNER JOIN user_model ON user.id = user_model.uuid").
Joins("INNER JOIN model ON user.id = model_id").
Select(m.GetSelectFields()).
Find(m.GetSelectModel());
这基本上是做什么的,它将检查数据库中与新子段相似的所有现有值,并将其与行号进行匹配以检查间隙,如果没有发现间隙,它将使用所生成的最大数目被推到末尾的第一个相同的子弹(注意:我们替换(SELECT CONCAT({$slug}, '-', counter) FROM (
SELECT (@row_number:=@row_number + 1) AS counter, ev.*
FROM (
SELECT REPLACE(slug, {$slug}-, '') AS remainder
FROM products, (SELECT @row_number:=0) AS t
WHERE slug LIKE '{$slug}%'
) ev
ORDER BY LENGTH(remainder), remainder
) sr
WHERE counter <> remainder)
LIMIT 1
而不是slug-
)
答案 5 :(得分:0)
$query = "SELECT * FROM $table_name WHERE $field_name LIKE '".$slug."%'";
$result = mysqli_query($db_connect, $query) or die(mysqli_error($db_connect));
//EDITED BASED ON COMMENT SUGGESTIONS
//create array of all matching slug names currently in database
$slugs = array();
while($row = $result->fetch_row()) {
$slugs[] = $row['field_name'];
}
//test if slug is in database, append - '1,2,..n' until available slug is found
if(in_array($slug, $slugs)){
$count = 1;
do{
$testSlug = $slug . '-' . $count;
$count++;
} while(in_array($testSlug, $slugs));
$slug = $testSlug;
}
//insert slug
您应该能够在单个数据库调用中使用LIKE关键字执行此操作,这将减少执行时间。
答案 6 :(得分:0)
为什么不创建一个slug并留下涉及索引到MySQL的其余工作。这是一个slugify
函数(它是Symfony框架使用的略微修改的版本)。
function slugify( $text ) {
$text = preg_replace('~[^\\pL\d]+~u', '-', $text);
$text = trim($text, '-');
$text = iconv('utf-8', 'ASCII//IGNORE//TRANSLIT', $text);
$text = strtolower(trim($text));
$text = preg_replace('~[^-\w]+~', '', $text);
return empty($text) ? substr( md5( time() ), 0, 8 ) : $text;
}
可以使用trigger解决MySQL部分(更改表名和列名)。
BEGIN
declare original_slug varchar(255);
declare slug_counter int;
set original_slug = new.slug;
set slug_counter = 1;
while exists (select true from post where slug = new.slug) do
set new.slug = concat(original_slug, '-', slug_counter);
set slug_counter = slug_counter + 1;
end while;
END
答案 7 :(得分:0)
您可以使用Fbeen/UniqueSlugBundle。这个Bundle很轻巧,可以做它需要做的事情。