?php namespace Drupal\renault_services\Service; use Drupal\book\BookManagerInterface; use Drupal\Core\Database\Connection; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Messenger\MessengerInterface; use Drupal\Core\Routing\RouteMatchInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\Url; use Drupal\lumi_tools\Service\BlockHelper; use Drupal\node\Entity\Node; use Drupal\paragraphs\Entity\Paragraph; class ChapterService { use StringTranslationTrait; /** * Constructs a ChapterService object. * * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager * The entity type manager. * @param \Drupal\renault_services\Service\ModelService $modelService * The model service. * @param \Drupal\renault_services\Service\NoticeUI $noticeUi * The notice UI service. * @param \Drupal\Core\Messenger\MessengerInterface $messenger * The messenger service. * @param \Drupal\book\BookManagerInterface $bookManager * The book manager service. * @param \Drupal\renault_services\Service\VideoService $videoService * The video service. * @param \Drupal\Core\Language\LanguageManagerInterface $languageManager * The language manager. * @param \Drupal\lumi_tools\Service\BlockHelper $blockHelper * The block helper service. * @param \Drupal\Core\Routing\RouteMatchInterface $routeMatch * The current route match. */ public function __construct( protected EntityTypeManagerInterface $entityTypeManager, protected ModelService $modelService, protected NoticeUI $noticeUi, protected MessengerInterface $messenger, protected BookManagerInterface $bookManager, protected VideoService $videoService, protected LanguageManagerInterface $languageManager, protected BlockHelper $blockHelper, protected RouteMatchInterface $routeMatch, protected Connection $connection) { } /** * Get chapters by technical code. * * @param array $codes * The technical codes to search for. * @param int|null $media_id * The media ID to filter by, if applicable. * * @return array * An array of chapter node IDs. */ public function getChaptersIdsByTechnicalCodes(array $codes, ?int $media_id = NULL, ?int $notice_id = NULL): array { if (empty($codes)) { return []; } $query = $this->connection->select('node_field_data', 'n') ->fields('n', ['nid']) ->addTag('node_access'); $query->join('node__field_technical_code', 'ftc', 'n.nid = ftc.entity_id'); $query->condition('n.type', 'chapter'); $query->condition('ftc.field_technical_code_value', $codes, 'IN'); if ($media_id) { $query->join('book', 'b', 'n.nid = b.nid'); $query->join('node__field_interactive_images', 'nii', 'b.bid = nii.entity_id'); $query->condition('nii.field_interactive_images_target_id', $media_id); } if ($notice_id) { $query->join('book', 'b2', 'n.nid = b2.nid'); $query->condition('b2.bid', $notice_id); } $result = $query->execute()->fetchCol(); // If there is no chapter found in the notice, check in the multimedia systems. // for now, it only check in all chapters with no filter. if (empty($result)) { // Get all chapters with theses codes. $query = $this->connection->select('node_field_data', 'n') ->fields('n', ['nid']) ->addTag('node_access'); $query->join('node__field_technical_code', 'ftc', 'n.nid = ftc.entity_id'); $query->condition('n.type', 'chapter'); $query->condition('ftc.field_technical_code_value', $codes, 'IN'); $query->groupBy('n.nid'); $result = $query->execute()->fetchCol(); } return $result; } /** * Get the notice ID from a media entity. * * @param \Drupal\media\Entity\Media $media * The media entity. * * @return int|null * The notice ID or null if not found. */ public function getNoticeIdFromMedia($media) { // Get the video technical code. $technicalCode = $media->field_technical_code?->value; if (empty($technicalCode)) { return NULL; } $chapterIds = $this->getChaptersIdsByTechnicalCodes([$technicalCode]); $chapterId = reset($chapterIds); // Load the chapter entity. $chapter = $this->entityTypeManager->getStorage('node')->load($chapterId); if (!$chapter) { return NULL; } return $this->getNoticeId($chapter); } /** * Extract technical codes from a paragraph as a simple array. * * @param \Drupal\paragraphs\Entity\Paragraph $paragraph * The paragraph entity. * * @return array * An array of technical codes. */ public function extractTechnicalCodesFromParagraph(Paragraph $paragraph): array { $technical_code_list = $paragraph->get('field_technical_code')->getValue(); $codes = []; foreach ($technical_code_list as $technical_code) { $codes[] = $technical_code['value']; } return $codes; } /** * Get the pin data from pin paragraph. * * @param \Drupal\paragraphs\Entity\Paragraph $pin * The pin paragraph entity. * * @return array|null * The pin data or null if not found. */ public function getPinData(Paragraph $pin): ?array { $codes = $this->extractTechnicalCodesFromParagraph($pin); $media_id = $pin->getParentEntity()->id(); if (empty($codes)) { /** @var \Drupal\media\Entity\Media $interactive_image */ $interactive_image = $this->entityTypeManager->getStorage('media')->load($media_id); $media_url = $interactive_image->toUrl('edit-form')->toString(); $this->messenger->addWarning($this->t('Technical code is missing for @media.', [ '@media' => $interactive_image->label(), '@url' => $media_url, ])); return NULL; } $pinData = [ 'multiple' => FALSE, ]; // Handle multiple technical codes. if (count($codes) > 1) { // Get notice id in route parameters. $noticeId = $this->routeMatch->getParameter('node')?->id() ?? $this->routeMatch->getParameter('node_id'); $chapterListUrl = Url::fromRoute('renault_eguide.pin_chapter_list', [ 'notice' => $noticeId, 'paragraph_id' => $pin->id(), ]); $pinData['title'] = $this->t('Multiple associated notices'); $pinData['url'] = $chapterListUrl->toString(); $pinData['multiple'] = TRUE; } // Only 1 technical code. if (count($codes) === 1) { $technical_code = [reset($codes)]; $nids = $this->getChaptersIdsByTechnicalCodes($technical_code, $media_id); if (!$nids) { return NULL; } /** @var \Drupal\node\Entity\Node $node */ $langId = $this->languageManager->getCurrentLanguage()->getId(); $node = Node::load(reset($nids)); $node = $node->hasTranslation($langId) ? $node->getTranslation($langId) : $node; $pinData['title'] = $node->label(); $pinData['url'] = $node->toUrl()->toString(); } return $pinData; } /** * Get the notice for a chapter. * * @param \Drupal\node\Entity\Node $chapter * The chapter node. * * @return \Drupal\node\Entity\Node|null * The notice node or null if not found. */ public function getNotice(Node $chapter): ?Node { $noticeId = $this->getNoticeId($chapter); return Node::load($noticeId) ?? NULL; } /** * Get the notice ID for a chapter. * * @param \Drupal\node\Entity\Node $chapter * The chapter node. * * @return int|null * The notice ID or null if not found. */ public function getNoticeId(Node $chapter): ?int { $chapterLink = $this->bookManager->loadBookLinks([$chapter->id()]); if (empty($chapterLink)) { return NULL; } return reset($chapterLink)['bid'] ?? NULL; } /** * Get a chapter by technical code. * * @param string $technical_code * The technical code to search for. * * @return \Drupal\node\Entity\Node|nullcurrentName * The chapter node or NULL if not found. */ public function getChapterByTechnicalCode(string $technical_code, ?int $notice_id): ?Node { $chapterIds = $this->getChaptersIdsByTechnicalCodes([$technical_code], NULL, $notice_id); if (empty($chapterIds)) { return NULL; } return $this->entityTypeManager->getStorage('node')->load(reset($chapterIds)); } /** * Determines if a book has at least one chapter updated. * * @param int $bookId * The ID of the book to check. * * @return bool * TRUE if at least one chapter is updated, FALSE otherwise. */ public function hasUpdatedChapter(int $bookId): bool { $chapterIds = $this->getBookTableOfContentIds($bookId); foreach ($chapterIds as $chapterId) { $chapter = $this->entityTypeManager->getStorage('node')->load($chapterId); if ($chapter && $chapter->field_modification->value) { return TRUE; } } return FALSE; } /** * Get the IDs of the chapters in a book's table of contents. * * @param int $bookId * The ID of the book. * * @return array * An array of chapter IDs. */ public function getBookTableOfContentIds(int $bookId): array { $tableOfContent = $this->bookManager->getTableOfContents($bookId, 10); $chapterIds = array_keys($tableOfContent); // Remove the book ID from the chapter IDs. $chapterIds = array_filter($chapterIds, function ($id) use ($bookId) { return $id !== (int) $bookId; }); return $chapterIds; } /** * Get updated chapters for a book. * * @param int $bookId * The ID of the book to check. * * @return array * An array of updated chapter nodes. */ public function getUpdatedChapters(int $bookId): array { $updatedChapters = []; $chapterIds = $this->getBookTableOfContentIds($bookId); $chapterCollection = $this->entityTypeManager->getStorage('node')->loadMultiple($chapterIds); foreach ($chapterCollection as $chapter) { if ($chapter && $chapter->field_modification->value) { $updatedChapters[] = $chapter; } } return $updatedChapters; } } The website encountered an unexpected error. Try again later.