Laravel 使用运算符 AND/OR 在 multi table 上搜索多个关键字
Laravel multiple keywords search on multi table with operator AND/OR
我有一个高级搜索表单,可以从 mySql 数据库中过滤结果。其中一部分是关键字搜索,应匹配所有相关 table 上的任何文本字段。
这是上述搜索表单代码。
<form action="/search" method="POST">
Keyword: <input type="text" name="keyword">
Matching: <input type="radio" name="keywordControl" value="AND"> Match every words<br>
<input type="radio" name="keywordControl" value="OR"> Match any words<br>
<input type="submit">
</form>
表格:
CREATE TABLE `User` (
`id` INT,
`firstName` VARCHAR(100),
`lastName` VARCHAR(100),
`email` VARCHAR(100),
`address` VARCHAR(400)
);
CREATE TABLE `savedLinks` (
`id` INT,
`userId` INT,
`linkName` VARCHAR(100),
`linkURL` VARCHAR(400),
`linkNote` VARCHAR(100),
);
CREATE TABLE `BlogEntry` (
`id` INT,
`userId` INT,
`entryTitle` VARCHAR(100),
`entryExcerpt` VARCHAR(100),
`entryBody` TEXT,
);
我需要的:
如果我按这些参数搜索:
关键字:火影忍者区51
关键字控制:AND
我希望在所有 table 中看到与火影忍者和 area51 匹配的结果。
但是,我能做的是在相同的 table 上过滤结果。我设法显示
这样的结果
"Mister Naruto",地址为“Area51 lane...”
任何保存包含“火影忍者”和“Area51”
[的链接的人=73=]
任何写过关于“火影忍者 运行”和“Area51”博客的人
但我无法带来像
这样的结果
"Mister Naruto",他的博文是关于“Area51”
任何保存了关于“火影忍者”的链接并发布了关于“Area51”的博客的人
因为后者需要搜索所有 table。这是我的 laravel eloquent 代码。
$keyword = urldecode($request->input('keyword'));
$keywords = preg_split('/\s+/', $keyword, -1, PREG_SPLIT_NO_EMPTY);
$users = User::when(sizeof($keywords) > 0, function ($query) use ($keywords, $keywordControl) {
$query->where(function ($query) use ($keywords, $keywordControl) {
foreach ($keywords as $keyword) {
if ($keywordControl == 'AND') {
$query->whereRaw("CONCAT(firstName,lastName,email,address) LIKE ?", "%{$keyword}%");
} else {
$query->orWhereRaw("CONCAT(firstName,lastName,email,address) LIKE ?", "%{$keyword}%");
}
}
});
})
->when(sizeof($keywords) > 0, function ($query) use ($keywords, $keywordControl) {
$query->whereHas('savedLinks', function ($query) use ($keywords, $keywordControl) {
$query->where(function ($query) use ($keywords, $keywordControl) {
foreach ($keywords as $keyword) {
if($keywordControl == 'AND'){
$query->whereRaw("CONCAT(linkName,linkURL,linkNote) LIKE ?", "%{$keyword}%");
} else {
$query->orWhereRaw("CONCAT(linkName,linkURL,linkNote) LIKE ?", "%{$keyword}%");
}
}
});
});
})
->when(sizeof($keywords) > 0, function ($query) use ($keywords, $keywordControl) {
$query->whereHas('blogEntry', function ($query) use ($keywords, $keywordControl) {
$query->where(function ($query) use ($keywords, $keywordControl) {
foreach ($keywords as $keyword) {
if($keywordControl=='AND') {
$query->whereRaw("CONCAT(entryTitle,entryExcerpt,entryBody) LIKE ?", "%{$keyword}%");
} else {
$query->orWhereRaw("CONCAT(entryTitle,entryExcerpt,entryBody) LIKE ?", "%{$keyword}%");
}
}
});
});
})
->get();
我的想法是,或者如果我应该创建另一个 table 并将所有这些文本连接到同一字段中仅用于搜索目的。我不知道这是不是个好主意。请指导我,谢谢!
您是否定义了 User、SavedLinks 和 Blog 的关系?对于此查询,用户必须与相关模型具有正确的关系:
# User model
public function savedLinks()
{
return $this->hasMany(SavedLink::class, 'userId', 'id');
}
public function blogEntries()
{
return $this->hasMany(BlogEntry::class, 'userId', 'id');
}
你所有的when(...)
条件都是一样的。可以重写此查询以考虑到这一点。
此外,whereRaw()
接受 3 参数。如果您选中 it's definition,您将看到以下内容:
/**
* Add a raw where clause to the query.
*
* @param string $sql
* @param mixed $bindings
* @param string $boolean
* @return $this
*/
public function whereRaw($sql, $bindings = [], $boolean = 'and')
{
$this->wheres[] = ['type' => 'raw', 'sql' => $sql, 'boolean' => $boolean];
$this->addBinding((array) $bindings, 'where');
return $this;
}
/**
* Add a raw or where clause to the query.
*
* @param string $sql
* @param mixed $bindings
* @return \Illuminate\Database\Query\Builder|static
*/
public function orWhereRaw($sql, $bindings = [])
{
return $this->whereRaw($sql, $bindings, 'or');
}
在我看来你也应该使用 orWhereHas
。您可以使用 empty()
而不是 sizeof()
和 unless()
(it's just the opposite function) 而不是 when()
来使内容更具可读性。考虑到这一点,您的查询已经可以减少到这个,假设 $keywordControl
是 AND 或 OR:
$users = User::unless(empty($keywords), function ($query) use ($keywords, $keywordControl) {
$query->where(function ($query) use ($keywords, $keywordControl) {
foreach ($keywords as $keyword) {
$query->whereRaw("CONCAT(firstName,lastName,email,address) LIKE ?", "%{$keyword}%", $keywordControl);
}
});
})
->orWhereHas('savedLinks', function ($query) use ($keywords, $keywordControl) {
$query->where(function ($query) use ($keywords, $keywordControl) {
foreach ($keywords as $keyword) {
$query->whereRaw("CONCAT(linkName,linkURL,linkNote) LIKE ?", "%{$keyword}%", $keywordControl);
}
});
})
->orWhereHas('blogEntry', function ($query) use ($keywords, $keywordControl) {
$query->where(function ($query) use ($keywords, $keywordControl) {
foreach ($keywords as $keyword) {
$query->whereRaw("CONCAT(entryTitle,entryExcerpt,entryBody) LIKE ?", "%{$keyword}%", $keywordControl);
}
});
})
->get();
我不知道用 whereRaw 连接然后比较它是否比对每个属性使用 where 子句更快。
# Example
->whereRaw("CONCAT(entryTitle,entryExcerpt,entryBody) LIKE ?", "%{$keyword}%", $keywordControl);
# vs
->where([
['entryTitle', 'like', "%{keyword}%", $keywordControl],
['entryExcerpt', 'like', "%{keyword}%", $keywordControl],
['entryBody', 'like', "%{keyword}%", $keywordControl],
])
# If someone could comment on this I'd be grateful.
我有一个高级搜索表单,可以从 mySql 数据库中过滤结果。其中一部分是关键字搜索,应匹配所有相关 table 上的任何文本字段。
这是上述搜索表单代码。
<form action="/search" method="POST">
Keyword: <input type="text" name="keyword">
Matching: <input type="radio" name="keywordControl" value="AND"> Match every words<br>
<input type="radio" name="keywordControl" value="OR"> Match any words<br>
<input type="submit">
</form>
表格:
CREATE TABLE `User` (
`id` INT,
`firstName` VARCHAR(100),
`lastName` VARCHAR(100),
`email` VARCHAR(100),
`address` VARCHAR(400)
);
CREATE TABLE `savedLinks` (
`id` INT,
`userId` INT,
`linkName` VARCHAR(100),
`linkURL` VARCHAR(400),
`linkNote` VARCHAR(100),
);
CREATE TABLE `BlogEntry` (
`id` INT,
`userId` INT,
`entryTitle` VARCHAR(100),
`entryExcerpt` VARCHAR(100),
`entryBody` TEXT,
);
我需要的:
如果我按这些参数搜索:
关键字:火影忍者区51
关键字控制:AND
我希望在所有 table 中看到与火影忍者和 area51 匹配的结果。
但是,我能做的是在相同的 table 上过滤结果。我设法显示
这样的结果"Mister Naruto",地址为“Area51 lane...”
任何保存包含“火影忍者”和“Area51”
[的链接的人=73=]任何写过关于“火影忍者 运行”和“Area51”博客的人
但我无法带来像
这样的结果"Mister Naruto",他的博文是关于“Area51”
任何保存了关于“火影忍者”的链接并发布了关于“Area51”的博客的人
因为后者需要搜索所有 table。这是我的 laravel eloquent 代码。
$keyword = urldecode($request->input('keyword'));
$keywords = preg_split('/\s+/', $keyword, -1, PREG_SPLIT_NO_EMPTY);
$users = User::when(sizeof($keywords) > 0, function ($query) use ($keywords, $keywordControl) {
$query->where(function ($query) use ($keywords, $keywordControl) {
foreach ($keywords as $keyword) {
if ($keywordControl == 'AND') {
$query->whereRaw("CONCAT(firstName,lastName,email,address) LIKE ?", "%{$keyword}%");
} else {
$query->orWhereRaw("CONCAT(firstName,lastName,email,address) LIKE ?", "%{$keyword}%");
}
}
});
})
->when(sizeof($keywords) > 0, function ($query) use ($keywords, $keywordControl) {
$query->whereHas('savedLinks', function ($query) use ($keywords, $keywordControl) {
$query->where(function ($query) use ($keywords, $keywordControl) {
foreach ($keywords as $keyword) {
if($keywordControl == 'AND'){
$query->whereRaw("CONCAT(linkName,linkURL,linkNote) LIKE ?", "%{$keyword}%");
} else {
$query->orWhereRaw("CONCAT(linkName,linkURL,linkNote) LIKE ?", "%{$keyword}%");
}
}
});
});
})
->when(sizeof($keywords) > 0, function ($query) use ($keywords, $keywordControl) {
$query->whereHas('blogEntry', function ($query) use ($keywords, $keywordControl) {
$query->where(function ($query) use ($keywords, $keywordControl) {
foreach ($keywords as $keyword) {
if($keywordControl=='AND') {
$query->whereRaw("CONCAT(entryTitle,entryExcerpt,entryBody) LIKE ?", "%{$keyword}%");
} else {
$query->orWhereRaw("CONCAT(entryTitle,entryExcerpt,entryBody) LIKE ?", "%{$keyword}%");
}
}
});
});
})
->get();
我的想法是,或者如果我应该创建另一个 table 并将所有这些文本连接到同一字段中仅用于搜索目的。我不知道这是不是个好主意。请指导我,谢谢!
您是否定义了 User、SavedLinks 和 Blog 的关系?对于此查询,用户必须与相关模型具有正确的关系:
# User model
public function savedLinks()
{
return $this->hasMany(SavedLink::class, 'userId', 'id');
}
public function blogEntries()
{
return $this->hasMany(BlogEntry::class, 'userId', 'id');
}
你所有的when(...)
条件都是一样的。可以重写此查询以考虑到这一点。
此外,whereRaw()
接受 3 参数。如果您选中 it's definition,您将看到以下内容:
/**
* Add a raw where clause to the query.
*
* @param string $sql
* @param mixed $bindings
* @param string $boolean
* @return $this
*/
public function whereRaw($sql, $bindings = [], $boolean = 'and')
{
$this->wheres[] = ['type' => 'raw', 'sql' => $sql, 'boolean' => $boolean];
$this->addBinding((array) $bindings, 'where');
return $this;
}
/**
* Add a raw or where clause to the query.
*
* @param string $sql
* @param mixed $bindings
* @return \Illuminate\Database\Query\Builder|static
*/
public function orWhereRaw($sql, $bindings = [])
{
return $this->whereRaw($sql, $bindings, 'or');
}
在我看来你也应该使用 orWhereHas
。您可以使用 empty()
而不是 sizeof()
和 unless()
(it's just the opposite function) 而不是 when()
来使内容更具可读性。考虑到这一点,您的查询已经可以减少到这个,假设 $keywordControl
是 AND 或 OR:
$users = User::unless(empty($keywords), function ($query) use ($keywords, $keywordControl) {
$query->where(function ($query) use ($keywords, $keywordControl) {
foreach ($keywords as $keyword) {
$query->whereRaw("CONCAT(firstName,lastName,email,address) LIKE ?", "%{$keyword}%", $keywordControl);
}
});
})
->orWhereHas('savedLinks', function ($query) use ($keywords, $keywordControl) {
$query->where(function ($query) use ($keywords, $keywordControl) {
foreach ($keywords as $keyword) {
$query->whereRaw("CONCAT(linkName,linkURL,linkNote) LIKE ?", "%{$keyword}%", $keywordControl);
}
});
})
->orWhereHas('blogEntry', function ($query) use ($keywords, $keywordControl) {
$query->where(function ($query) use ($keywords, $keywordControl) {
foreach ($keywords as $keyword) {
$query->whereRaw("CONCAT(entryTitle,entryExcerpt,entryBody) LIKE ?", "%{$keyword}%", $keywordControl);
}
});
})
->get();
我不知道用 whereRaw 连接然后比较它是否比对每个属性使用 where 子句更快。
# Example
->whereRaw("CONCAT(entryTitle,entryExcerpt,entryBody) LIKE ?", "%{$keyword}%", $keywordControl);
# vs
->where([
['entryTitle', 'like', "%{keyword}%", $keywordControl],
['entryExcerpt', 'like', "%{keyword}%", $keywordControl],
['entryBody', 'like', "%{keyword}%", $keywordControl],
])
# If someone could comment on this I'd be grateful.