Создаем кастомный тип записи (Custom Post Type) с кастомными категориями (Custom Taxonomy). Живой поиск на Битрикс

28.02.2024

Основные задачи:

  • реализовать поиск таким образом, чтобы после ввода поискового запроса в строку, ниже этой строки появились результаты поиска
  • запрос на получение результата должен происходить только после окончания ввода поискового запроса

Окей, поехали!

Примерная вёрстка самого блока с поисковой строкой и div-ником, куда будем добавлять результаты поиска:

Т.к. поиск доступен в шапке сайта, добавим соответствующие скрипты поиска и стилизации результатов:

//подрубаем поиск: $APPLICATION->AddHeadScript("/search/ajax_search.js"); $APPLICATION->AddHeadScript("/search/jquery.mCustomScrollbar.js"); $APPLICATION->SetAdditionalCSS(SITE_TEMPLATE_PATH . "/css/ajax_search.css"); $APPLICATION->SetAdditionalCSS(SITE_TEMPLATE_PATH . "/css/jquery.mCustomScrollbar.min.css");

Теперь посмотрим, что лежит в нашем ajax_search.js:

Function get_result (){ //очищаем результаты поиска $("#search_result").html(""); //пока не получили результаты поиска - отобразим прелоадер $("#search_result").append("

"); $.ajax({ type: "POST", url: "/search/ajax_search.php", data: "q="+q, dataType: "json", success: function(json){ //очистим прелоадер $("#search_result").html(""); $("#search_result").append(""); //добавляем каждый элемент массива json внутрь div-ника с class="live-search" (вёрстку можете использовать свою) $.each(json, function(index, element) { $("#search_result").find(".live-search").append(""+element.TITLE+""+element.BODY_FORMATED+""); //console.log (element.BODY_FORMATED); }); //стилизуем скроллинг $(".live-search").mCustomScrollbar({ scrollInertia: 500 }); } }); } var timer = 0; var q = ""; $(document).ready(function() { $("#q").keyup(function() { q = this.value; clearTimeout(timer); timer = setTimeout(get_result, 1000); }); $("#reset_live_search").click(function() { $("#search_result").html(""); }); });

keyup функция осуществляем вызов функции get_result(), которая собственно и заполняет div-ник с id=»search_result» по аяксу.

mCustomScrollbar — это просто вызов стилизации (можете отключить).

Данные от /search/ajax_search.php мы получаем в формате JSON.

С JS составляющей всё понятно, теперь посмотрим, что происходит в ajax_search.php:

Search(array("QUERY" => $q, "SITE_ID" => LANG, "MODULE_ID" => "iblock", "CHECK_DATES" => "Y", "PARAM2" => "8")); $result = array(); while ($res = $obSearch->GetNext()){ $id = $res["ITEM_ID"]; //если нашли раздел: if (strripos($id, "S")!==false){ $result_item["TITLE"] = $res["TITLE"]; $result_item["URL"] = $res["URL"]; $result_item["BODY_FORMATED"] = $res["TITLE_FORMATED"]; $result = $result_item; } //если S-ки нету, то else{ $result_item["TITLE"] = $res["TITLE"]; $result_item["URL"] = $res["URL"]; $result_item["BODY_FORMATED"] = $res["BODY_FORMATED"]; $result = $result_item; } } echo json_encode($result); } ?>

В данном случае поиск осуществляется методом Search Битриксового класса CSearch. В PARAM2 пишем в каком инфоблоке ищем. Результаты поиска запихиваем в массив $result. Обратите внимание, что в $res[‘ITEM_ID’] может быть как элемент, так и раздел. В зависимости от того, что нашли, в $result_item[‘BODY_FORMATED’] пихаем либо название раздела, либо кусок текста из найдённого элемента инфоблока.

От автора: приветствую вас друзья. В этой статье мы с вами продолжим реализацию живого поиска для сайта. Что такое живой поиск? Вы с ним сталкиваетесь постоянно, когда что-то ищете в Гугле или Яндексе. Как только вы начинаете набирать поисковый запрос, поисковик тут же начинает предлагать вам варианты, из которых вам остается лишь выбрать наиболее подходящий. Удобная штука, не так ли? Попробуем и мы реализовать нечто подобное.

Исходные файлы текущей статьи вы можете скачать по . Первую часть статьи вы можете найти по .

Итак, в первой части мы подготовили базу данных, которая будет использована для живого поиска, а также прикрутили виджет Autocomplete библиотеки jQuery UI к полю поиска на нашей странице. Пока что виджет работает с тестовыми данными, но сейчас мы с вами это исправим.

Прежде всего укажем для виджета другой источник данных, это будет, скажем, файл search.php, который нам необходимо также создать.

$(function(){ $("#search").autocomplete({ source: "search.php", }); });

Теперь наберу любой символ в поле поиска и посмотрю, что происходит при этом в консоли браузера.

Как видим, отправляется GET запрос с параметром term, значением которого является строка, набранная в поле поиска. При этом все происходит асинхронно, без перезагрузки страницы, т.е. используется AJAX.

Отлично, теперь остается лишь принять поступивший поисковый запрос и выдать ответ на него. Для этого потребуется организовать подключение к серверу БД и написать несложный код, получающий данные из БД по запросу. Код файл search.php будет примерно таким:

$db = mysqli_connect("localhost", "root", "", "world") or die("Нет соединения с БД"); mysqli_set_charset($db, "utf8") or die("Не установлена кодировка соединения"); /** * поиск автокомплит **/ function search_autocomplete(){ global $db; $search = trim(mysqli_real_escape_string($db, $_GET["term"])); $query = "SELECT Name FROM city WHERE Name LIKE "%{$search}%" LIMIT 10"; $res = mysqli_query($db, $query); $result_search = array(); while($row = mysqli_fetch_assoc($res)){ $result_search = array("label" => $row["Name"]); } return $result_search; } if(!empty($_GET["term"])){ $search = search_autocomplete(); exit(json_encode($search)); }

$ db = mysqli_connect ("localhost" , "root" , "" , "world" ) or die ("Нет соединения с БД" ) ;

mysqli_set_charset ($ db , "utf8" ) or die ("Не установлена кодировка соединения" ) ;

* поиск автокомплит

function search_autocomplete () {

global $ db ;

$ search = trim (mysqli_real_escape_string ($ db , $ _GET [ "term" ] ) ) ;

$ res = mysqli_query ($ db , $ query ) ;

$ result_search = array () ;

while ($ row = mysqli_fetch_assoc ($ res ) ) {

$ result_search = array ("label" = > $ row [ "Name" ] ) ;

return $ result_search ;

if (! empty ($ _GET [ "term" ] ) ) {

$ search = search_autocomplete () ;

exit (json_encode ($ search ) ) ;

Обратите внимание, функция search_autocomplete, которая и получает данные по запросу, должна возвращать эти данные в определенном формате, это должен быть массив с ключами label и значениями найденных городов. После вызова функции данные необходимо преобразовать в формат JSON.

Остается проверить работу нашего живого поиска. Для этого, как и в прошлый раз, наберем всего одну букву — a:

Отлично! В ответ мы получили десяток городов, в названии которых встречается введенная буква. Если мы продолжим набирать название, список вариантов будет изменяться, т.е. с каждой буквой будет отправляться новый запрос AJAX.

By Ibrahim Diallo

Published Jul 2 2014 ~ 16 minutes read

Search is an important feature on a website. When my few readers want to look for a particular passage on my blog, they use the search box. It used to be powered by Google Search, but I have since then changed it to my own home-brewed version not because I can do better but because it was an interesting challenge.

If you are in a hurry and just want your site to be searchable, well do what I did before, use Google.

// In search.php file $term = isset($_GET["query"])?$_GET["query"]: ""; $term = urlencode($term); $website = urlencode("www.yourwebsite.com"); $redirect = "https://www.google.com/search?q=site%3A{$website}+{$term}"; header("Location: $redirect"); exit;

What it does is pretty simple. Get the term passed by the user, and forward it to Google search page. Limit the search result to our current domain using the site: keyword in the search query. All your pages that are indexed by Google will be available through search now. If you do want to handle your search in house however, then keep reading.

Homemade Search Solution

Before we go any further, try using the search box on this blog. It uses the same process that I will describe below. If you feel that this is what you want then please continue reading.

This solution is catered to small websites. I make use of LIKE with wild cards on both ends, meaning your search cannot be indexed. This means the solution will work fine for your blog or personal website that doesn"t contain tons of data. Port it to a bigger website and it might become very slow. MySQL offers Full Text Search which is not what we are doing here.

Note: If you have 5000 blog posts you are still fine. .

We will take the structure of this blog as a reference. Each blog post has:

  • A title p_title
  • A url p_url
  • A summary p_summary
  • A post content p_content
  • And catergories category.tagname

For every field that matches with our search term, we will give it a score. The score will be based on the importance of the match:

// the exact term matches is found in the title $scoreFullTitle = 6; // match the title in part $scoreTitleKeyword = 5; // the exact term matches is found in the summary $scoreFullSummary = 5; // match the summary in part $scoreSummaryKeyword = 4; // the exact term matches is found in the content $scoreFullDocument = 4; // match the document in part $scoreDocumentKeyword = 3; // matches a category $scoreCategoryKeyword = 2; // matches the url $scoreUrlKeyword = 1;

Before we get started, there are a few words that do not contribute much to a search that should be removed. Example "in","it","a","the","of" ... . We will filter those out and feel free to add any word you think is irrelevant. Another thing is, we want to limit the length of our query. We don"t want a user to write a novel in the search field and crash our MySQL server.

// Remove unnecessary words from the search term and return them as an array function filterSearchKeys($query){ $query = trim(preg_replace("/(\s+)+/", " ", $query)); $words = array(); // expand this list with your words. $list = array("in","it","a","the","of","or","I","you","he","me","us","they","she","to","but","that","this","those","then"); $c = 0; foreach(explode(" ", $query) as $key){ if (in_array($key, $list)){ continue; } $words = $key; if ($c >= 15){ break; } $c++; } return $words; } // limit words number of characters function limitChars($query, $limit = 200){ return substr($query, 0,$limit); }

Our helper functions can now limit character count and filter useless words. The way we will implement our algorithm is by giving a score every time we find a match. We will match words using the if statement and accumulate points as we match more words. At the end we can use that score to sort our results

Note: I will not be showing how to connect to MySQL database. If you are having problems to efficiently connect to the database I recommend reading this .

Let"s give our function a structure first. Note I left placeholders so we can implement sections separately.

Function search($query){ $query = trim($query); if (mb_strlen($query)===0){ // no need for empty search right? return false; } $query = limitChars($query); // Weighing scores $scoreFullTitle = 6; $scoreTitleKeyword = 5; $scoreFullSummary = 5; $scoreSummaryKeyword = 4; $scoreFullDocument = 4; $scoreDocumentKeyword = 3; $scoreCategoryKeyword = 2; $scoreUrlKeyword = 1; $keywords = filterSearchKeys($query); $escQuery = DB::escape($query); // see note above to get db object $titleSQL = array(); $sumSQL = array(); $docSQL = array(); $categorySQL = array(); $urlSQL = array(); /** Matching full occurrences PLACE HOLDER **/ /** Matching Keywords PLACE HOLDER **/ $sql = "SELECT p.p_id,p.p_title,p.p_date_published,p.p_url, p.p_summary,p.p_content,p.thumbnail, ((-- Title score ".implode(" + ", $titleSQL).")+ (-- Summary ".implode(" + ", $sumSQL).")+ (-- document ".implode(" + ", $docSQL).")+ (-- tag/category ".implode(" + ", $categorySQL).")+ (-- url ".implode(" + ", $urlSQL).")) as relevance FROM post p WHERE p.status = "published" HAVING relevance >

In the query, all scores will be summed up as the relevance variable and we can use it to sort the results.

Matching full occurrences

We make sure we have some keywords first then add our query.

If (count($keywords) > 1){ $titleSQL = "if (p_title LIKE "%".$escQuery."%",{$scoreFullTitle},0)"; $sumSQL = "if (p_summary LIKE "%".$escQuery."%",{$scoreFullSummary},0)"; $docSQL = "if (p_content LIKE "%".$escQuery."%",{$scoreFullDocument},0)"; }

Those are the matches with higher score. If the search term matches an article that contains these, they will have higher chances of appearing on top.

Matching keywords occurrences

We loop through all keywords and check if they match any of the fields. For the category match, I used a sub-query since a post can have multiple categories.

Foreach($keywords as $key){ $titleSQL = "if (p_title LIKE "%".DB::escape($key)."%",{$scoreTitleKeyword},0)"; $sumSQL = "if (p_summary LIKE "%".DB::escape($key)."%",{$scoreSummaryKeyword},0)"; $docSQL = "if (p_content LIKE "%".DB::escape($key)."%",{$scoreDocumentKeyword},0)"; $urlSQL = "if (p_url LIKE "%".DB::escape($key)."%",{$scoreUrlKeyword},0)"; $categorySQL = "if ((SELECT count(category.tag_id) FROM category JOIN post_category ON post_category.tag_id = category.tag_id WHERE post_category.post_id = p.post_id AND category.name = "".DB::escape($key)."") > 0,{$scoreCategoryKeyword},0)"; }

Also as pointed by a commenter below, we have to make sure that the these variables are not empty arrays or the query will fail.

// Just incase it"s empty, add 0 if (empty($titleSQL)){ $titleSQL = 0; } if (empty($sumSQL)){ $sumSQL = 0; } if (empty($docSQL)){ $docSQL = 0; } if (empty($urlSQL)){ $urlSQL = 0; } if (empty($tagSQL)){ $tagSQL = 0; }

At the end the queries are all concatenated and added together to determine the relevance of the post to the search term.

// Remove unnecessary words from the search term and return them as an array function filterSearchKeys($query){ $query = trim(preg_replace("/(\s+)+/", " ", $query)); $words = array(); // expand this list with your words. $list = array("in","it","a","the","of","or","I","you","he","me","us","they","she","to","but","that","this","those","then"); $c = 0; foreach(explode(" ", $query) as $key){ if (in_array($key, $list)){ continue; } $words = $key; if ($c >= 15){ break; } $c++; } return $words; } // limit words number of characters function limitChars($query, $limit = 200){ return substr($query, 0,$limit); } function search($query){ $query = trim($query); if (mb_strlen($query)===0){ // no need for empty search right? return false; } $query = limitChars($query); // Weighing scores $scoreFullTitle = 6; $scoreTitleKeyword = 5; $scoreFullSummary = 5; $scoreSummaryKeyword = 4; $scoreFullDocument = 4; $scoreDocumentKeyword = 3; $scoreCategoryKeyword = 2; $scoreUrlKeyword = 1; $keywords = filterSearchKeys($query); $escQuery = DB::escape($query); // see note above to get db object $titleSQL = array(); $sumSQL = array(); $docSQL = array(); $categorySQL = array(); $urlSQL = array(); /** Matching full occurences **/ if (count($keywords) > 1){ $titleSQL = "if (p_title LIKE "%".$escQuery."%",{$scoreFullTitle},0)"; $sumSQL = "if (p_summary LIKE "%".$escQuery."%",{$scoreFullSummary},0)"; $docSQL = "if (p_content LIKE "%".$escQuery."%",{$scoreFullDocument},0)"; } /** Matching Keywords **/ foreach($keywords as $key){ $titleSQL = "if (p_title LIKE "%".DB::escape($key)."%",{$scoreTitleKeyword},0)"; $sumSQL = "if (p_summary LIKE "%".DB::escape($key)."%",{$scoreSummaryKeyword},0)"; $docSQL = "if (p_content LIKE "%".DB::escape($key)."%",{$scoreDocumentKeyword},0)"; $urlSQL = "if (p_url LIKE "%".DB::escape($key)."%",{$scoreUrlKeyword},0)"; $categorySQL = "if ((SELECT count(category.tag_id) FROM category JOIN post_category ON post_category.tag_id = category.tag_id WHERE post_category.post_id = p.post_id AND category.name = "".DB::escape($key)."") > 0,{$scoreCategoryKeyword},0)"; } // Just incase it"s empty, add 0 if (empty($titleSQL)){ $titleSQL = 0; } if (empty($sumSQL)){ $sumSQL = 0; } if (empty($docSQL)){ $docSQL = 0; } if (empty($urlSQL)){ $urlSQL = 0; } if (empty($tagSQL)){ $tagSQL = 0; } $sql = "SELECT p.p_id,p.p_title,p.p_date_published,p.p_url, p.p_summary,p.p_content,p.thumbnail, ((-- Title score ".implode(" + ", $titleSQL).")+ (-- Summary ".implode(" + ", $sumSQL).")+ (-- document ".implode(" + ", $docSQL).")+ (-- tag/category ".implode(" + ", $categorySQL).")+ (-- url ".implode(" + ", $urlSQL).")) as relevance FROM post p WHERE p.status = "published" HAVING relevance > 0 ORDER BY relevance DESC,p.page_views DESC LIMIT 25"; $results = DB::query($sql); if (!$results){ return false; } return $results; }

Now your search.php file can look like this:

$term = isset($_GET["query"])?$_GET["query"]: ""; $search_results = search($term); if (!$search_results) { echo "No results"; exit; } // Print page with results here.

We created a simple search algorithm that can handle a fair amount of content. I arbitrarily chose the score for each match, feel free to tweak it to something that works best for you. And there is always room for improvement.

It is a good idea to track the search term coming from your users, this way you can see if most users search for the same thing. If there is a pattern, then you can save them a trip and just cache the results using Memcached .

If you want to see this search algorithm in action, go ahead and try looking for an article on the search box on top of the page. I have added extra features like returning the part where the match was found in the text. Feel free to add features to yours.

Did you like this article? You can subscribe to read more awesome ones. .

On a related note, here are some interesting articles.

If there is one thing a web server does everyday it iss connecting to the database. I have been using PHP for many years now, but if you ask me to write a script to fetch data from the database I couldn"t do it without going back to the Ultimate PHP manual to find a few examples first.

A few months ago I updated my PHP version. PHP 5.4 to 5.5 . I never had any problems updating PHP before. I follow its development closely and try to remove my deprecated functions long before they are officially removed. But this time I was caught off guard. It silently broke part of my website for the silliest reason.

Comments(45)

Zaryel Aug 12 2015:

Ian Mustafa Sep 26 2015:

Rob Sep 29 2015:

adeem Feb 11 2016:

Ivan Venediktov Apr 9 2016.

В этой статье я покажу, как можно создавать многоуровневое меню на PHP и MySQL . Безусловно, вариантов его создания можно придумать много, но, судя по количеству Ваших вопросов на эту тему, Вам нужен пример. И его я приведу в этой статье. Сразу отмечу, что данная статья имеет смысл только для тех, кто знает PHP и умеет работать с MySQL . Всем остальным сначала надо пройти этот , либо прочитать какие-нибудь книги по PHP и MySQL .

Для начала создадим таблицу в базе данных со следующими полями:

  • id - уникальный идентификатор.
  • title - анкор ссылки в меню.
  • link - адрес, на который будет вести пункт меню.
  • parent_id - родительский ID. Если родительского пункта нет, то здесь будет NULL (либо можно ещё 0 поставить).

С таблицей разобрались, теперь пришло время PHP-кода . Полный PHP-код приведён ниже:

$mysqli = new mysqli("localhost", "root", "", "db"); // Подключаемся к БД
$result_set = $mysqli->query("SELECT * FROM `menu`"); // Делаем выборку всех записей из таблицы с меню
$items = array(); // Массив для пунктов меню
while (($row = $result_set->fetch_assoc()) != false) $items[$row["id"]] = $row; // Заполняем массив выборкой из БД
$childrens = array(); // Массив для соответствий дочерних элементов их родительским
foreach ($items as $item) {
if ($item["parent_id"]) $childrens[$item["id"]] = $item["parent_id"]; // Заполняем массив
}
function printItem($item, $items, $childrens) {
/* Выводим пункт меню */
echo "

  • ";
    echo "".$item["title"]."";
    $ul = false; // Выводились ли дочерние элементы?
    while (true) {
    /* Бесконечный цикл, в котором мы ищем все дочерние элементы */
    $key = array_search($item["id"], $childrens); // Ищем дочерний элемент
    if (!$key) {
    /* Дочерних элементов не найдено */
    if ($ul) echo ""; // Если выводились дочерние элементы, то закрываем список
    break; // Выходим из цикла
    }
    unset($childrens[$key]); // Удаляем найденный элемент (чтобы он не выводился ещё раз)
    if (!$ul) {
    echo "
      "; // Начинаем внутренний список, если дочерних элементов ещё не было
      $ul = true; // Устанавливаем флаг
      }
      echo printItem($items[$key], $items, $childrens); // Рекурсивно выводим все дочерние элементы
      }
      echo "";
      }
      ?>

      Этот код полностью рабочий, однако, Вы должны понимать, что так никто не пишет (в частности, вывод через echo HTML-тегов ). И Ваша задача взять алгоритм из этого кода, но не сам код. А дальше этот алгоритм подключить к своему движку. Я постарался тщательно прокомментировать код вывода многоуровневого меню на PHP и MySQL , но, безусловно, он не самый прозрачный и требует уже неплохих начальных знаний. Если Вы ещё плохо знаете PHP и MySQL , то сначала настоятельно рекомендую пройти этот

      template

      Имя шаблона, по которому следует выводить результаты поиска по сайту. В XSLT-шаблонизаторе игнорируется.

      Search_string

      Поисковая фраза. Если значение не задано, оно берётся из переданного через форму поиска запроса.

      Search_types

      Список идентификаторов иерархических типов для поиска (указываются через пробел). Если значение не указано, поиск оcуществляется по всем типам.

      Search_branches

      Список разделов в которых будет осуществляться поиск (указываются через пробел). Если значение не указано, поиск осуществляется по всем разделам. Параметр может принимать как id страниц, так и их URL.

      Per_page

      Количество результатов на странице. Если параметр не задан, будет взято значение, указанное в настройках модуля "Поиск".

      %total%

      Выводит общее количество новостей в ленте. Можно использовать для макроса %system numpages()% .

      %per_page%

      Выводит значение параметра per_page. Можно использовать для макроса %system numpages()% .

      %list-class-first%

      в случае, если элемент первый, выводит "first"

      %list-class-last%

      в случае, если элемент последний, выводит "last"

      %list-class-odd%

      в случае, если элемент четный, выводит "odd"

      %list-class-even%

      в случае, если элемент нечетный, выводит "even"

      %list-position%

      вставляет порядковый номер в списке

      search_empty_result

      Используется в том случае, если в результате поиска не найдено ни одной страницы. В таком случае этот блок выводится вместо блока search_block .

      %last_search_string%

      Выводит предыдущий поисковый запрос, если такой был.

      search_block_line_quant

      Выводит некий разделитель, который вставляется между результатами поиска.

      Примеры использования

      Найдено %total% страниц.

      %lines%

      %system numpages(%total%, %per_page%)%

      END; $FORMS ["search_block_line" ] = << %num%. %name% %context%

      END; $FORMS ["search_empty_result" ] = <<Извините. По данному запросу ничего не найдено.

      END; ?>