200 lines
6.4 KiB
PHP
200 lines
6.4 KiB
PHP
<?php
|
|
|
|
namespace App\Repository\Blog;
|
|
|
|
use App\Core\Repository\RepositoryQuery;
|
|
use App\Entity\Blog\Category;
|
|
use Knp\Component\Pager\PaginatorInterface;
|
|
|
|
/**
|
|
* class PostRepositoryQuery.
|
|
*
|
|
* @author Simon Vieille <simon@deblan.fr>
|
|
*/
|
|
class PostRepositoryQuery extends RepositoryQuery
|
|
{
|
|
public function __construct(PostRepository $repository, PaginatorInterface $paginator)
|
|
{
|
|
parent::__construct($repository, 'p', $paginator);
|
|
}
|
|
|
|
public function inCategory(Category $category, bool $strict = true): self
|
|
{
|
|
$c = 'c'.mt_rand();
|
|
|
|
if ($strict) {
|
|
$this
|
|
->innerJoin('p.categories', $c)
|
|
->andWhere($c.'.id = :category')
|
|
->setParameter(':category', $category->getId())
|
|
;
|
|
} else {
|
|
$ids = [$category->getId()];
|
|
|
|
foreach ($category->getCategories() as $childCategory) {
|
|
$ids[] = $childCategory->getId();
|
|
}
|
|
|
|
$this
|
|
->innerJoin('p.categories', $c)
|
|
->andWhere($c.'.id IN (:categories)')
|
|
->setParameter(':categories', $ids)
|
|
;
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function published(): self
|
|
{
|
|
return $this
|
|
->andWhere('.status = 1')
|
|
->andWhere('.publishedAt <= :now')
|
|
->setParameter(':now', (new \DateTime('now'))->format('Y-m-d H:i:s'))
|
|
;
|
|
}
|
|
|
|
public function search(?string $keywords, ?string $tag)
|
|
{
|
|
$keywords = explode(' ', $keywords);
|
|
|
|
$filterWords = fn ($keyword) => '' !== trim($keyword) && preg_match('/[a-zA-Z]+/', $keyword);
|
|
$keywords = array_filter($keywords, $filterWords);
|
|
|
|
if ($keywords) {
|
|
$conn = $this->repository->getEm()->getConnection();
|
|
|
|
$query = $conn->prepare(
|
|
'SELECT
|
|
post.id,
|
|
post.title,
|
|
post.content,
|
|
post.published_at
|
|
FROM post
|
|
WHERE
|
|
post.status = 1 AND
|
|
post.published_at < :date
|
|
'
|
|
);
|
|
|
|
$statement = $query->execute([
|
|
':date' => (new \DateTime())->format('Y-m-d H:i:s'),
|
|
]);
|
|
|
|
$results = $statement->fetchAll();
|
|
$ids = [];
|
|
$matches = [];
|
|
|
|
foreach ($results as $k => $v) {
|
|
$initWords = explode(' ', $v['title']);
|
|
$words = [];
|
|
|
|
foreach ($initWords as $initWord) {
|
|
$words = array_merge($words, preg_split('/[:_\'-]+/', $initWord));
|
|
}
|
|
|
|
$words = array_filter($words, $filterWords);
|
|
|
|
foreach ($keywords as $keyword) {
|
|
if (str_contains(mb_strtolower($v['content']), mb_strtolower($keyword))) {
|
|
$similarity = 99;
|
|
|
|
if (isset($matches[$v['id']])) {
|
|
$matches[$v['id']]['similarity'] += $similarity;
|
|
++$matches[$v['id']]['count'];
|
|
} else {
|
|
$matches[$v['id']] = [
|
|
'id' => $v['id'],
|
|
'title' => $v['title'],
|
|
'published_at' => $v['published_at'],
|
|
'similarity' => $similarity,
|
|
'count' => 1,
|
|
];
|
|
}
|
|
}
|
|
|
|
foreach ($words as $word) {
|
|
if (str_contains(mb_strtolower($word), mb_strtolower($keyword))) {
|
|
$similarity = 150;
|
|
|
|
if (isset($matches[$v['id']])) {
|
|
$matches[$v['id']]['similarity'] += $similarity;
|
|
++$matches[$v['id']]['count'];
|
|
} else {
|
|
$matches[$v['id']] = [
|
|
'id' => $v['id'],
|
|
'title' => $v['title'],
|
|
'published_at' => $v['published_at'],
|
|
'similarity' => $similarity,
|
|
'count' => 1,
|
|
];
|
|
}
|
|
} else {
|
|
$lev = levenshtein($word, $keyword);
|
|
$similarity = 100 - ($lev * 100 / mb_strlen($word));
|
|
|
|
if ($similarity > 70) {
|
|
if (isset($matches[$v['id']])) {
|
|
$matches[$v['id']]['similarity'] += $similarity;
|
|
} else {
|
|
$matches[$v['id']] = [
|
|
'id' => $v['id'],
|
|
'title' => $v['title'],
|
|
'published_at' => $v['published_at'],
|
|
'similarity' => $similarity,
|
|
'count' => 1,
|
|
];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
$matches = array_filter($matches, function($match) use ($keywords) {
|
|
return (100 * $match['count'] / count($keywords)) > 80;
|
|
});
|
|
|
|
usort($matches, function ($a, $b) {
|
|
if ($a['similarity'] > $b['similarity']) {
|
|
return -1;
|
|
}
|
|
|
|
if ($b['similarity'] > $a['similarity']) {
|
|
return 1;
|
|
}
|
|
|
|
return ($a['published_at'] != $b['published_at']) * -1;
|
|
});
|
|
|
|
$ids = array_column($matches, 'id');
|
|
|
|
if (!$ids) {
|
|
$ids = [-1];
|
|
}
|
|
|
|
$this
|
|
->orderBy('FIELD(p.id, :ids)')
|
|
->andWhere('.id IN(:ids)')
|
|
->setParameter(':ids', $ids)
|
|
;
|
|
}
|
|
|
|
if ($tag) {
|
|
$this
|
|
->andWhere('.tags LIKE :tag')
|
|
->setParameter(':tag', '%"'.$tag.'"%')
|
|
;
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
protected function filterHandler(string $name, $value)
|
|
{
|
|
if ('category' === $name) {
|
|
$this->inCategory($value);
|
|
}
|
|
}
|
|
}
|