создание уникального заголовка страницы slugs php
У меня есть функция для создания уникального слизняка для заголовка страницы. Он проверяет, доступен ли пуля в таблице pages, а затем создает уникальный пуля, добавляя "- int " соответственно. Функция отлично работает для первых трех записей, например, для "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;
}
7 ответов
просто используйте один запрос, чтобы сделать все тяжелое для вас...
$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;
просто нажмите на базу данных один раз, захватить все сразу, скорее всего, это самое большое узкое место.
$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()
проверяет, как если бы пуля была my-slug
на LIKE
также возвращает строки, такие как
my-slug-is-awesome
my-slug-is-awesome-1
my-slug-rules
etc, который вызовет проблемы,in_array()
проверки гарантируют, что мы проверяем только точный слиток, который был введен.
почему разве мы просто не считаем результаты и +1?
это потому, что если у вас было несколько результатов и вы удалили несколько, ваш следующий слиток может конфликтовать.
Э. Г.
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
Э. Г.
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:
$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
. Но не знаю, как это исправить. может быть!--7-->idDESC
?
обновление 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
также добавлена проверка оригинального слизняка. Благодаря 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";
для одной части я бы создал объект, который имеет дело с частью, создающей слизень и обрабатывающей число:
// 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;
}
но, как уже писали другие, вы должны сначала получить все пули, которые нумеруются сразу из базы данных, а затем при необходимости выбирают уникальный. Это позволит сократить число вызовов базы данных. Также код намного компактнее:
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;
}
$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, что сократит время выполнения.
почему бы вам просто не создать slug и оставить остальную часть задания, которая включает индексирование в MySQL. Вот это slugify
функция (это слегка измененная версия, используемая Symfony framework ).
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;
}
и часть 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
MySQL вставить строку, на дубликат: добавить суффикс и повторно вставить
можно использовать Fbeen / UniqueSlugBundle. Этот комплект Легкий и делает то, что ему нужно сделать.