<?php
namespace App\Controller;
use App\Annotation\CmsComponent;
use App\Entity\Page;
use App\Service\ServiceController;
use Doctrine\ORM\EntityManagerInterface;
use Knp\Component\Pager\PaginatorInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class SearchController extends AbstractController
{
/**
* @CmsComponent("Search Results", active=true, routeName="process_search")
*/
#[Route(path: '/cms-search', name: 'process_search')]
public function processSearch(Request $request, EntityManagerInterface $em, ServiceController $serviceController, PaginatorInterface $paginator)
{
$resultsPerPage = 20;
$titleFields = ['title', 'kicker', 'subtitle','headline', 'name'];
$descriptionFields = ['content', 'content2', 'description', 'information', 'taggedin', 'excerpt'];
$searchableFields = array_merge($titleFields, $descriptionFields);
$criteria = $request->get('search-terms');
$string = $request->get('search-terms');
$totalResults = 0;
$pageHits = [];
$criteriaArray = [];
if ($criteria) {
$criteriaArray = explode(' ', (string) $criteria);
// print_r($criteriaArray);
foreach ($criteriaArray as $key => $criteriaCheck) {
if (strlen($criteriaCheck) < 3) {
unset($criteriaArray[$key]);
}
if ('' == $criteriaCheck) {
unset($criteriaArray[$key]);
}
}
$cmsComponentArray = $serviceController->fetchCmsComponents();
$pages = $em->getRepository(Page::class)->findBy(['deleted' => false, 'active' => true]);
// get only active pages with componets - to prevent fetching routes without any possible links
// 2 arrays - one for quick searching(activePageRoutes) and sluggedPages for full details - just ensure they have both have the same key
$activePageRoutes = [];
$sluggedPages = [];
foreach ($pages as $page) {
$pageComps = $page->getComponents();
foreach ($pageComps as $pageComp) {
if ('' != $pageComp['route']) {
$sluggedPages[] = $page;
$activePageRoutes[] = $pageComp['route'];
}
}
}
// echo "<pre>".print_r($activePageRoutes, true)."</pre>";
// get only componets which have the 'slugEntity' annotation
$activeRoutes = [];
$sluggedComponents = [];
foreach ($cmsComponentArray as $cmsComponent) {
if ('' != $cmsComponent['slugEntity']) {
$sluggedComponents[] = $cmsComponent;
$activeRoutes[] = $cmsComponent['route'];
}
}
// echo "<pre>".print_r($sluggedComponents, true)."</pre>";
$hits = [];
// loop through all criteria
foreach ($criteriaArray as $criteria) {
// search page content first - as its easier ;)
foreach ($pages as $page) {
// loop through all searchable fields
foreach ($searchableFields as $searchableField) {
$getter = 'get'.ucwords($searchableField);
// check that the field exists
// search content - convert to lowercase
if (method_exists($page, $getter) && strstr(strtolower((string) $page->{$getter}()), strtolower($criteria))) {
// we have a hit - added a field called hits - if a entity gets more than 1 hit
// increase the hits, i'll use this to calculate weighting
$key = 'page'.$page->getId();
if (!array_key_exists($key, $hits)) {
$hits[$key] = [
'hits' => 1,
'title' => $page->getTitle(),
'description' => $page->getContent() ?? $page->getKicker(),
'link' => $page->getSlug(),
'image' => $page->getFullImagePath(),
];
} else {
// same entity hit - so increase hit count
++$hits[$key]['hits'];
}
dump($key,$page->getTitle());
++$totalResults;
}
}
}
}
foreach ($criteriaArray as $criteria) {
// search other bundles
// find entites to check - make sure they have a page to link to
foreach ($sluggedComponents as $key => $sluggedComponent) {
if (in_array($sluggedComponent['route'], $activePageRoutes)) {
// its an active page so search it
$checkComponent = $sluggedComponents[$key];
// get page info - so i know where to link the result
$pageKey = array_search($sluggedComponent['route'], $activePageRoutes);
$pageEntity = $sluggedPages[$pageKey];
$queryEntity = str_replace('\\', '', $checkComponent['bundle'].':'.$checkComponent['slugEntity']);
$queryEntity = str_replace('/', '', $queryEntity);
$queryEntity = str_replace('App:', '\\App\Entity\\', $queryEntity);
$entities = $em->getRepository($queryEntity)->findBy(['deleted' => 0, 'active' => 1]);
foreach ($entities as $entity) {
// loop through all searchable fields
foreach ($searchableFields as $searchableField) {
$getter = 'get'.ucwords($searchableField);
// check that the field exists
// search content - convert to lowercase
if (method_exists($entity, $getter) && strstr(strtolower((string) $entity->{$getter}()), strtolower($criteria))) {
// we have a hit - added a field called hits - if a entity gets more than 1 hit
// increase the hits, i'll use this to calculate weighting
// used key as a way to uniquly reference to increase hits
$key = $checkComponent['slugEntity'].$entity->getId();
$title = '';
$description = '';
$link = '';
$image = '';
// get title field
foreach (array_reverse($titleFields) as $titleField) {
$getter = 'get'.ucwords($titleField);
if (method_exists($entity, $getter)) {
$title = $entity->{$getter}();
}
}
// get description field
foreach (array_reverse($descriptionFields) as $descriptionField) {
$getter = 'get'.ucwords($descriptionField);
if (method_exists($entity, $getter)) {
$description = $entity->{$getter}();
}
}
// use excerpt if exists
if (method_exists($entity, 'getExcerpt')) {
$description = $entity->getExcerpt();
}
// check if image exists
if (method_exists($entity, 'getFullImagePath')) {
$image = $entity->getFullImagePath();
}
// get link
$link = str_replace(
$checkComponent['slug'],
$entity->getSlug(),
$pageEntity->getSlug()
);
if (!array_key_exists($key, $hits)) {
$hits[$key] = [
'hits' => 1,
'title' => $title,
'description' => $description,
'link' => $link,
'image' => $image,
];
} else {
// same entity hit - so increase hit count
++$hits[$key]['hits'];
if ($title) {
$hits[$key]['title'] = $title;
}
if ($description) {
$hits[$key]['description'] = $description;
}
if ($link) {
$hits[$key]['link'] = $link;
}
if ($image) {
$hits[$key]['image'] = $image;
}
}
++$totalResults;
}
}
}
}
}
}
// echo "<pre>".print_r($hits, true)."</pre>";
$sortedHits = $this->subvalSort($hits, 'hits');
$pageHits = $paginator->paginate(
array_reverse($sortedHits),
$request->query->getInt('page', 1),
$resultsPerPage
);
}
dump($pageHits);
return $this->render('@theme/common/search-results.html.twig', [
'pageHits' => $pageHits,
'criteriaArray' => $criteriaArray,
'criteria' => $string,
'totalResults' => $totalResults,
]);
return new Response('The search criteria is invalid');
}
public function subvalSort($a, $subkey)
{
$c = [];
$b = [];
foreach ($a as $k => $v) {
$b[$k] = strtolower((string) $v[$subkey]);
}
if (isset($b) && is_array($b)) {
asort($b);
foreach (array_keys($b) as $key) {
$c[] = $a[$key];
}
return $c;
}
return [];
}
}