<?php
namespace App\V4Bundle\Controller;
use App\Entity\Billing;
use App\Entity\Command;
use App\Entity\Food;
use App\Entity\ServiceCategories;
use App\V4Bundle\Entity\Billing2;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Security\Csrf\TokenStorage\TokenStorageInterface;
use GuzzleHttp\Client;
class FoodController extends BaseController
{
/**
* Groupes de recherche flexibles
*/
private const SEARCH_GROUPS = [
'spagho' => [
'include' => [
'spaghett',
'spaghetti',
'pâtes',
'pates',
'pasta',],
'exclude' => [],
'name_only' => true,
],
'poissonbraise' => [
'include' => ['poisson', 'brais'],
'exclude' => ['soupe', 'poulet', 'pintade', 'porc'],
'mode' => 'all',
],
'foufou' => [
'include' => ['foufou', 'fufu'],
'exclude' => [],
],
'akoumé' => [
'include' => ['akoum'],
'exclude' => [],
],
'émakoume' => [
'include' => ['makoum', 'emakoum'],
'exclude' => [],
],
'degue' => [
'include' => ['degue', 'dèguè'],
'exclude' => [],
],
'botokoin' => [
'include' => ['botokoin'],
'exclude' => [],
],
'tchintchinga' => [
'include' => ['tchintchinga'],
'exclude' => [],
],
'brochettes' => [
'include' => ['brochette','brochettes'],
'exclude' => [],
],
'pizza' => [
'include' => ['pizza'],
'exclude' => [],
],
'burger' => [
'include' => ['burger'],
'exclude' => [],
],
'shawarma' => [
'include' => ['shaw', 'charw', 'chaw'],
'exclude' => [],
],
'poulet' => [
'include' => ['poulet','nugget','poule','chicken','wings'],
'exclude' => [],
],
'riz' => [
'include' => ['riz','ayimolou','watchi','wachi'],
'exclude' => [],
],
'jus' => [
'include' => ['jus'],
'exclude' => [],
'name_only' => true,
],
'smoothie' => [
'include' => ['smooth'],
'exclude' => [],
'name_only' => true,
],
'milkshakes' => [
'include' => ['milk', 'shake'],
'exclude' => [],
'name_only' => true,
],
'théaulait' => [
'include' => ['thé au lait', 'théaulait'],
'exclude' => [],
'name_only' => true,
],
'glaces' => [
'include' => [
'glace',
'ice cream',
'ice-cream',
'coupe',
'sorbet',
'sundae',
'cône',
'Eskimo'
],
'exclude' => [
'burger','pizza','shawarma','chawarma','crêpe','spaghett','pasta',
'poulet','chicken','riz','jus','smoothie','café','cafe','latte','thé','théaulait','milkshakes',
'milkshake','bouillie','attieke','garba','champagne','rice','poisson', 'soupe','beignet','epice','epices'
],
'name_only' => true,
],
'crêpes' => [
'include' => ['crep', 'crêp'],
'exclude' => [],
],
'bouillie' => [
'include' => ['bouillie'],
'exclude' => [],
'name_only' => true,
'match' => 'starts_with',
],
'attieke' => [
'include' => ['attieke','garba','atchiéké','Atsèké'],
'exclude' => [],
],
];
/**
* @Route("mobile/api/v4/foods/search", name="user_mobile_api_search_food", methods={"GET"})
*/
public function searchTopFoods(Request $request): JsonResponse
{
$filter = strtolower(trim((string) $request->query->get('filter')));
$category = strtolower(trim((string) $request->query->get('category')));
$em = $this->entityManager;
/**
* -----------------------------------------
* 1️⃣ BASE QUERY : FOOD → RESTAURANT → CATEGORY
* -----------------------------------------
*/
$qb = $em->getRepository(Food::class)->createQueryBuilder('f')
->innerJoin('f.restaurant', 'r')
->innerJoin(ServiceCategories::class, 's', 'WITH', 'r.category = s.id')
->andWhere('f.deleted = 0')
->andWhere('f.state = 1')
->andWhere('r.enabled = 1')
->andWhere('r.isDisabled = 0');
/**
* -----------------------------------------
* 2️⃣ FILTER BY RESTAURANT CATEGORY (CONTEXT)
* -----------------------------------------
*/
$service_category = null;
if ($category && $category != 'all') {
$service_category = $em
->getRepository(ServiceCategories::class)
->findOneBy(['key' => $category]);
if ($service_category) {
$qb
->andWhere('s.id = :serviceCategoryId')
->setParameter('serviceCategoryId', $service_category->getId());
}
}
/**
* -----------------------------------------
* 3️⃣ TEXT SEARCH (WITH GROUPS)
* -----------------------------------------
*/
if (!empty($filter)) {
$filter = strtolower($filter);
$terms = preg_split('/\s+/', $filter);
/**
* -----------------------------------------
* CASE A — FILTER GROUP EXISTS
* -----------------------------------------
*/
if (isset(self::SEARCH_GROUPS[$filter])) {
$group = self::SEARCH_GROUPS[$filter];
$mode = $group['mode'] ?? 'any'; // any | all
$nameOnly = $group['name_only'] ?? false;
// INCLUDE
if ($mode === 'any') {
$orX = $qb->expr()->orX();
foreach ($group['include'] as $i => $term) {
$match = $group['match'] ?? 'contains';
$pattern = ($match === 'starts_with')
? $term . '%'
: '%' . $term . '%';
if ($nameOnly) {
$orX->add(
$qb->expr()->like('LOWER(f.name)', ":inc$i")
);
} else {
$orX->add(
$qb->expr()->orX(
$qb->expr()->like('LOWER(f.name)', ":inc$i"),
$qb->expr()->like('LOWER(f.description)', ":inc$i")
)
);
}
$qb->setParameter("inc$i", $pattern);
}
$qb->andWhere($orX);
} else {
foreach ($group['include'] as $i => $term) {
if ($nameOnly) {
$qb
->andWhere(
$qb->expr()->like('LOWER(f.name)', ":inc$i")
);
} else {
$qb
->andWhere(
$qb->expr()->orX(
$qb->expr()->like('LOWER(f.name)', ":inc$i"),
$qb->expr()->like('LOWER(f.description)', ":inc$i")
)
);
}
$qb->setParameter("inc$i", "%$term%");
}
}
foreach ($group['exclude'] as $i => $term) {
$qb->andWhere(
$qb->expr()->andX(
'LOWER(f.name) NOT LIKE :exc'.$i,
'LOWER(f.description) NOT LIKE :exc'.$i
)
);
$qb->setParameter('exc'.$i, "%$term%");
}
} else {
$terms = array_values(array_filter($terms, function ($t) {
return strlen($t) >= 2;
}));
$termCount = count($terms);
// single word → large
if ($termCount === 1) {
$term = $terms[0];
$qb
->andWhere(
$qb->expr()->orX(
$qb->expr()->like('LOWER(f.name)', ':term'),
$qb->expr()->like('LOWER(f.description)', ':term')
)
)
->setParameter('term', "%$term%");
}
// multiple words → AND
if ($termCount >= 2) {
foreach ($terms as $i => $term) {
$qb
->andWhere(
$qb->expr()->orX(
$qb->expr()->like('LOWER(f.name)', ":t$i"),
$qb->expr()->like('LOWER(f.description)', ":t$i")
)
)
->setParameter("t$i", "%$term%");
}
}
}
}
/**
* -----------------------------------------
* 4️⃣ FETCH FOODS
* -----------------------------------------
*/
$foods = $qb
->setMaxResults(1000)
->getQuery()
->getResult();
if (!$foods) {
return $this->json([
'success' => true,
'count' => 0,
'data' => [],
]);
}
/**
* -----------------------------------------
* 5️⃣ STATS (MOST ORDERED)
* -----------------------------------------
*/
$foodStats = [];
$foodsByRestaurant = [];
foreach ($foods as $food) {
$foodId = $food->getId();
$restaurantId = $food->getRestaurantId();
$foodStats[$foodId] = [
'food' => $food,
'count' => 0,
];
$foodsByRestaurant[$restaurantId][] = $foodId;
}
foreach ($foodsByRestaurant as $restaurantId => $foodIds) {
$commands = $em->createQuery(
'SELECT c.foodCommand
FROM App:Command c
WHERE c.restaurantId = :resto
AND c.state = 3'
)
->setParameter('resto', $restaurantId)
->getScalarResult();
if (!$commands) {
continue;
}
$foodIdLookup = array_flip($foodIds);
foreach ($commands as $row) {
$items = @unserialize($row['foodCommand']);
if ($items === false) {
$items = json_decode($row['foodCommand'], true);
}
if (!is_array($items)) {
continue;
}
foreach ($items as $item) {
if (!isset($item['food_id'])) {
continue;
}
$fid = (int) $item['food_id'];
if (!isset($foodIdLookup[$fid])) {
continue;
}
$qty = (int) ($item['quantity'] ?? 1);
$foodStats[$fid]['count'] += $qty;
}
}
}
uasort($foodStats, function ($a, $b) {
return $b['count'] <=> $a['count'];
});
/**
* -----------------------------------------
* 6️⃣ BILLING (AS-IS)
* -----------------------------------------
*/
$billing_array = [
'phoneNumber' => [],
'email' => [],
];
foreach ($em->getRepository(Billing::class)->findAll() as $item) {
$billing_array['phoneNumber'][] = $this->billingToArrayLite($item);
}
foreach ($em->getRepository(Billing2::class)->findAll() as $item) {
$billing_array['email'][] = $this->billingToArrayLite($item);
}
if ($billing_array['phoneNumber']) {
usort($billing_array['phoneNumber'], [$this, 'sort_billing_by_startRange']);
}
if ($billing_array['email']) {
usort($billing_array['email'], [$this, 'sort_billing_by_startRange']);
}
/**
* -----------------------------------------
* 7️⃣ RESPONSE
* -----------------------------------------
*/
$data = [];
foreach ($foodStats as $item) {
$data[] = $this->foodApiToArray($item['food']) + [
'ordered_count' => $item['count'],
];
}
return $this->json([
'service_category' => $service_category ? $service_category->getId() : null,
'data' => $data,
'billing' => $billing_array,
'error' => 0,
], 200);
}
}