создание уникального заголовка страницы 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. Этот комплект Легкий и делает то, что ему нужно сделать.