WIP: Implement closer BPN integration
This commit is contained in:
@@ -16,7 +16,6 @@ class ApiClient
|
||||
{
|
||||
public const TYPE_NOTIFICATION = 'HINWEIS';
|
||||
public const TYPE_CUSTOMER_DATA = 'KUNDENKONTO';
|
||||
public const TYPE_PRODUCTS = 'PRODUKTE';
|
||||
public const TYPE_BASE_DATA_COUNTRIES = 'STAMMLAENDER';
|
||||
public const TYPE_BASE_DATA_PICKUPS = 'STAMMZUSTIEGE';
|
||||
public const TYPE_BASE_DATA_HOTELS = 'STAMMHOTELS';
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\BusProNet\DataProvider;
|
||||
|
||||
use App\BusProNet\ApiClient;
|
||||
use App\BusProNet\ApiClientException;
|
||||
use App\BusProNet\Model\Product;
|
||||
use Symfony\Contracts\Cache\CacheInterface;
|
||||
use Symfony\Contracts\Cache\ItemInterface;
|
||||
|
||||
class ProductDataProvider
|
||||
{
|
||||
public function __construct(private readonly ApiClient $apiClient, private readonly CacheInterface $cache)
|
||||
{}
|
||||
|
||||
public function getAll(): array
|
||||
{
|
||||
try {
|
||||
$products = $this->cache->get('bpn_products', function (ItemInterface $item) {
|
||||
$item->expiresAfter(3600);
|
||||
|
||||
return $this
|
||||
->apiClient
|
||||
->getBaseData(ApiClient::TYPE_PRODUCTS)
|
||||
->getItems()
|
||||
;
|
||||
});
|
||||
} catch (ApiClientException $e) {
|
||||
$products = [];
|
||||
}
|
||||
|
||||
return $products;
|
||||
}
|
||||
|
||||
public function get(int $busProId): ?Product
|
||||
{
|
||||
return $this->getAll()[$busProId] ?? null;
|
||||
}
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\BusProNet\Model;
|
||||
|
||||
class Product
|
||||
{
|
||||
private ?int $id = null;
|
||||
private ?string $code = null;
|
||||
private ?string $name = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function setId(?int $id): static
|
||||
{
|
||||
$this->id = $id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCode(): ?string
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
public function setCode(?string $code): static
|
||||
{
|
||||
$this->code = $code;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getName(): ?string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setName(?string $name): static
|
||||
{
|
||||
$this->name = $name;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -12,7 +12,6 @@ use App\BusProNet\Model\CrmAttributesResponse;
|
||||
use App\BusProNet\Model\Hotel;
|
||||
use App\BusProNet\Model\NotificationResponse;
|
||||
use App\BusProNet\Model\Pickup;
|
||||
use App\BusProNet\Model\Product;
|
||||
use App\BusProNet\Model\ProfileResponse;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
@@ -59,8 +58,6 @@ class ResponseParser
|
||||
return $this->createPickupsResponse($xml);
|
||||
case ApiClient::TYPE_BASE_DATA_HOTELS:
|
||||
return $this->createHotelsResponse($xml);
|
||||
case ApiClient::TYPE_PRODUCTS:
|
||||
return $this->createProductsResponse($xml);
|
||||
}
|
||||
|
||||
throw new ResponseParserException('Unable to parse XML response');
|
||||
@@ -241,28 +238,6 @@ class ResponseParser
|
||||
return new BaseDataResponse($hotels);
|
||||
}
|
||||
|
||||
public function createProductsResponse(\SimpleXMLElement $xml): BaseDataResponse
|
||||
{
|
||||
$products = [];
|
||||
|
||||
foreach ($xml->xpath('produkte/produkt') as $item) {
|
||||
$code = (string) $item->attributes()['code'];
|
||||
if (true === empty($code)) {
|
||||
continue;
|
||||
}
|
||||
$id = (int) $item->attributes()['id'];
|
||||
$product = new Product();
|
||||
$product
|
||||
->setId($id)
|
||||
->setName((string) $item->attributes()['bezeichnung'])
|
||||
->setCode($code)
|
||||
;
|
||||
$products[$id] = $product;
|
||||
}
|
||||
|
||||
return new BaseDataResponse($products);
|
||||
}
|
||||
|
||||
private function resolveOptions(array $options): array
|
||||
{
|
||||
$optionsResolver = new OptionsResolver();
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
namespace App\Command;
|
||||
|
||||
use App\BusProNet\DataProvider\HotelDataProvider;
|
||||
use App\BusProNet\DataProvider\PickupDataProvider;
|
||||
use Doctrine\DBAL\Connection;
|
||||
use Symfony\Component\Console\Attribute\AsCommand;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Style\SymfonyStyle;
|
||||
use Symfony\Component\Finder\Finder;
|
||||
|
||||
#[AsCommand(
|
||||
name: 'app:bpn-import',
|
||||
description: 'Imports dates, products and hotels from BusProNet',
|
||||
)]
|
||||
class BpnImportCommand extends Command
|
||||
{
|
||||
public function __construct(
|
||||
private readonly Connection $connection,
|
||||
private readonly HotelDataProvider $hotelDataProvider,
|
||||
private readonly PickupDataProvider $pickupDataProvider,
|
||||
private readonly string $xmlFilesPath
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output): int
|
||||
{
|
||||
$io = new SymfonyStyle($input, $output);
|
||||
|
||||
// Get all xml files
|
||||
$xmlFiles = (new Finder())
|
||||
->name('Ziel_*.xml')
|
||||
->in($this->xmlFilesPath)
|
||||
;
|
||||
|
||||
$totalCount = count($xmlFiles);
|
||||
|
||||
if (0 === $totalCount) {
|
||||
$io->error('No XML files available or found');
|
||||
|
||||
return Command::FAILURE;
|
||||
}
|
||||
|
||||
// Flush dates
|
||||
$this
|
||||
->connection
|
||||
->executeQuery('TRUNCATE TABLE bpn_date')
|
||||
;
|
||||
|
||||
$io->info('Found '.$totalCount.' XML files');
|
||||
$datesCount = 0;
|
||||
|
||||
// Iterate over xml files
|
||||
foreach ($xmlFiles as $xmlFile) {
|
||||
|
||||
// Load xml file into simplexml object
|
||||
$xml = simplexml_load_file($xmlFile);
|
||||
|
||||
// Iterate over all dates
|
||||
foreach ($xml->xpath('reise/termin') as $dateXml) {
|
||||
$product = (string) $dateXml->xpath('text')[0];
|
||||
$busProId = (int) $dateXml->attributes()['idbuspro'];
|
||||
$code = (string) $dateXml->attributes()['code'];
|
||||
$dateFrom = \DateTimeImmutable::createFromFormat('d.m.Y', (string) $dateXml->attributes()['termin']);
|
||||
$dateTo = \DateTimeImmutable::createFromFormat('d.m.Y', (string) $dateXml->attributes()['bis']);
|
||||
$bus = false;
|
||||
|
||||
// Iterate over all service entries and look for 'bus'
|
||||
foreach ($dateXml->xpath('lei_befoerderung/leistung') as $serviceXml) {
|
||||
if ('BUS' === (string) $serviceXml->attributes()['unterart']) {
|
||||
$bus = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Find pickups
|
||||
$datePickups = [];
|
||||
foreach ($dateXml->xpath('zustiege/zustieg') as $pickupXml) {
|
||||
$pickupBusProId = (int) $pickupXml->attributes()['idbuspro'];
|
||||
if ($pickupBusProId) {
|
||||
$pickup = $this->pickupDataProvider->get($pickupBusProId);
|
||||
$datePickups[] = [
|
||||
'busProId' => $pickupBusProId,
|
||||
'city' => $pickup->getCity(),
|
||||
'street' => $pickup->getStreet(),
|
||||
'time' => (string) $pickupXml->attributes()['zeit'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over all hotel entries
|
||||
foreach ($dateXml->xpath('hotel') as $hotelXml) {
|
||||
$hotelBusProId = (int) $hotelXml->attributes()['idbuspro'];
|
||||
$hotel = $this->hotelDataProvider->get($hotelBusProId);
|
||||
|
||||
// Finally store collected data
|
||||
$this
|
||||
->connection
|
||||
->insert('bpn_date', [
|
||||
'code' => $code,
|
||||
'bus_pro_id' => $busProId,
|
||||
'date_from' => $dateFrom->format('Y-m-d'),
|
||||
'date_to' => $dateTo->format('Y-m-d'),
|
||||
'product' => $product,
|
||||
'hotel' => $hotel->getName(),
|
||||
'hotel_code' => $hotel->getCode(),
|
||||
'hotel_bus_pro_id' => $hotelBusProId,
|
||||
'bus' => (int) $bus,
|
||||
'pickups' => json_encode($datePickups),
|
||||
])
|
||||
;
|
||||
++$datesCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$io->info('Imported '.$datesCount.' dates');
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller\Admin\Autocomplete;
|
||||
|
||||
use App\Repository\BpnDateRepository;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
|
||||
class BpnDateController extends AbstractController
|
||||
{
|
||||
public function __construct(private readonly BpnDateRepository $bpnDateRepository)
|
||||
{}
|
||||
|
||||
#[Route('/admin/autocomplete/bpndate', name: 'app_admin_autocomplete_bpndate')]
|
||||
#[IsGranted('ROLE_ADMINISTRATIVE')]
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$query = json_decode($request->getContent(), true, 512, JSON_THROW_ON_ERROR);
|
||||
$queryString = $query['search'];
|
||||
} catch (\JsonException $e) {
|
||||
throw $this->createNotFoundException();
|
||||
}
|
||||
|
||||
$dates = $this->bpnDateRepository->getAutocompletionData($queryString);
|
||||
|
||||
return $this->json($dates);
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller\Admin\Autocomplete;
|
||||
|
||||
use App\BusProNet\DataProvider\HotelDataProvider;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
|
||||
class HotelController extends AbstractController
|
||||
{
|
||||
public function __construct(private readonly HotelDataProvider $hotelDataProvider)
|
||||
{}
|
||||
|
||||
#[Route('/admin/autocomplete/hotel', name: 'app_admin_autocomplete_hotel')]
|
||||
#[IsGranted('ROLE_ADMINISTRATIVE')]
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$query = json_decode($request->getContent(), true, 512, JSON_THROW_ON_ERROR);
|
||||
$queryString = $query['search'];
|
||||
} catch (\JsonException $e) {
|
||||
throw $this->createNotFoundException();
|
||||
}
|
||||
|
||||
$hotels = $this->hotelDataProvider->getAll();
|
||||
$data = [];
|
||||
|
||||
foreach ($hotels as $hotel) {
|
||||
$name = $hotel->getName();
|
||||
if (1 === preg_match('/'.preg_quote($queryString, '/').'/i', $name)) {
|
||||
$data[] = [
|
||||
'value' => $hotel->getBusProId(),
|
||||
'text' => sprintf('%s (%s)', $name, $hotel->getCode()),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->json($data);
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller\Admin\Autocomplete;
|
||||
|
||||
use App\BusProNet\DataProvider\ProductDataProvider;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
|
||||
class ProductController extends AbstractController
|
||||
{
|
||||
public function __construct(private readonly ProductDataProvider $productDataProvider)
|
||||
{}
|
||||
|
||||
#[Route('/admin/autocomplete/product', name: 'app_admin_autocomplete_product')]
|
||||
#[IsGranted('ROLE_ADMINISTRATIVE')]
|
||||
public function index(Request $request): JsonResponse
|
||||
{
|
||||
try {
|
||||
$query = json_decode($request->getContent(), true, 512, JSON_THROW_ON_ERROR);
|
||||
$queryString = $query['search'];
|
||||
} catch (\JsonException $e) {
|
||||
throw $this->createNotFoundException();
|
||||
}
|
||||
|
||||
$products = $this->productDataProvider->getAll();
|
||||
$data = [];
|
||||
|
||||
foreach ($products as $product) {
|
||||
$name = $product->getName();
|
||||
if (1 === preg_match('/'.preg_quote($queryString, '/').'/i', $name)) {
|
||||
$data[] = [
|
||||
'value' => $product->getId(),
|
||||
'text' => sprintf('%s (%s)', $name, $product->getCode()),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $this->json($data);
|
||||
}
|
||||
}
|
||||
@@ -27,9 +27,9 @@ class Assignment implements BlameableEntityInterface, TimestampableEntityInterfa
|
||||
#[ORM\Column(type: 'string', length: 36, unique: true)]
|
||||
private string $uuid;
|
||||
|
||||
#[ORM\Column(nullable: true)]
|
||||
#[ORM\ManyToOne]
|
||||
#[Assert\NotNull(message: 'Bitte gib die Destination an')]
|
||||
private ?int $destinationBusProId = null;
|
||||
private ?BpnDate $destination = null;
|
||||
|
||||
#[ORM\ManyToOne]
|
||||
#[Assert\NotNull(message: 'Bitte gib das Jobprofil an')]
|
||||
@@ -93,14 +93,14 @@ class Assignment implements BlameableEntityInterface, TimestampableEntityInterfa
|
||||
return $this->uuid;
|
||||
}
|
||||
|
||||
public function getDestinationBusProId(): ?int
|
||||
public function getDestination(): ?BpnDate
|
||||
{
|
||||
return $this->destinationBusProId;
|
||||
return $this->destination;
|
||||
}
|
||||
|
||||
public function setDestinationBusProId(?int $destinationBusProId): static
|
||||
public function setDestination(?BpnDate $destination): static
|
||||
{
|
||||
$this->destinationBusProId = $destinationBusProId;
|
||||
$this->destination = $destination;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\BpnDateRepository;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: BpnDateRepository::class)]
|
||||
class BpnDate
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $busProId = null;
|
||||
|
||||
#[ORM\Column(length: 32)]
|
||||
private ?string $code = null;
|
||||
|
||||
#[ORM\Column(type: Types::DATE_IMMUTABLE)]
|
||||
private ?\DateTimeImmutable $dateFrom = null;
|
||||
|
||||
#[ORM\Column(type: Types::DATE_IMMUTABLE)]
|
||||
private ?\DateTimeImmutable $dateTo = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?bool $bus;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $product = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $hotel = null;
|
||||
|
||||
#[ORM\Column(length: 32)]
|
||||
private ?string $hotelCode = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private ?int $hotelBusProId = null;
|
||||
|
||||
#[ORM\Column]
|
||||
private array $pickups = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->bus = false;
|
||||
}
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getBusProId(): ?int
|
||||
{
|
||||
return $this->busProId;
|
||||
}
|
||||
|
||||
public function setBusProId(int $busProId): static
|
||||
{
|
||||
$this->busProId = $busProId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDateFrom(): ?\DateTimeImmutable
|
||||
{
|
||||
return $this->dateFrom;
|
||||
}
|
||||
|
||||
public function setDateFrom(\DateTimeImmutable $dateFrom): static
|
||||
{
|
||||
$this->dateFrom = $dateFrom;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDateTo(): ?\DateTimeImmutable
|
||||
{
|
||||
return $this->dateTo;
|
||||
}
|
||||
|
||||
public function setDateTo(\DateTimeImmutable $dateTo): static
|
||||
{
|
||||
$this->dateTo = $dateTo;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isBus(): ?bool
|
||||
{
|
||||
return $this->bus;
|
||||
}
|
||||
|
||||
public function setBus(bool $bus): static
|
||||
{
|
||||
$this->bus = $bus;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCode(): ?string
|
||||
{
|
||||
return $this->code;
|
||||
}
|
||||
|
||||
public function setCode(string $code): static
|
||||
{
|
||||
$this->code = $code;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getProduct(): ?string
|
||||
{
|
||||
return $this->product;
|
||||
}
|
||||
|
||||
public function setProduct(string $product): static
|
||||
{
|
||||
$this->product = $product;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getHotel(): ?string
|
||||
{
|
||||
return $this->hotel;
|
||||
}
|
||||
|
||||
public function setHotel(string $hotel): static
|
||||
{
|
||||
$this->hotel = $hotel;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getHotelCode(): ?string
|
||||
{
|
||||
return $this->hotelCode;
|
||||
}
|
||||
|
||||
public function setHotelCode(?string $hotelCode): static
|
||||
{
|
||||
$this->hotelCode = $hotelCode;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getHotelBusProId(): ?int
|
||||
{
|
||||
return $this->hotelBusProId;
|
||||
}
|
||||
|
||||
public function setHotelBusProId(?int $hotelBusProId): static
|
||||
{
|
||||
$this->hotelBusProId = $hotelBusProId;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPickups(): array
|
||||
{
|
||||
return $this->pickups;
|
||||
}
|
||||
|
||||
public function setPickups(array $pickups): static
|
||||
{
|
||||
$this->pickups = $pickups;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
namespace App\Form;
|
||||
|
||||
use App\Entity\Assignment;
|
||||
use App\Entity\BpnDate;
|
||||
use App\Entity\Fee;
|
||||
use App\Entity\JobProfile;
|
||||
use App\Entity\User;
|
||||
@@ -25,9 +26,11 @@ class AssignmentType extends AbstractType
|
||||
'label' => 'Anzahl',
|
||||
'required' => false,
|
||||
])
|
||||
->add('destinationBusProId', AutocompleteChoiceType::class, [
|
||||
->add('destination', AutocompleteEntityType::class, [
|
||||
'label' => 'Destination',
|
||||
'endpoint_route' => 'app_admin_autocomplete_hotel',
|
||||
'class' => BpnDate::class,
|
||||
'label_property' => 'product',
|
||||
'endpoint_route' => 'app_admin_autocomplete_bpndate',
|
||||
])
|
||||
->add('jobProfile', EntityType::class, [
|
||||
'label' => 'Jobprofil',
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\BpnDate;
|
||||
use App\Repository\Traits\QueryHelperTrait;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<BpnDate>
|
||||
*
|
||||
* @method BpnDate|null find($id, $lockMode = null, $lockVersion = null)
|
||||
* @method BpnDate|null findOneBy(array $criteria, array $orderBy = null)
|
||||
* @method BpnDate[] findAll()
|
||||
* @method BpnDate[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
|
||||
*/
|
||||
class BpnDateRepository extends ServiceEntityRepository
|
||||
{
|
||||
use QueryHelperTrait;
|
||||
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, BpnDate::class);
|
||||
}
|
||||
|
||||
public function getAutocompletionData(string $search): array
|
||||
{
|
||||
$qb = $this->createQueryBuilder('bpn_date');
|
||||
|
||||
$dates = $qb
|
||||
->select('bpn_date.id', 'bpn_date.product', 'bpn_date.hotel', 'bpn_date.dateFrom', 'bpn_date.dateTo')
|
||||
->where($qb->expr()->orX(
|
||||
$qb->expr()->like('bpn_date.product', ':product'),
|
||||
$qb->expr()->like('bpn_date.hotel', ':hotel')
|
||||
))
|
||||
->setParameter('product', '%'.$this->escapeLikeWildcards($search).'%')
|
||||
->setParameter('hotel', '%'.$this->escapeLikeWildcards($search).'%')
|
||||
->orderBy('bpn_date.product', 'ASC')
|
||||
->addOrderBy('bpn_date.dateFrom', 'ASC')
|
||||
->getQuery()
|
||||
->getArrayResult()
|
||||
;
|
||||
|
||||
$data = [
|
||||
[
|
||||
'value' => '',
|
||||
'text' => '...',
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($dates as $date) {
|
||||
$data[] = [
|
||||
'value' => $date['id'],
|
||||
'text' => sprintf('%s (%s - %s)', $date['product'], $date['dateFrom']->format('d.m.y'), $date['dateTo']->format('d.m.y')),
|
||||
];
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repository\Traits;
|
||||
|
||||
trait QueryHelperTrait
|
||||
{
|
||||
public function escapeLikeWildcards(string $value): string
|
||||
{
|
||||
return addcslashes($value, '_%');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user