src/Entity/Journal.php line 146

  1. <?php
  2. namespace App\Entity;
  3. use Doctrine\ORM\Mapping as ORM,
  4.     Doctrine\Common\Collections\Collection,
  5.     Doctrine\Common\Collections\ArrayCollection,
  6.     Doctrine\DBAL\Types\Types,
  7.     Doctrine\Common\Collections\Criteria;
  8. use Doctrine\ORM\Mapping\Index;
  9. use Gedmo\Mapping\Annotation as Gedmo;
  10. use Symfony\Component\Serializer\Annotation\Groups,
  11.     Symfony\Component\Validator\Constraints as Assert;
  12. use ApiPlatform\Metadata\ApiResource,
  13.     ApiPlatform\Metadata\ApiProperty,
  14.     ApiPlatform\Metadata\Get,
  15.     ApiPlatform\Metadata\GetCollection,
  16.     ApiPlatform\Metadata\Post,
  17.     ApiPlatform\Metadata\Put,
  18.     ApiPlatform\Metadata\Delete,
  19.     ApiPlatform\Metadata\ApiFilter,
  20.     ApiPlatform\Doctrine\Orm\Filter\SearchFilter,
  21.     ApiPlatform\Doctrine\Orm\Filter\RangeFilter,
  22.     ApiPlatform\Doctrine\Orm\Filter\DateFilter,
  23.     ApiPlatform\Doctrine\Orm\Filter\BooleanFilter,
  24.     ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
  25. use App\Entity\Trait\IdTrait,
  26.     App\Entity\Trait\UuidTrait,
  27.     App\Entity\Trait\OrdStatTrait,
  28.     App\Entity\Trait\TimestampableTrait,
  29.     App\Entity\Trait\TranslatableTrait,
  30.     App\Entity\Trait\Languages\JournalLanguagesTrait,
  31.     App\Entity\Trait\ArchivedTrait,
  32.     App\Entity\Trait\EditorialGroupableTrait,
  33.     App\Entity\Trait\ImportIdTrait,
  34.     App\Entity\Interface\TranslatableInterface,
  35.     App\Entity\Interface\OrdStatableInterface,
  36.     App\Entity\Interface\LanguageableInterface,
  37.     App\Entity\Interface\EditorialGroupableInterface,
  38.     App\Entity\Interface\ArchivableInterface,
  39.     App\Entity\Interface\OwnerableInterface,
  40.     App\Entity\Interface\CloneableInterface,
  41.     App\Repository\JournalRepository;
  42. use App\Enum\Language,
  43.     App\Enum\JournalCoverType,
  44.     App\Enum\JournalDefaultPage,
  45.     App\Entity\Admin,
  46.     App\Security\Voter\ArchivableVoter,
  47.     App\Lib\Actions,
  48.     App\DTO\CloneDTO,
  49.     App\StateProcessor\CloneProcessor,
  50.     App\Security\Voter\CloneVoter;
  51. use App\Attribute\DenormalizationInject,
  52.     App\Util\ClassUtils,
  53.     App\Filter\IriFilter;
  54. use App\Controller\Export\Journal\JournalCrossrefExporter,
  55.     App\Controller\Export\Journal\JournalSimcheckCrossrefExporter,
  56.     App\Controller\Export\Journal\JournalLicenceFulltextCrossrefExporter;
  57. use App\Controller\Export\Journal\JournalMainDataExporter;
  58. use App\Controller\Stats\StatsJournalXlsxGenerator;
  59. use App\Filter\DateUnmapped;
  60. use App\Filter\OrderByUnmapped;
  61. use App\Filter\RangeUnmapped;
  62. use App\StateProvider\StatsJournalProvider;
  63. use App\StateProvider\StatsSystemProvider;
  64. #[ApiResource(
  65.     description'Journals',
  66.     normalizationContext: ['groups' => [
  67.         'read',
  68.         'read:' self::class,
  69.         'read:' self::class . 'Translation'
  70.     ]],
  71.     denormalizationContext: ['groups' => ['write']],
  72.     security'is_granted("' self::class . '")',
  73.     order: ['ord' => 'desc'],
  74.     operations: [
  75.         new GetCollection(),
  76.         new GetCollection(
  77.             uriTemplate'/journals/list',
  78.             normalizationContext: ['groups' => ['read''read:list']],
  79.         ),
  80.         new GetCollection(
  81.             name'get_journal_stats_system',
  82.             uriTemplate'/journals/stats/system',
  83.             providerStatsSystemProvider::class,
  84.             security'is_granted("ROLE_MODULE_STATS")',
  85.             normalizationContext: ['groups' => ['read:stats']],
  86.         ),
  87.         new GetCollection(
  88.             name'get_journal_stats',
  89.             uriTemplate'/journals/stats',
  90.             providerStatsJournalProvider::class,
  91.             security'is_granted("ROLE_MODULE_STATS")',
  92.             normalizationContext: ['groups' => ['read:stats']],
  93.             paginationMaximumItemsPerPage500
  94.         ),
  95.         new GetCollection(
  96.             name'get_journal_stats_xlsx',
  97.             uriTemplate'/journals/stats/xlsx',
  98.             providerStatsJournalProvider::class,
  99.             controllerStatsJournalXlsxGenerator::class,
  100.             security'is_granted("ROLE_MODULE_STATS")',
  101.         ),
  102.         new Post(
  103.             uriTemplate'/journals/clone',
  104.             inputCloneDTO::class,
  105.             processorCloneProcessor::class,
  106.             security'is_granted("' Actions::CLONE . '")',
  107.             securityMessageCloneVoter::MESSAGE
  108.         ),
  109.         new Post(),
  110.         new Get(),
  111.         new Get(
  112.             name'get_journal_crossref',
  113.             uriTemplate'/journals/{uuid}/export/crossref',
  114.             controllerJournalCrossrefExporter::class
  115.         ),
  116.         new Get(
  117.             name'get_journal_simcheck_crossref',
  118.             uriTemplate'/journals/{uuid}/export/simcheck_crossref',
  119.             controllerJournalSimcheckCrossrefExporter::class
  120.         ),
  121.         new Get(
  122.             name'get_journal_licence_fulltext_crossref',
  123.             uriTemplate'/journals/{uuid}/export/licence_fulltext_crossref',
  124.             controllerJournalLicenceFulltextCrossrefExporter::class
  125.         ),
  126.         new Get(
  127.             name'get_journal_main_data',
  128.             uriTemplate'/journals/{uuid}/export/main_data',
  129.             controllerJournalMainDataExporter::class
  130.         ),
  131.         new Put(),
  132.         new Delete(
  133.             securityPostDenormalize'is_granted("' Actions::DELETE '", object)',
  134.             securityPostDenormalizeMessageArchivableVoter::MESSAGE
  135.         ),
  136.     ],
  137.     extraProperties: ['standard_put' => false],
  138. )]
  139. #[ApiFilter(SearchFilter::class, properties: [
  140.     'translations.title' => 'partial',
  141.     'nativeTitle' => 'partial',
  142.     'nativeLanguage' => 'partial',
  143.     'data.workingTitle' => 'partial',
  144.     'data.issn' => 'exact',
  145.     'data.eIssn' => 'exact',
  146.     'data.state' => 'exact',
  147.     'data.type' => 'exact',
  148.     'domains.domain.nativeTitle' => 'partial',
  149.     'affiliations.affiliation.nativeTitle' => 'partial',
  150.     'affiliations.affiliation.type.title' => 'partial'
  151. ])]
  152. #[ApiFilter(RangeFilter::class, properties: ['data.meinScore'])]
  153. #[ApiFilter(DateFilter::class, properties: ['updatedAt'])]
  154. #[ApiFilter(IriFilter::class, properties: [
  155.     'nativeLanguage',
  156.     'domains.domain',
  157.     'affiliations.affiliation',
  158.     'data.periodicity',
  159.     'data.type',
  160.     'data.licence',
  161.     'data.state'
  162. ])]
  163. #[ApiFilter(BooleanFilter::class, properties: ['isArchived''stat'])]
  164. #[ApiFilter(OrderFilter::class, properties: [
  165.     'nativeTitle',
  166.     'nativeLanguage',
  167.     'data.type',
  168.     'data.state',
  169.     'affiliations.affiliation.nativeTitle',
  170.     'affiliations.affiliation.type.title',
  171.     'domains.domain.nativeTitle',
  172.     'data.workingTitle',
  173.     'data.periodicity',
  174.     'updatedAt',
  175.     'stat',
  176.     'ord'
  177. ])]
  178. #[ApiFilter(OrderByUnmapped::class, properties: [
  179.     'statsEarliestArticlePublicationDate',
  180.     'statsLatestArticlePublicationDate',
  181.     'statsAmountOfArticles',
  182.     'statsAmountOfArticles',
  183.     'statsAmountOfIssues',
  184.     'statsAmountOfVolumes',
  185.     'statsAmountOfViews',
  186.     'statsAmountOfDownloads'
  187. ])]
  188. #[ApiFilter(RangeUnmapped::class, properties: [
  189.     'statsAmountOfVolumes',
  190.     'statsAmountOfIssues',
  191.     'statsAmountOfArticles',
  192.     'statsAmountOfViews',
  193.     'statsAmountOfDownloads'
  194. ])]
  195. #[ApiFilter(DateUnmapped::class, properties: ['statsEarliestArticlePublicationDate','statsLatestArticlePublicationDate'])]
  196. #[Index(fields: ["nativeLanguage"], name"native_language_idx")]
  197. #[Index(fields: ["stat"], name"stat_idx")]
  198. #[ORM\Entity(repositoryClassJournalRepository::class)]
  199. class Journal implements
  200.     TranslatableInterface,
  201.     OrdStatableInterface,
  202.     LanguageableInterface,
  203.     EditorialGroupableInterface,
  204.     ArchivableInterface,
  205.     OwnerableInterface,
  206.     CloneableInterface
  207. {
  208.     use IdTrait,
  209.         UuidTrait,
  210.         OrdStatTrait,
  211.         TimestampableTrait,
  212.         TranslatableTrait,
  213.         JournalLanguagesTrait,
  214.         ArchivedTrait,
  215.         EditorialGroupableTrait,
  216.         ImportIdTrait;
  217.     #[ApiProperty(description'Native title sort key')]
  218.     #[Groups(['read:' self::class, 'read:stats'])]
  219.     #[ORM\Column(typeTypes::STRINGlength255nullabletrue)]
  220.     private ?string $nativeTitleSortKey null;
  221.     #[ApiProperty(description'Data subresource'writabletrue)]
  222.     #[Groups(['read:' self::class, 'read:list''write'])]
  223.     #[Assert\Valid]
  224.     #[DenormalizationInject(method: ['name' => 'getData'])]
  225.     #[ORM\OneToOne(targetEntityJournalData::class, mappedBy'parent'cascade: ['persist''remove'])]
  226.     private JournalData $data;
  227.     #[ApiProperty(description'View subresource'writabletrue)]
  228.     #[Groups(['read:' self::class, 'write'])]
  229.     #[Assert\Valid]
  230.     #[DenormalizationInject(method: ['name' => 'getView'])]
  231.     #[ORM\OneToOne(targetEntityJournalView::class, mappedBy'parent'cascade: ['persist''remove'])]
  232.     private JournalView $view;
  233.     #[ApiProperty(description'Newest new issue'writabletrue)]
  234.     #[ORM\OneToOne(targetEntityJournalIssue::class)]
  235.     #[ORM\JoinColumn(onDelete'set null'nullabletrue)]
  236.     private ?JournalIssue $newestNewIssue null;
  237.     #[ApiProperty(description'Slug')]
  238.     #[Groups(['read:' self::class, 'write'])]
  239.     #[Gedmo\Slug(separator'-'style'default'updatabletruefields: ['nativeTitle'])]
  240.     #[ORM\Column(typeTypes::STRINGlength255nullabletrue)]
  241.     private ?string $slug null;
  242.     #[ApiProperty(description'Slug')]
  243.     #[Groups(['read:' self::class, 'write'])]
  244.     #[ORM\Column(typeTypes::STRINGlength255nullabletrue)]
  245.     private ?string $importSlug null;
  246.     #[ApiProperty(description'Slug')]
  247.     #[Groups(['read:' self::class, 'write'])]
  248.     #[ORM\Column(typeTypes::STRINGlength255nullabletrue)]
  249.     private ?string $importSlugEn null;
  250.     #[ApiProperty(description'Created by')]
  251.     #[Groups(['read:' self::class])]
  252.     #[ORM\ManyToOne(targetEntityAdmin::class, inversedBy'journals')]
  253.     #[ORM\JoinColumn(onDelete'set null')]
  254.     private Admin $createdBy;
  255.     #[ApiProperty(description'Updated by')]
  256.     #[Groups(['read:' self::class])]
  257.     #[ORM\ManyToOne(targetEntityAdmin::class)]
  258.     #[ORM\JoinColumn(onDelete'set null')]
  259.     private Admin $updatedBy;
  260.     #[ApiProperty(description'Scientific council members'writableLinkfalse)]
  261.     #[ORM\OneToMany(targetEntityJournalScientificCouncilMember::class, mappedBy'parent'cascade: ['persist'])]
  262.     #[ORM\OrderBy(['ord' => 'desc'])]
  263.     private Collection $scientificCouncilMembers;
  264.     #[ApiProperty(description'Domains'fetchEagerfalse)]
  265.     #[Groups(['read''read:list'])]
  266.     #[ORM\OneToMany(targetEntityJournalDomain::class, mappedBy'parent'cascade: ['persist''remove'])]
  267.     #[ORM\OrderBy(['ord' => 'desc'])]
  268.     private Collection $domains;
  269.     #[ApiProperty(description'Indexation collections')]
  270.     #[ORM\OneToMany(targetEntityJournalIndexationCollection::class, mappedBy'parent'cascade: ['persist''remove'])]
  271.     #[ORM\OrderBy(['ord' => 'desc'])]
  272.     private Collection $indexationCollections;
  273.     #[ApiProperty(description'Indicators')]
  274.     #[ORM\OneToMany(targetEntityJournalIndicator::class, mappedBy'parent'cascade: ['persist''remove'])]
  275.     #[ORM\OrderBy(['ord' => 'desc'])]
  276.     private Collection $indicators;
  277.     #[ApiProperty(description'Affiliations'fetchEagerfalse)]
  278.     #[Groups(['read''read:list'])]
  279.     #[ORM\OneToMany(targetEntityJournalAffiliation::class, mappedBy'parent'cascade: ['persist''remove'])]
  280.     #[ORM\OrderBy(['ord' => 'desc'])]
  281.     private Collection $affiliations;
  282.     #[ApiProperty(description'Publishers')]
  283.     #[ORM\OneToMany(targetEntityJournalPublisher::class, mappedBy'parent'cascade: ['persist''remove'])]
  284.     #[ORM\OrderBy(['ord' => 'desc'])]
  285.     private Collection $publishers;
  286.     #[ApiProperty(description'Partners')]
  287.     #[ORM\OneToMany(targetEntityJournalPartner::class, mappedBy'parent'cascade: ['persist''remove'])]
  288.     #[ORM\OrderBy(['ord' => 'desc'])]
  289.     private Collection $partners;
  290.     #[ApiProperty(description'Slides')]
  291.     #[ORM\OneToMany(targetEntityJournalSlide::class, mappedBy'parent'cascade: ['persist''remove'])]
  292.     #[ORM\OrderBy(['ord' => 'desc'])]
  293.     private Collection $slides;
  294.     #[ApiProperty(description'Sections')]
  295.     #[ORM\OneToMany(targetEntityJournalSection::class, mappedBy'parent'cascade: ['persist''remove'])]
  296.     #[ORM\OrderBy(['ord' => 'desc'])]
  297.     private Collection $sections;
  298.     #[ApiProperty(description'Menu items')]
  299.     #[ORM\OneToMany(targetEntityJournalMenuItem::class, mappedBy'parent'cascade: ['persist''remove'])]
  300.     #[ORM\OrderBy(['ord' => 'desc'])]
  301.     private Collection $menuItems;
  302.     #[ApiProperty(description'Submenu groups')]
  303.     #[ORM\OneToMany(targetEntityJournalSubmenuGroup::class, mappedBy'parent'cascade: ['persist''remove'])]
  304.     #[ORM\OrderBy(['ord' => 'desc'])]
  305.     private Collection $submenuGroups;
  306.     #[ApiProperty(description'Sidemenu groups')]
  307.     #[ORM\OneToMany(targetEntityJournalSidemenuGroup::class, mappedBy'parent'cascade: ['persist''remove'])]
  308.     #[ORM\OrderBy(['ord' => 'desc'])]
  309.     private Collection $sidemenuGroups;
  310.     #[ApiProperty(description'Banners')]
  311.     #[ORM\OneToMany(targetEntityJournalBanner::class, mappedBy'parent'cascade: ['persist''remove'])]
  312.     #[ORM\OrderBy(['ord' => 'desc'])]
  313.     private Collection $banners;
  314.     #[ApiProperty(description'Volumes')]
  315.     #[ORM\OneToMany(targetEntityJournalVolume::class, mappedBy'parent'cascade: ['persist''remove'])]
  316.     #[ORM\OrderBy(['ord' => 'desc'])]
  317.     private Collection $volumes;
  318.     #[ApiProperty(description'Issues')]
  319.     #[ORM\OneToMany(targetEntityJournalIssue::class, mappedBy'parent'cascade: ['persist''remove'])]
  320.     #[ORM\OrderBy(['ord' => 'desc'])]
  321.     private Collection $issues;
  322.     #[ApiProperty(description'Articles')]
  323.     #[ORM\OneToMany(targetEntityJournalArticle::class, mappedBy'parent'cascade: ['persist''remove'])]
  324.     #[ORM\OrderBy(['ord' => 'desc'])]
  325.     private Collection $articles;
  326.     #[ApiProperty(description'Pages')]
  327.     #[ORM\OneToMany(targetEntityJournalPage::class, mappedBy'parent'cascade: ['persist''remove'])]
  328.     #[ORM\OrderBy(['ord' => 'desc'])]
  329.     private Collection $pages;
  330.     #[ApiProperty(description'News')]
  331.     #[ORM\OneToMany(targetEntityJournalNews::class, mappedBy'parent'cascade: ['persist''remove'])]
  332.     #[ORM\OrderBy(['ord' => 'desc'])]
  333.     private Collection $news;
  334.     #[ApiProperty(description'Collections')]
  335.     #[ORM\OneToMany(targetEntityJournalCollection::class, mappedBy'parent'cascade: ['persist''remove'])]
  336.     #[ORM\OrderBy(['ord' => 'desc'])]
  337.     private Collection $collections;
  338.     #[ApiProperty(description'Sort article by ord')]
  339.     #[Groups(['read:' self::class, 'write'])]
  340.     #[ORM\Column(typeTypes::BOOLEANoptions: ['default' => false])]
  341.     private bool $sortArticlesByOrd false;
  342.     #[ORM\OneToMany(mappedBy'parent'targetEntityJournalFooter::class, orphanRemovaltrue)]
  343.     private Collection $smallFooters;
  344.     #[ApiProperty(description'Statistics: earliest article publication date')]
  345.     #[Groups(['read:stats'])]
  346.     private ?\DateTimeImmutable $statsEarliestArticlePublicationDate null;
  347.     #[ApiProperty(description'Statistics: latest article publication date')]
  348.     #[Groups(['read:stats'])]
  349.     private ?\DateTimeImmutable $statsLatestArticlePublicationDate null;
  350.     #[ApiProperty(description'Statistics: amount of views')]
  351.     #[Groups(['read:stats'])]
  352.     private int $statsAmountOfArticles 0;
  353.     #[ApiProperty(description'Statistics: amount of views')]
  354.     #[Groups(['read:stats'])]
  355.     private int $statsAmountOfIssues 0;
  356.     #[ApiProperty(description'Statistics: amount of views')]
  357.     #[Groups(['read:stats'])]
  358.     private int $statsAmountOfVolumes 0;
  359.     #[ApiProperty(description'Statistics: amount of views')]
  360.     #[Groups(['read:stats'])]
  361.     private int $statsAmountOfViews 0;
  362.     #[ApiProperty(description'Statistics: amount of downloads')]
  363.     #[Groups(['read:stats'])]
  364.     private int $statsAmountOfDownloads 0;
  365.     public function __construct(Admin $createdBy, ?array $languages)
  366.     {
  367.         $this->setUuid();
  368.         $this->data = new JournalData($this);
  369.         $this->view = new JournalView($this);
  370.         $this->createdBy $createdBy;
  371.         $this->updatedBy $createdBy;
  372.         $this->translations = new ArrayCollection();
  373.         $this->domains = new ArrayCollection();
  374.         $this->indexationCollections = new ArrayCollection();
  375.         $this->indicators = new ArrayCollection();
  376.         $this->affiliations = new ArrayCollection();
  377.         $this->publishers = new ArrayCollection();
  378.         $this->partners = new ArrayCollection();
  379.         $this->slides = new ArrayCollection();
  380.         $this->editorialGroups = new ArrayCollection();
  381.         $this->scientificCouncilMembers = new ArrayCollection();
  382.         $this->sections = new ArrayCollection();
  383.         $this->menuItems = new ArrayCollection();
  384.         $this->submenuGroups = new ArrayCollection();
  385.         $this->sidemenuGroups = new ArrayCollection();
  386.         $this->banners = new ArrayCollection();
  387.         $this->volumes = new ArrayCollection();
  388.         $this->issues = new ArrayCollection();
  389.         $this->articles = new ArrayCollection();
  390.         $this->pages = new ArrayCollection();
  391.         $this->news = new ArrayCollection();
  392.         $this->collections = new ArrayCollection();
  393.         $this->setLanguages($languages ?? Language::DEFAULT_JOURNAL);
  394.         foreach (array_reverse(JournalDefaultPage::cases()) as $index => $idName) {
  395.             new JournalPage(
  396.                 parent$this,
  397.                 workingTitle$idName->value,
  398.                 idName$idName->value,
  399.                 ord$index 1,
  400.                 stattrue
  401.             );
  402.         }
  403.         $this->createdAt = new \DateTimeImmutable();
  404.         $this->updatedAt = new \DateTimeImmutable();
  405.         $this->smallFooters = new ArrayCollection();
  406.     }
  407.     public function getTitle(): string
  408.     {
  409.         return $this->data->getWorkingTitle();
  410.     }
  411.     public function setNativeTitleSortKey(string $nativeTitleSortKey): self
  412.     {
  413.         $this->nativeTitleSortKey $nativeTitleSortKey;
  414.         return $this;
  415.     }
  416.     public function getNativeTitleSortKey(): ?string
  417.     {
  418.         return $this->nativeTitleSortKey;
  419.     }
  420.     public function getData(): JournalData
  421.     {
  422.         return $this->data;
  423.     }
  424.     public function getView(): JournalView
  425.     {
  426.         return $this->view;
  427.     }
  428.     public function getCover(Language $lang): ?Media
  429.     {
  430.         if ($this->view->getCoverType() === JournalCoverType::CHOSEN) {
  431.             return $this->readAvailableTranslation($lang'cover');
  432.         }
  433.         if ($cover $this->getNewestIssue()?->readAvailableTranslation($lang'cover')) {
  434.             return $cover;
  435.         }
  436.         return $this->view->getCoverType() === JournalCoverType::CURRENT_ISSUE_OR_CHOSEN
  437.             $this->readAvailableTranslation($lang'cover')
  438.             : null;
  439.     }
  440.     public function getCoverAlt(Language $lang): ?string
  441.     {
  442.         if ($this->view->getCoverType() === JournalCoverType::CHOSEN) {
  443.             return $this->readAvailableTranslation($lang'coverAlt');
  444.         }
  445.         if ($this->getNewestIssue()?->readAvailableTranslation($lang'cover')) {
  446.             return $this->getNewestIssue()?->readAvailableTranslation($lang'coverAlt');
  447.         }
  448.         return $this->view->getCoverType() === JournalCoverType::CURRENT_ISSUE_OR_CHOSEN
  449.             $this->readAvailableTranslation($lang'coverAlt')
  450.             : null;
  451.     }
  452.     public function getNewestNewIssue(): ?JournalIssue
  453.     {
  454.         return $this->newestNewIssue;
  455.     }
  456.     public function setNewestNewIssue(?JournalIssue $newestNewIssue): self
  457.     {
  458.         $this->newestNewIssue $newestNewIssue;
  459.         return $this;
  460.     }
  461.     public function getNewestIssue(): ?JournalIssue
  462.     {
  463.         foreach ($this->getIssues() as $issue) {
  464.             if ($issue->getStat() && $issue->getIsNewestIssue()) {
  465.                 return $issue;
  466.             }
  467.         }
  468.         return null;
  469.     }
  470.     public function getSlug(): ?string
  471.     {
  472.         return $this->slug;
  473.     }
  474.     public function setSlug(?string $slug): self
  475.     {
  476.         $this->slug $slug;
  477.         return $this;
  478.     }
  479.     public function getImportSlug(): ?string
  480.     {
  481.         return $this->importSlug;
  482.     }
  483.     public function setImportSlug(?string $importSlug): self
  484.     {
  485.         $this->importSlug $importSlug;
  486.         return $this;
  487.     }
  488.     public function getImportSlugEn(): ?string
  489.     {
  490.         return $this->importSlugEn;
  491.     }
  492.     public function setImportSlugEn(?string $importSlugEn): self
  493.     {
  494.         $this->importSlugEn $importSlugEn;
  495.         return $this;
  496.     }
  497.     public function getCreatedBy(): Admin
  498.     {
  499.         return $this->createdBy;
  500.     }
  501.     public function getUpdatedBy(): Admin
  502.     {
  503.         return $this->updatedBy;
  504.     }
  505.     public function setUpdatedBy(Admin $updatedBy): self
  506.     {
  507.         $this->updatedBy $updatedBy;
  508.         return $this;
  509.     }
  510.     /**
  511.      * @return Collection|JournalScientificCouncilMember[]
  512.      */
  513.     public function getScientificCouncilMembers(): Collection
  514.     {
  515.         return $this->scientificCouncilMembers;
  516.     }
  517.     public function addScientificCouncilMember(JournalScientificCouncilMember $scientificCouncilMember): self
  518.     {
  519.         if ($this->scientificCouncilMembers->contains($scientificCouncilMember)) {
  520.             return $this;
  521.         }
  522.         $this->scientificCouncilMembers[] = $scientificCouncilMember;
  523.         return $this;
  524.     }
  525.     public function removeScientificCouncilMember(JournalScientificCouncilMember $scientificCouncilMember): self
  526.     {
  527.         $this->scientificCouncilMembers->removeElement($scientificCouncilMember);
  528.         return $this;
  529.     }
  530.     public function resetScientificCouncilMembers(): self
  531.     {
  532.         $this->scientificCouncilMembers = new ArrayCollection();
  533.         return $this;
  534.     }
  535.     /**
  536.      * @return Collection|JournalDomain[]
  537.      */
  538.     public function getDomains(): Collection
  539.     {
  540.         return $this->domains;
  541.     }
  542.     public function addDomain(JournalDomain $domain): self
  543.     {
  544.         if ($this->domains->contains($domain)) {
  545.             return $this;
  546.         }
  547.         $this->domains[] = $domain;
  548.         return $this;
  549.     }
  550.     public function removeDomain(JournalDomain $domain): self
  551.     {
  552.         $this->domains->removeElement($domain);
  553.         return $this;
  554.     }
  555.     public function resetDomains(): self
  556.     {
  557.         $this->domains = new ArrayCollection();
  558.         return $this;
  559.     }
  560.     /**
  561.      * @return Collection|JournalIndexationCollection[]
  562.      */
  563.     public function getIndexationCollections(): Collection
  564.     {
  565.         return $this->indexationCollections;
  566.     }
  567.     public function addIndexationCollection(JournalIndexationCollection $indexationCollection): self
  568.     {
  569.         if ($this->indexationCollections->contains($indexationCollection)) {
  570.             return $this;
  571.         }
  572.         $this->indexationCollections[] = $indexationCollection;
  573.         return $this;
  574.     }
  575.     public function removeIndexationCollection(JournalIndexationCollection $indexationCollection): self
  576.     {
  577.         $this->indexationCollections->removeElement($indexationCollection);
  578.         return $this;
  579.     }
  580.     public function resetIndexationCollections(): self
  581.     {
  582.         $this->indexationCollections = new ArrayCollection();
  583.         return $this;
  584.     }
  585.     /**
  586.      * @return Collection|JournalIndicator[]
  587.      */
  588.     public function getIndicators(): Collection
  589.     {
  590.         return $this->indicators;
  591.     }
  592.     public function addIndicator(JournalIndicator $indicator): self
  593.     {
  594.         if ($this->indicators->contains($indicator)) {
  595.             return $this;
  596.         }
  597.         $this->indicators[] = $indicator;
  598.         return $this;
  599.     }
  600.     public function removeIndicator(JournalIndicator $indicator): self
  601.     {
  602.         $this->indicators->removeElement($indicator);
  603.         return $this;
  604.     }
  605.     public function resetIndicators(): self
  606.     {
  607.         $this->indicators = new ArrayCollection();
  608.         return $this;
  609.     }
  610.     /**
  611.      * @return Collection|JournalAffiliation[]
  612.      */
  613.     public function getAffiliations(): Collection
  614.     {
  615.         return $this->affiliations;
  616.     }
  617.     public function addAffiliation(JournalAffiliation $affiliation): self
  618.     {
  619.         if ($this->affiliations->contains($affiliation)) {
  620.             return $this;
  621.         }
  622.         $this->affiliations[] = $affiliation;
  623.         return $this;
  624.     }
  625.     public function removeAffiliation(JournalAffiliation $affiliation): self
  626.     {
  627.         $this->affiliations->removeElement($affiliation);
  628.         return $this;
  629.     }
  630.     public function resetAffiliations(): self
  631.     {
  632.         $this->affiliations = new ArrayCollection();
  633.         return $this;
  634.     }
  635.     /**
  636.      * @return Collection|JournalPublisher[]
  637.      */
  638.     public function getPublishers(): Collection
  639.     {
  640.         return $this->publishers;
  641.     }
  642.     public function addPublisher(JournalPublisher $publisher): self
  643.     {
  644.         if ($this->publishers->contains($publisher)) {
  645.             return $this;
  646.         }
  647.         $this->publishers[] = $publisher;
  648.         return $this;
  649.     }
  650.     public function removePublisher(JournalPublisher $publisher): self
  651.     {
  652.         $this->publishers->removeElement($publisher);
  653.         return $this;
  654.     }
  655.     public function resetPublishers(): self
  656.     {
  657.         $this->publishers = new ArrayCollection();
  658.         return $this;
  659.     }
  660.     public function getVisiblePublishers(): Collection
  661.     {
  662.         return $this->publishers->filter(
  663.             fn (JournalPublisher $publisher) =>
  664.             $publisher->getStat() === true &&
  665.                 $publisher->getPublisher()->getStat() === true
  666.         );
  667.     }
  668.     /**
  669.      * @return Collection|JournalPartner[]
  670.      */
  671.     public function getPartners(): Collection
  672.     {
  673.         return $this->partners;
  674.     }
  675.     public function addPartner(JournalPartner $partner): self
  676.     {
  677.         if ($this->partners->contains($partner)) {
  678.             return $this;
  679.         }
  680.         $this->partners[] = $partner;
  681.         return $this;
  682.     }
  683.     public function removePartner(JournalPartner $partner): self
  684.     {
  685.         $this->partners->removeElement($partner);
  686.         return $this;
  687.     }
  688.     public function resetPartners(): self
  689.     {
  690.         $this->partners = new ArrayCollection();
  691.         return $this;
  692.     }
  693.     /**
  694.      * @return Collection|JournalSlide[]
  695.      */
  696.     public function getSlides(): Collection
  697.     {
  698.         return $this->slides;
  699.     }
  700.     public function addSlide(JournalSlide $slide): self
  701.     {
  702.         if ($this->slides->contains($slide)) {
  703.             return $this;
  704.         }
  705.         $this->slides[] = $slide;
  706.         return $this;
  707.     }
  708.     public function removeSlide(JournalSlide $slide): self
  709.     {
  710.         $this->slides->removeElement($slide);
  711.         return $this;
  712.     }
  713.     public function resetSlides(): self
  714.     {
  715.         $this->slides = new ArrayCollection();
  716.         return $this;
  717.     }
  718.     /**
  719.      * @return Collection|JournalSection[]
  720.      */
  721.     public function getSections(): Collection
  722.     {
  723.         return $this->sections;
  724.     }
  725.     public function addSection(JournalSection $section): self
  726.     {
  727.         if ($this->sections->contains($section)) {
  728.             return $this;
  729.         }
  730.         $this->sections[] = $section;
  731.         return $this;
  732.     }
  733.     public function removeSection(JournalSection $section): self
  734.     {
  735.         $this->sections->removeElement($section);
  736.         return $this;
  737.     }
  738.     public function resetSections(): self
  739.     {
  740.         $this->sections = new ArrayCollection();
  741.         return $this;
  742.     }
  743.     /**
  744.      * @return Collection|JournalMenuItem[]
  745.      */
  746.     public function getMenuItems(): Collection
  747.     {
  748.         return $this->menuItems;
  749.     }
  750.     public function addMenuItem(JournalMenuItem $menuItem): self
  751.     {
  752.         if ($this->menuItems->contains($menuItem)) {
  753.             return $this;
  754.         }
  755.         $this->menuItems[] = $menuItem;
  756.         return $this;
  757.     }
  758.     public function removeMenuItem(JournalMenuItem $menuItem): self
  759.     {
  760.         $this->menuItems->removeElement($menuItem);
  761.         return $this;
  762.     }
  763.     public function resetMenuItems(): self
  764.     {
  765.         $this->menuItems = new ArrayCollection();
  766.         return $this;
  767.     }
  768.     /**
  769.      * @return Collection|JournalSubmenuGroup[]
  770.      */
  771.     public function getSubmenuGroups(): Collection
  772.     {
  773.         return $this->submenuGroups;
  774.     }
  775.     public function addSubmenuGroup(JournalSubmenuGroup $submenuGroup): self
  776.     {
  777.         if ($this->submenuGroups->contains($submenuGroup)) {
  778.             return $this;
  779.         }
  780.         $this->submenuGroups[] = $submenuGroup;
  781.         return $this;
  782.     }
  783.     public function removeSubmenuGroup(JournalSubmenuGroup $submenuGroup): self
  784.     {
  785.         $this->submenuGroups->removeElement($submenuGroup);
  786.         return $this;
  787.     }
  788.     public function resetSubmenuGroups(): self
  789.     {
  790.         $this->submenuGroups = new ArrayCollection();
  791.         return $this;
  792.     }
  793.     /**
  794.      * @return Collection|JournalSidemenuGroup[]
  795.      */
  796.     public function getSidemenuGroups(): Collection
  797.     {
  798.         return $this->sidemenuGroups;
  799.     }
  800.     public function addSidemenuGroup(JournalSidemenuGroup $sidemenuGroup): self
  801.     {
  802.         if ($this->sidemenuGroups->contains($sidemenuGroup)) {
  803.             return $this;
  804.         }
  805.         $this->sidemenuGroups[] = $sidemenuGroup;
  806.         return $this;
  807.     }
  808.     public function removeSidemenuGroup(JournalSidemenuGroup $sidemenuGroup): self
  809.     {
  810.         $this->sidemenuGroups->removeElement($sidemenuGroup);
  811.         return $this;
  812.     }
  813.     public function resetSidemenuGroups(): self
  814.     {
  815.         $this->sidemenuGroups = new ArrayCollection();
  816.         return $this;
  817.     }
  818.     /**
  819.      * @return Collection|JournalBanner[]
  820.      */
  821.     public function getBanners(): Collection
  822.     {
  823.         return $this->banners;
  824.     }
  825.     public function addBanner(JournalBanner $banner): self
  826.     {
  827.         if ($this->banners->contains($banner)) {
  828.             return $this;
  829.         }
  830.         $this->banners[] = $banner;
  831.         return $this;
  832.     }
  833.     public function removeBanner(JournalBanner $banner): self
  834.     {
  835.         $this->banners->removeElement($banner);
  836.         return $this;
  837.     }
  838.     public function resetBanners(): self
  839.     {
  840.         $this->banners = new ArrayCollection();
  841.         return $this;
  842.     }
  843.     /**
  844.      * @return Collection|JournalVolume[]
  845.      */
  846.     public function getVolumes(): Collection
  847.     {
  848.         return $this->volumes;
  849.     }
  850.     public function addVolume(JournalVolume $volume): self
  851.     {
  852.         if ($this->volumes->contains($volume)) {
  853.             return $this;
  854.         }
  855.         $this->volumes[] = $volume;
  856.         return $this;
  857.     }
  858.     public function removeVolume(JournalVolume $volume): self
  859.     {
  860.         $this->volumes->removeElement($volume);
  861.         return $this;
  862.     }
  863.     public function resetVolumes(): self
  864.     {
  865.         $this->volumes = new ArrayCollection();
  866.         return $this;
  867.     }
  868.     /**
  869.      * @return Collection<int, JournalIssue>
  870.      */
  871.     public function getIssues(): Collection
  872.     {
  873.         return $this->issues;
  874.     }
  875.     /**
  876.      * @return Collection<int, JournalIssue>
  877.      */
  878.     public function getVisibleIssues(): Collection
  879.     {
  880.         return $this->issues->filter(fn (JournalIssue $issue) => $issue->getStat());
  881.     }
  882.     public function addIssue(JournalIssue $issue): self
  883.     {
  884.         if ($this->issues->contains($issue)) {
  885.             return $this;
  886.         }
  887.         $this->issues[] = $issue;
  888.         return $this;
  889.     }
  890.     public function removeIssue(JournalIssue $issue): self
  891.     {
  892.         $this->issues->removeElement($issue);
  893.         return $this;
  894.     }
  895.     public function resetIssues(): self
  896.     {
  897.         $this->issues = new ArrayCollection();
  898.         return $this;
  899.     }
  900.     public function getOldestIssue(): ?JournalIssue
  901.     {
  902.         $filtered = new ArrayCollection();
  903.         foreach ($this->getIssues() as $issue) {
  904.             if ($issue->getStat() && $issue->getPublishedAt()) {
  905.                 $filtered->add($issue);
  906.             }
  907.         }
  908.         if (count($filtered) === 0) {
  909.             return null;
  910.         }
  911.         $criteria Criteria::create()->orderBy(['publishedAt' => Criteria::ASC]);
  912.         return $filtered
  913.             ->matching($criteria)
  914.             ->first();
  915.     }
  916.     /**
  917.      * @return Collection|JournalArticle[]
  918.      */
  919.     public function getArticles(): Collection
  920.     {
  921.         return $this->articles;
  922.     }
  923.     /**
  924.      * @return Collection<int, JournalArticle>
  925.      */
  926.     public function getArticlesVisible(): Collection
  927.     {
  928.         return $this->articles->filter(fn (JournalArticle $article) => $article->getStat());
  929.     }
  930.     public function addArticle(JournalArticle $article): self
  931.     {
  932.         if ($this->articles->contains($article)) {
  933.             return $this;
  934.         }
  935.         $this->articles[] = $article;
  936.         return $this;
  937.     }
  938.     public function removeArticle(JournalArticle $article): self
  939.     {
  940.         $this->articles->removeElement($article);
  941.         return $this;
  942.     }
  943.     public function resetArticles(): self
  944.     {
  945.         $this->articles = new ArrayCollection();
  946.         return $this;
  947.     }
  948.     /**
  949.      * @return Collection|JournalPage[]
  950.      */
  951.     public function getPages(): Collection
  952.     {
  953.         return $this->pages;
  954.     }
  955.     public function addPage(JournalPage $page): self
  956.     {
  957.         if ($this->pages->contains($page)) {
  958.             return $this;
  959.         }
  960.         $this->pages[] = $page;
  961.         return $this;
  962.     }
  963.     public function removePage(JournalPage $page): self
  964.     {
  965.         $this->pages->removeElement($page);
  966.         return $this;
  967.     }
  968.     public function resetPages(): self
  969.     {
  970.         $this->pages = new ArrayCollection();
  971.         return $this;
  972.     }
  973.     /**
  974.      * @return Collection|JournalNews[]
  975.      */
  976.     public function getNews(): Collection
  977.     {
  978.         return $this->news;
  979.     }
  980.     public function addNews(JournalNews $news): self
  981.     {
  982.         if ($this->news->contains($news)) {
  983.             return $this;
  984.         }
  985.         $this->news[] = $news;
  986.         return $this;
  987.     }
  988.     public function removeNews(JournalNews $news): self
  989.     {
  990.         $this->news->removeElement($news);
  991.         return $this;
  992.     }
  993.     public function resetNews(): self
  994.     {
  995.         $this->news = new ArrayCollection();
  996.         return $this;
  997.     }
  998.     /**
  999.      * @return Collection|JournalCollection[]
  1000.      */
  1001.     public function getCollections(): Collection
  1002.     {
  1003.         return $this->collections;
  1004.     }
  1005.     public function addCollection(JournalCollection $collection): self
  1006.     {
  1007.         if ($this->collections->contains($collection)) {
  1008.             return $this;
  1009.         }
  1010.         $this->collections[] = $collection;
  1011.         return $this;
  1012.     }
  1013.     public function removeCollection(JournalCollection $news): self
  1014.     {
  1015.         $this->news->removeElement($news);
  1016.         return $this;
  1017.     }
  1018.     public function resetCollections(): self
  1019.     {
  1020.         $this->collections = new ArrayCollection();
  1021.         return $this;
  1022.     }
  1023.     public function clone(Admin $createdBy null): self
  1024.     {
  1025.         $clone = clone $this;
  1026.         $clone->id null;
  1027.         $clone->setUuid();
  1028.         $clone->ord 0;
  1029.         $clone->stat false;
  1030.         $clone->importId null;
  1031.         $clone->data $this->data->clone($clone);
  1032.         $clone->view $this->view->clone($clone);
  1033.         $nativeLanguage $clone->getNativeLanguage();
  1034.         $nativeTranslation $clone->getTranslation($nativeLanguage);
  1035.         $nativeTranslation->setTitle($nativeTranslation->getTitle() . ' [KOPIA]');
  1036.         ClassUtils::cloneCollection($this$clone'translations');
  1037.         $clone
  1038.             ->resetDomains()
  1039.             ->resetIndexationCollections()
  1040.             ->resetIndicators()
  1041.             ->resetAffiliations()
  1042.             ->resetPublishers()
  1043.             ->resetPartners()
  1044.             ->resetSlides()
  1045.             ->resetSections()
  1046.             ->resetMenuItems()
  1047.             ->resetSubmenuGroups()
  1048.             ->resetBanners()
  1049.             ->resetVolumes()
  1050.             ->resetIssues()
  1051.             ->resetArticles()
  1052.             ->resetPages()
  1053.             ->resetNews()
  1054.             ->resetCollections()
  1055.             ->resetEditorialGroups()
  1056.             ->resetScientificCouncilMembers();
  1057.         $clone->createdBy $createdBy ?? $clone->createdBy;
  1058.         $clone->updatedBy $createdBy ?? $clone->updatedBy;
  1059.         $clone->createdAt = new \DateTimeImmutable();
  1060.         $clone->updatedAt = new \DateTimeImmutable();
  1061.         return $clone;
  1062.     }
  1063.     public function isSortArticlesByOrd(): bool
  1064.     {
  1065.         return $this->sortArticlesByOrd;
  1066.     }
  1067.     public function setSortArticlesByOrd(bool $sortArticlesByOrd): self
  1068.     {
  1069.         $this->sortArticlesByOrd $sortArticlesByOrd;
  1070.         return $this;
  1071.     }
  1072.     /**
  1073.      * @return Collection<int, JournalFooter>
  1074.      */
  1075.     public function getSmallFooters(): Collection
  1076.     {
  1077.         return $this->smallFooters;
  1078.     }
  1079.     public function addSmallFooter(JournalFooter $smallFooter): self
  1080.     {
  1081.         if (!$this->smallFooters->contains($smallFooter)) {
  1082.             $this->smallFooters->add($smallFooter);
  1083.             $smallFooter->setParent($this);
  1084.         }
  1085.         return $this;
  1086.     }
  1087.     public function removeSmallFooter(JournalFooter $smallFooter): self
  1088.     {
  1089.         if ($this->smallFooters->removeElement($smallFooter)) {
  1090.             // set the owning side to null (unless already changed)
  1091.             if ($smallFooter->getParent() === $this) {
  1092.                 $smallFooter->setParent(null);
  1093.             }
  1094.         }
  1095.         return $this;
  1096.     }
  1097.     public function getVisibleSmallFooters(): Collection
  1098.     {
  1099.         return $this->smallFooters->filter(fn (JournalFooter $smallFooter) => $smallFooter->getStat());
  1100.     }
  1101.     public function getStatsAmountOfArticles(): int
  1102.     {
  1103.         return $this->statsAmountOfArticles;
  1104.     }
  1105.     public function setStatsAmountOfArticles(int $statsAmountOfArticles): self
  1106.     {
  1107.         $this->statsAmountOfArticles $statsAmountOfArticles;
  1108.         return $this;
  1109.     }
  1110.     public function getStatsAmountOfIssues(): int
  1111.     {
  1112.         return $this->statsAmountOfIssues;
  1113.     }
  1114.     public function setStatsAmountOfIssues(int $statsAmountOfIssues): self
  1115.     {
  1116.         $this->statsAmountOfIssues $statsAmountOfIssues;
  1117.         return $this;
  1118.     }
  1119.     public function getStatsAmountOfVolumes(): int
  1120.     {
  1121.         return $this->statsAmountOfVolumes;
  1122.     }
  1123.     public function setStatsAmountOfVolumes(int $statsAmountOfVolumes): self
  1124.     {
  1125.         $this->statsAmountOfVolumes $statsAmountOfVolumes;
  1126.         return $this;
  1127.     }
  1128.     public function getStatsAmountOfViews(): int
  1129.     {
  1130.         return $this->statsAmountOfViews;
  1131.     }
  1132.     public function setStatsAmountOfViews(int $statsAmountOfViews): self
  1133.     {
  1134.         $this->statsAmountOfViews $statsAmountOfViews;
  1135.         return $this;
  1136.     }
  1137.     public function getStatsAmountOfDownloads(): int
  1138.     {
  1139.         return $this->statsAmountOfDownloads;
  1140.     }
  1141.     public function setStatsAmountOfDownloads(int $statsAmountOfDownloads): self
  1142.     {
  1143.         $this->statsAmountOfDownloads $statsAmountOfDownloads;
  1144.         return $this;
  1145.     }
  1146.     public function getStatsEarliestArticlePublicationDate(): ?\DateTimeImmutable
  1147.     {
  1148.         return $this->statsEarliestArticlePublicationDate;
  1149.     }
  1150.     public function setStatsEarliestArticlePublicationDate(?string $statsEarliestArticlePublicationDate): self
  1151.     {
  1152.         if (!$statsEarliestArticlePublicationDate) {
  1153.             return $this;
  1154.         }
  1155.         $this->statsEarliestArticlePublicationDate = new \DateTimeImmutable($statsEarliestArticlePublicationDate);
  1156.         return $this;
  1157.     }
  1158.     public function getStatsLatestArticlePublicationDate(): ?\DateTimeImmutable
  1159.     {
  1160.         return $this->statsLatestArticlePublicationDate;
  1161.     }
  1162.     public function setStatsLatestArticlePublicationDate(?string $statsLatestArticlePublicationDate): self
  1163.     {
  1164.         if (!$statsLatestArticlePublicationDate) {
  1165.             return $this;
  1166.         }
  1167.         $this->statsLatestArticlePublicationDate = new \DateTimeImmutable($statsLatestArticlePublicationDate);
  1168.         return $this;
  1169.     }
  1170.     #[Groups(['read:stats'])]
  1171.     public function getStatsAffiliations(): ?string
  1172.     {
  1173.         $affiliations = [];
  1174.         /** @var JournalAffiliation */
  1175.         foreach ($this->affiliations as $entry) {
  1176.             if (isset($affiliations[$entry->getAffiliation()->getUuid()->toString()])) {
  1177.                 continue;
  1178.             }
  1179.             $affiliations[$entry->getAffiliation()->getUuid()->toString()] =
  1180.                 $entry->getAffiliation()->readNativeLanguageTranslation('title');
  1181.         }
  1182.         sort($affiliations);
  1183.         return implode(', ',  $affiliations);
  1184.     }
  1185.     #[Groups(['read:stats'])]
  1186.     public function getStatsAffiliationTypes(): ?string
  1187.     {
  1188.         $types = [];
  1189.         /** @var JournalAffiliation */
  1190.         foreach ($this->affiliations as $entry) {
  1191.             if (!($type $entry->getAffiliation()?->getType())) {
  1192.                 continue;
  1193.             }
  1194.             if (isset($types[$type->getUuid()->toString()])) {
  1195.                 continue;
  1196.             }
  1197.             $types[$type->getUuid()->toString()] = $type->getTitle();
  1198.         }
  1199.         sort($types);
  1200.         return implode(', ',  $types);
  1201.     }
  1202.     #[Groups(['read:stats'])]
  1203.     public function getStatsDomains(): ?string
  1204.     {
  1205.         $domains = [];
  1206.         /** @var JournalDomain */
  1207.         foreach ($this->domains as $entry) {
  1208.             if (isset($domains[$entry->getDomain()->getUuid()->toString()])) {
  1209.                 continue;
  1210.             }
  1211.             $domains[$entry->getDomain()->getUuid()->toString()] =
  1212.                 $entry->getDomain()->readNativeLanguageTranslation('title');
  1213.         }
  1214.         sort($domains);
  1215.         return implode(', ',  $domains);
  1216.     }
  1217.     #[Groups(['read:stats'])]
  1218.     public function getStatsType(): ?string
  1219.     {
  1220.         return $this->data->getType()?->getTitle();
  1221.     }
  1222.     #[Groups(['read:stats'])]
  1223.     public function getStatsState(): ?string
  1224.     {
  1225.         return $this->data->getState()?->getTitle();
  1226.     }
  1227. }