本文目录导读:

PHP API 搜索建议通常通过 AJAX 异步请求 + 后端模糊查询 来实现,下面我会提供一个完整的实现方案,包括前端和后端代码。
数据库设计
假设我们有一个 products 表:
CREATE TABLE products (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) NOT NULL,
category VARCHAR(100),
price DECIMAL(10,2),
status TINYINT DEFAULT 1,
INDEX idx_name (name)
);
PHP API 后端实现
1 搜索建议 API (search_suggest.php)
<?php
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET');
// 数据库连接配置
$host = 'localhost';
$dbname = 'your_database';
$username = 'your_username';
$password = 'your_password';
try {
$pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8", $username, $password);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
echo json_encode(['error' => '数据库连接失败']);
exit;
}
// 获取搜索关键词
$keyword = isset($_GET['q']) ? trim($_GET['q']) : '';
if (empty($keyword)) {
echo json_encode(['suggestions' => []]);
exit;
}
// 防止SQL注入,使用预处理语句
$stmt = $pdo->prepare("
SELECT DISTINCT name, category
FROM products
WHERE name LIKE :keyword
OR category LIKE :keyword
ORDER BY
CASE
WHEN name LIKE :exact THEN 0
WHEN name LIKE :start THEN 1
ELSE 2
END,
name ASC
LIMIT 10
");
$likeKeyword = '%' . $keyword . '%';
$exactKeyword = $keyword . '%';
$startKeyword = $keyword . '%';
$stmt->execute([
':keyword' => $likeKeyword,
':exact' => $exactKeyword,
':start' => $startKeyword
]);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 格式化返回数据
$suggestions = [];
foreach ($results as $row) {
$suggestions[] = [
'value' => $row['name'],
'category' => $row['category'],
'label' => highlightKeyword($row['name'], $keyword)
];
}
echo json_encode([
'success' => true,
'suggestions' => $suggestions,
'keyword' => $keyword
]);
/**
* 高亮搜索关键词
*/
function highlightKeyword($text, $keyword) {
return preg_replace('/(' . preg_quote($keyword, '/') . ')/iu', '<strong>$1</strong>', $text);
}
2 优化版本:使用缓存提升性能
<?php
// search_suggest_cached.php
// 设置缓存
$cacheFile = __DIR__ . '/cache/suggestions_' . md5($keyword) . '.json';
$cacheTime = 300; // 5分钟缓存
if (file_exists($cacheFile) && (time() - filemtime($cacheFile)) < $cacheTime) {
echo file_get_contents($cacheFile);
exit;
}
// ... 数据库查询代码 ...
// 保存到缓存
file_put_contents($cacheFile, json_encode($response));
echo json_encode($response);
前端实现
1 HTML + JavaScript 实现
<!DOCTYPE html>
<html>
<head>
<style>
.search-container {
position: relative;
width: 300px;
}
.search-input {
width: 100%;
padding: 10px;
font-size: 16px;
border: 2px solid #ddd;
border-radius: 4px;
}
.suggestions-box {
position: absolute;
top: 100%;
left: 0;
right: 0;
background: white;
border: 1px solid #ddd;
border-top: none;
max-height: 300px;
overflow-y: auto;
display: none;
z-index: 1000;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.suggestion-item {
padding: 10px;
cursor: pointer;
border-bottom: 1px solid #eee;
}
.suggestion-item:hover {
background: #f5f5f5;
}
.suggestion-item strong {
color: #ff6b6b;
}
.no-results {
padding: 10px;
color: #999;
text-align: center;
}
</style>
</head>
<body>
<div class="search-container">
<input type="text"
id="searchInput"
class="search-input"
placeholder="搜索产品..."
autocomplete="off">
<div id="suggestionsBox" class="suggestions-box"></div>
</div>
<script>
class SearchSuggest {
constructor(options) {
this.input = document.getElementById(options.inputId);
this.suggestionsBox = document.getElementById(options.suggestionsBox);
this.apiUrl = options.apiUrl;
this.debounceTimer = null;
this.debounceDelay = options.debounceDelay || 300;
this.minChars = options.minChars || 2;
this.maxSuggestions = options.maxSuggestions || 10;
this.init();
}
init() {
// 输入事件监听
this.input.addEventListener('input', (e) => {
this.debounceSearch(e.target.value);
});
// 失焦隐藏建议框
this.input.addEventListener('blur', () => {
setTimeout(() => this.hideSuggestions(), 200);
});
// 获取焦点时如果有关键词则显示
this.input.addEventListener('focus', () => {
if (this.input.value.length >= this.minChars) {
this.search(this.input.value);
}
});
// 键盘事件
this.input.addEventListener('keydown', (e) => {
if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
e.preventDefault();
this.navigateSuggestions(e.key);
} else if (e.key === 'Enter') {
this.selectCurrentSuggestion();
} else if (e.key === 'Escape') {
this.hideSuggestions();
}
});
}
debounceSearch(value) {
clearTimeout(this.debounceTimer);
if (value.length < this.minChars) {
this.hideSuggestions();
return;
}
this.debounceTimer = setTimeout(() => {
this.search(value);
}, this.debounceDelay);
}
async search(keyword) {
try {
const response = await fetch(`${this.apiUrl}?q=${encodeURIComponent(keyword)}`);
const data = await response.json();
if (data.success) {
this.displaySuggestions(data.suggestions);
}
} catch (error) {
console.error('搜索建议请求失败:', error);
}
}
displaySuggestions(suggestions) {
if (!suggestions || suggestions.length === 0) {
this.suggestionsBox.innerHTML = '<div class="no-results">无匹配结果</div>';
this.showSuggestions();
return;
}
// 限制显示数量
const limitedSuggestions = suggestions.slice(0, this.maxSuggestions);
let html = '';
limitedSuggestions.forEach((item, index) => {
html += `
<div class="suggestion-item" data-index="${index}" data-value="${item.value}">
<div>${item.label}</div>
<small style="color: #999;">${item.category || ''}</small>
</div>
`;
});
this.suggestionsBox.innerHTML = html;
this.showSuggestions();
// 点击选择
this.suggestionsBox.querySelectorAll('.suggestion-item').forEach(item => {
item.addEventListener('mousedown', (e) => {
this.input.value = item.dataset.value;
this.hideSuggestions();
// 触发选择事件
this.onSelect && this.onSelect(item.dataset.value);
});
});
// 鼠标悬停效果
this.currentIndex = -1;
this.items = this.suggestionsBox.querySelectorAll('.suggestion-item');
}
navigateSuggestions(key) {
if (!this.items || this.items.length === 0) return;
// 移除当前高亮
if (this.currentIndex >= 0 && this.items[this.currentIndex]) {
this.items[this.currentIndex].style.background = '';
}
if (key === 'ArrowDown') {
this.currentIndex = Math.min(this.currentIndex + 1, this.items.length - 1);
} else if (key === 'ArrowUp') {
this.currentIndex = Math.max(this.currentIndex - 1, -1);
}
// 高亮当前项
if (this.currentIndex >= 0 && this.items[this.currentIndex]) {
this.items[this.currentIndex].style.background = '#f0f0f0';
// 滚动到可见区域
this.items[this.currentIndex].scrollIntoView({
block: 'nearest'
});
}
}
selectCurrentSuggestion() {
if (this.currentIndex >= 0 && this.items[this.currentIndex]) {
const value = this.items[this.currentIndex].dataset.value;
this.input.value = value;
this.hideSuggestions();
this.onSelect && this.onSelect(value);
}
}
showSuggestions() {
this.suggestionsBox.style.display = 'block';
}
hideSuggestions() {
this.suggestionsBox.style.display = 'none';
this.currentIndex = -1;
}
// 设置选择回调
onSelect(callback) {
this.onSelect = callback;
}
}
// 使用示例
const searchSuggest = new SearchSuggest({
inputId: 'searchInput',
suggestionsBox: 'suggestionsBox',
apiUrl: 'search_suggest.php',
debounceDelay: 300,
minChars: 2,
maxSuggestions: 10
});
// 选择回调
searchSuggest.onSelect((value) => {
console.log('选择了:', value);
// 这里可以执行搜索或跳转
});
</script>
</body>
</html>
高级优化方案
1 使用 Redis 缓存
<?php
// 使用 Redis 缓存搜索建议
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$cacheKey = 'suggest:' . md5($keyword);
$cached = $redis->get($cacheKey);
if ($cached) {
echo $cached;
exit;
}
// 执行数据库查询
// ...
// 缓存结果,设置过期时间为300秒
$redis->setex($cacheKey, 300, json_encode($response));
echo json_encode($response);
2 使用 Elasticsearch 实现高级搜索
<?php
require 'vendor/autoload.php';
use Elasticsearch\ClientBuilder;
$client = ClientBuilder::create()->build();
$params = [
'index' => 'products',
'body' => [
'size' => 10,
'query' => [
'multi_match' => [
'query' => $keyword,
'fields' => ['name^3', 'category^2', 'description'],
'type' => 'phrase_prefix',
'fuzziness' => 'AUTO'
]
],
'suggest' => [
'name-suggest' => [
'text' => $keyword,
'term' => [
'field' => 'name'
]
]
]
]
];
$response = $client->search($params);
性能优化建议
- 添加索引:确保搜索字段有适当的索引
- 限制结果数量:通常返回5-10条建议
- 使用缓存:减少数据库查询
- 防抖处理:减少请求频率
- 压缩数据:使用gzip压缩返回数据
- CDN加速:静态资源使用CDN
完整示例下载
您可以将上述代码保存为以下文件:
search_suggest.php- PHP APIindex.html- 前端页面
这个实现方案具有良好的用户体验和性能表现,适用于大多数搜索建议场景。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播