diff --git a/composer.json b/composer.json index 66f8e75..27674ce 100644 --- a/composer.json +++ b/composer.json @@ -7,6 +7,7 @@ "php": ">=7.2.5", "ext-ctype": "*", "ext-iconv": "*", + "beberlei/doctrineextensions": "^1.3", "bjeavons/zxcvbn-php": "^1.2", "cocur/slugify": "^4.0", "composer/package-versions-deprecated": "1.11.99.1", diff --git a/config/packages/doctrine.yaml b/config/packages/doctrine.yaml index 103af1c..4d98fe0 100644 --- a/config/packages/doctrine.yaml +++ b/config/packages/doctrine.yaml @@ -9,6 +9,9 @@ doctrine: auto_generate_proxy_classes: true naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware auto_mapping: true + dql: + string_functions: + FIELD: DoctrineExtensions\Query\Mysql\Field mappings: App\Core\Entity: is_bundle: false diff --git a/core/Repository/RepositoryQuery.php b/core/Repository/RepositoryQuery.php index 27addf9..05a4a86 100644 --- a/core/Repository/RepositoryQuery.php +++ b/core/Repository/RepositoryQuery.php @@ -28,28 +28,8 @@ abstract class RepositoryQuery public function __call(string $name, $params): self { - $fn = function (&$data) { - if (is_string($data)) { - $words = explode(' ', $data); - - foreach ($words as $k => $v) { - if (isset($v[0]) && '.' === $v[0]) { - $words[$k] = $this->id.$v; - } - } - - $data = implode(' ', $words); - } elseif (is_array($data)) { - foreach ($data as $k => $v) { - $fn($data[$k]); - } - } - - return $data; - }; - foreach ($params as $key => $value) { - $fn($params[$key]); + $this->populateDqlId($params[$key]); } call_user_func_array([$this->query, $name], $params); @@ -93,4 +73,25 @@ abstract class RepositoryQuery { return $this->repository; } + + protected function populateDqlId(&$data) + { + if (is_string($data)) { + $words = explode(' ', $data); + + foreach ($words as $k => $v) { + if (isset($v[0]) && '.' === $v[0]) { + $words[$k] = $this->id.$v; + } + } + + $data = implode(' ', $words); + } elseif (is_array($data)) { + foreach ($data as $k => $v) { + $this->populateDqlId($data[$k]); + } + } + + return $data; + } } diff --git a/src/Controller/Blog/PostController.php b/src/Controller/Blog/PostController.php index 5d64b84..73755bf 100644 --- a/src/Controller/Blog/PostController.php +++ b/src/Controller/Blog/PostController.php @@ -11,6 +11,7 @@ use App\Repository\Blog\PostRepositoryQuery; use App\UrlGenerator\PostGenerator; use Symfony\Component\HttpFoundation\Response; use App\Core\Site\SiteStore; +use Symfony\Component\HttpFoundation\Request; class PostController extends PageController { @@ -58,6 +59,23 @@ class PostController extends PageController ]); } + public function search(Request $request, int $page = 1): Response + { + $query = $request->query->get('query'); + + if ($query) { + $entities = $this->createQuery() + ->search($query) + ->paginate($page, 5) + ; + } + + return $this->defaultRender('blog/post/search.html.twig', [ + 'pager' => $entities ?? null, + 'query' => $query, + ]); + } + /** * @UrlGenerator(service=PostGenerator::class, method="category") */ diff --git a/src/Entity/Blog/Post.php b/src/Entity/Blog/Post.php index 2e0ab5d..6d9c9df 100644 --- a/src/Entity/Blog/Post.php +++ b/src/Entity/Blog/Post.php @@ -13,6 +13,10 @@ use Symfony\Component\HttpFoundation\File\File; /** * @ORM\Entity(repositoryClass=PostRepository::class) + * @ORM\Table(indexes={ + * @ORM\Index(name="post_title", columns={"title"}, flags={"fulltext"}), + * @ORM\Index(name="post_content", columns={"content"}, flags={"fulltext"}) + * }) * @ORM\HasLifecycleCallbacks */ class Post implements EntityInterface diff --git a/src/Repository/Blog/PostRepository.php b/src/Repository/Blog/PostRepository.php index d6a3032..a81a358 100644 --- a/src/Repository/Blog/PostRepository.php +++ b/src/Repository/Blog/PostRepository.php @@ -5,6 +5,7 @@ namespace App\Repository\Blog; use App\Entity\Blog\Post; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\Persistence\ManagerRegistry; +use App\Core\Manager\EntityManager; class PostRepository extends ServiceEntityRepository { @@ -12,4 +13,9 @@ class PostRepository extends ServiceEntityRepository { parent::__construct($registry, Post::class); } + + public function getEm() + { + return $this->getEntityManager(); + } } diff --git a/src/Repository/Blog/PostRepositoryQuery.php b/src/Repository/Blog/PostRepositoryQuery.php index 44ffd78..c8a0dda 100644 --- a/src/Repository/Blog/PostRepositoryQuery.php +++ b/src/Repository/Blog/PostRepositoryQuery.php @@ -4,7 +4,6 @@ namespace App\Repository\Blog; use App\Core\Repository\RepositoryQuery; use App\Entity\Blog\Category; -use App\Entity\User; use Knp\Component\Pager\PaginatorInterface; /** @@ -37,13 +36,14 @@ class PostRepositoryQuery extends RepositoryQuery return $this ->andWhere('.status = 1') ->andWhere('.publishedAt <= :now') - ->setParameter(':now', (new \DateTime('now'))->format('Y-m-d H:i:s')); + ->setParameter(':now', (new \DateTime('now'))->format('Y-m-d H:i:s')) + ; } public function useFilters(array $filters) { foreach ($filters as $name => $value) { - if ($value === null) { + if (null === $value) { continue; } @@ -54,7 +54,7 @@ class PostRepositoryQuery extends RepositoryQuery $this->andWhere('.'.$name.' LIKE :'.$name); $this->setParameter(':'.$name, '%'.$value.'%'); } else { - if ($name === 'category') { + if ('category' === $name) { $this->inCategory($value); } } @@ -62,4 +62,60 @@ class PostRepositoryQuery extends RepositoryQuery return $this; } + + public function search(string $keywords) + { + $conn = $this->repository->getEm()->getConnection(); + + $statement = $conn->prepare( + 'SELECT + post.id, + post.title, + MATCH(post.title) AGAINST(:search) AS MATCH_TITLE, + MATCH(post.content) AGAINST(:search) AS MATCH_CONTENT + FROM post + WHERE + post.status = 1 AND + post.published_at < :date + ORDER BY + MATCH_TITLE DESC, + MATCH_CONTENT DESC + '); + + $statement->execute([ + ':search' => $keywords, + ':date' => (new \DateTime())->format('Y-m-d H:i:s'), + ]); + + $results = $statement->fetchAll(); + $ids = []; + + foreach ($results as $k => $v) { + $rate = ($v['MATCH_TITLE'] * 2) + $v['MATCH_CONTENT']; + + if ($rate >= 7) { + $ids[] = $v['id']; + } + } + + if (0 == count($ids)) { + foreach ($results as $k => $v) { + $rate = ($v['MATCH_TITLE'] * 2) + $v['MATCH_CONTENT']; + + if ($rate >= 6) { + $ids[] = $v['id']; + } + } + } + + if (!$ids) { + $ids = [-1]; + } + + return $this + ->orderBy('FIELD(p.id, :ids)') + ->andWhere('.id IN(:ids)') + ->setParameter(':ids', $ids) + ; + } } diff --git a/symfony.lock b/symfony.lock index 46bac53..0b1be06 100644 --- a/symfony.lock +++ b/symfony.lock @@ -5,6 +5,9 @@ "beberlei/assert": { "version": "v3.3.0" }, + "beberlei/doctrineextensions": { + "version": "v1.3.0" + }, "behat/transliterator": { "version": "v1.3.0" },