vendor/croisiland/common-bundle/src/Helper/Cruise/CruiseHelper.php line 227

Open in your IDE?
  1. <?php
  2. declare(strict_types=1);
  3. namespace Croisiland\CommonBundle\Helper\Cruise;
  4. use Crea\MediaBundle\Entity\Enum\MediaFamilyCodeEnum;
  5. use Crea\MediaBundle\Entity\MediaTagOwnerInterface;
  6. use Crea\MediaBundle\Helper\MediaTagOwnerHelper;
  7. use Croisiland\CommonBundle\Entity\Company\Atmosphere;
  8. use Croisiland\CommonBundle\Entity\Cruise\Cruise;
  9. use Croisiland\CommonBundle\Entity\Cruise\CruiseInterface;
  10. use Croisiland\CommonBundle\Entity\Itinerary\DestinationMain;
  11. use Croisiland\CommonBundle\Entity\Itinerary\Itinerary;
  12. use Croisiland\CommonBundle\Entity\Itinerary\Stopover;
  13. use Croisiland\CommonBundle\Entity\Offer\Offer;
  14. use Croisiland\CommonBundle\Entity\Itinerary\ItinerarySegment;
  15. use Croisiland\CommonBundle\Entity\Ship\Ship;
  16. use Croisiland\CommonBundle\Enum\SegmentTypeEnum;
  17. use Croisiland\CommonBundle\Helper\DomDocumentHelper;
  18. use Croisiland\CommonBundle\Helper\Itinerary\ItineraryHelper;
  19. use Croisiland\CommonBundle\Repository\Cruise\CruiseRepository;
  20. use Croisiland\CommonBundle\Repository\Itinerary\ItinerarySegmentRepository;
  21. use Croisiland\CommonBundle\RepositoryView\Offer\OfferCruiseViewRepository;
  22. use Doctrine\ORM\NonUniqueResultException;
  23. use Doctrine\ORM\NoResultException;
  24. use function PHPUnit\Framework\matches;
  25. class CruiseHelper
  26. {
  27. public function __construct(
  28. private readonly CruiseRepository $cruiseRepository,
  29. private readonly OfferCruiseViewRepository $offerCruiseRepository,
  30. private readonly ItinerarySegmentRepository $itinerarySegmentRepository,
  31. private readonly DomDocumentHelper $domDocumentHelper,
  32. private readonly ItineraryHelper $itineraryHelper,
  33. private readonly MediaTagOwnerHelper $mediaTagOwnerHelper,
  34. )
  35. {
  36. }
  37. public function getUpdatedCruiseName(Cruise $cruise): string
  38. {
  39. $newName = '';
  40. $companyName = null;
  41. if (null !== $cruise->getShip() && null !== $cruise->getShip()->getCompany()) {
  42. $companyName = $cruise->getShip()->getCompany()->getName();
  43. }
  44. $itineraryName = null;
  45. if (null !== $cruise->getItinerary()) {
  46. $itineraryName = $cruise->getItinerary()->getName();
  47. }
  48. if (null !== $companyName && null !== $itineraryName) {
  49. $newName = $companyName.' : '.$itineraryName;
  50. if (strlen($newName) > 255) {
  51. $newName = substr($newName, 0, 252).'...';
  52. }
  53. }
  54. return $newName;
  55. }
  56. /**
  57. * @param array<int, int> $cruises
  58. *
  59. * @return Cruise[]
  60. */
  61. public function getCruisesFromIds(array $cruises, ?callable $callback = null): array
  62. {
  63. $resultSet = [];
  64. foreach ($cruises as $cruiseId) {
  65. $cruise = $this->cruiseRepository->find($cruiseId);
  66. if ($cruise instanceof Cruise && (null === $callback || $callback($cruise))) {
  67. $resultSet[] = $cruise;
  68. }
  69. }
  70. return $resultSet;
  71. }
  72. /**
  73. * Récupère un propriétaire de balise média (avatar) pour une croisière.
  74. * L'avatar est sélectionné selon cet ordre de priorité :
  75. * 1. Une escale aléatoire (en excluant le premier et dernier segment si plus de 2 segments).
  76. * 2. Le navire de la croisière.
  77. * 3. La destination de l'itinéraire.
  78. *
  79. * @param CruiseInterface $cruise La croisière pour laquelle récupérer l'avatar.
  80. * @return MediaTagOwnerInterface|null Le propriétaire de balise média ou null si aucun candidat valide n'est trouvé.
  81. */
  82. public function getCruiseAvatarOwner(CruiseInterface $cruise): ?MediaTagOwnerInterface
  83. {
  84. // Liste des candidats potentiels pour l'avatar, dans l'ordre de priorité.
  85. $avatarCandidates = [
  86. $this->getRandomStopover($cruise),
  87. $cruise->getShip(),
  88. $cruise->getItinerary()->getDestination(),
  89. ];
  90. // Sélectionner le premier candidat ayant des médias associés.
  91. foreach ($avatarCandidates as $avatarCandidate) {
  92. /** @var MediaTagOwnerInterface $avatarCandidate */
  93. $criteria = $this->getCruiseAvatarCriteria($avatarCandidate);
  94. try {
  95. if ($avatarCandidate !== null && $this->mediaTagOwnerHelper->hasMedia($avatarCandidate, MediaFamilyCodeEnum::FAM_IMAGE, $criteria)) {
  96. return $avatarCandidate;
  97. }
  98. } catch (NoResultException|NonUniqueResultException $e) {
  99. continue;
  100. }
  101. }
  102. return null;
  103. }
  104. /**
  105. * @return array<string|array<string>>
  106. */
  107. private function getCruiseAvatarCriteria(?MediaTagOwnerInterface $mediaOwner): array
  108. {
  109. return match (true) {
  110. $mediaOwner instanceof Stopover,
  111. $mediaOwner instanceof Ship,
  112. $mediaOwner instanceof DestinationMain => ['main'],
  113. default => [],
  114. };
  115. }
  116. /**
  117. * Commun Bundle
  118. */
  119. protected function getRandomStopover(CruiseInterface $cruise): ?Stopover
  120. {
  121. $segments = $this->itinerarySegmentRepository->getStopoverSegmentsForItinerary($cruise->getItinerary());
  122. if (empty($segments)) {
  123. return null;
  124. }
  125. // Filtrer les segments pour ignorer le premier et le dernier si plus de 2 segments.
  126. if (count($segments) > 2) {
  127. $segments = array_slice($segments, 1, -1);
  128. }
  129. shuffle($segments);
  130. // Sélectionner un segment aléatoire.
  131. foreach ($segments as $segment) {
  132. if ($this->mediaTagOwnerHelper->hasMedia($segment->getStopover(), MediaFamilyCodeEnum::FAM_IMAGE, $this->getCruiseAvatarCriteria($segment->getStopover()))) {
  133. return $segment->getStopover();
  134. }
  135. }
  136. return null;
  137. }
  138. public function getCruiseDisplayName(Cruise $cruise): string
  139. {
  140. if ($cruise->getItinerary() instanceof Itinerary) {
  141. $itineraryName = $this->itineraryHelper->getName($cruise->getItinerary());
  142. if ($itineraryName !== null && $itineraryName !== '') {
  143. return $itineraryName;
  144. }
  145. }
  146. return $cruise->getShip()->getCompany()->getName() .':'. $cruise->getShip()->getName() . ' - ' . $cruise->getDepartureDate()->format('d/m/Y');
  147. }
  148. public function getCruiseSelectName(Cruise $cruise): string
  149. {
  150. return sprintf(
  151. ' [%s] %s • %s (%s)',
  152. $cruise->getId(),
  153. $cruise->getCode(),
  154. $this->getCruiseDisplayName($cruise),
  155. $cruise->getDepartureDate()->format('d/m/Y').' - '.$cruise->getArrivalDate()->format('d/m/Y'),
  156. );
  157. }
  158. /**
  159. * Retourne le nombre de croisières actives ou non pour une Offer donnée
  160. * concernant les croisières dont la date de départ est dans le futur ou égale à aujourd'hui
  161. */
  162. public function getNbCruisesByOffer(Offer $offer):int
  163. {
  164. $cruiseIds = $this->offerCruiseRepository->findAllCruiseIdsPerOffer($offer);
  165. return count($cruiseIds);
  166. }
  167. /**
  168. * Retourne le nombre de croisières actives pour une Offer donnée
  169. * concernant les croisières dont la date de départ est dans le futur ou égale à aujourd'hui
  170. */
  171. public function getNbActiveCruisesByOffer(Offer $offer): int
  172. {
  173. $cruises = $this->cruiseRepository->findUpComingCruiseByOffer($offer, true);
  174. return count($cruises);
  175. }
  176. public function determineOfferOrigin(Offer $offer):?string
  177. {
  178. $results = $this->offerCruiseRepository->findResultsPerOffer($offer);
  179. $counts = array_count_values(array_column($results, 'linkType'));
  180. $nbDirect = $counts['direct'] ?? 0;
  181. $nbViaPrice = $counts['via_price'] ?? 0;
  182. if ($nbDirect > 0 && $nbViaPrice > 0) {
  183. return 'mixed';
  184. } elseif ($nbDirect > 0) {
  185. return 'direct';
  186. } elseif ($nbViaPrice > 0) {
  187. return 'via_price';
  188. }
  189. return null;
  190. }
  191. public function getCruiseIncludeForAtmosphereArray(Cruise $cruise, ?Atmosphere $atmosphere = null): array
  192. {
  193. return $this->getCruiseItemsForAtmosphereArray($cruise, $atmosphere, 'included');
  194. }
  195. public function getCruiseExcludeForAtmosphereArray(Cruise $cruise, ?Atmosphere $atmosphere = null): array
  196. {
  197. return $this->getCruiseItemsForAtmosphereArray($cruise, $atmosphere, 'excluded');
  198. }
  199. private function getCruiseItemsForAtmosphereArray(Cruise $cruise, ?Atmosphere $atmosphere, string $propertyName): array
  200. {
  201. $method = 'get' . mb_convert_case($propertyName, MB_CASE_TITLE, 'UTF-8');
  202. $cruiseItems = $this->domDocumentHelper->parseHtmlListToArray($cruise->$method());
  203. if (!empty($cruiseItems)) {
  204. return $cruiseItems;
  205. }
  206. $itineraryItems = $this->domDocumentHelper->parseHtmlListToArray($cruise->getItinerary()->$method());
  207. if (!empty($itineraryItems)) {
  208. return $itineraryItems;
  209. }
  210. $companyItems = $this->domDocumentHelper->parseHtmlListToArray($cruise->getShip()->getCompany()->$method());
  211. if ($atmosphere === null) {
  212. return $companyItems;
  213. }
  214. $atmosphereItems = $this->domDocumentHelper->parseHtmlListToArray($atmosphere->$method());
  215. if (empty($atmosphereItems)) {
  216. return $companyItems;
  217. }
  218. return array_unique(array_merge($companyItems, $atmosphereItems));
  219. }
  220. }