src/Entity/Journal.php line 187

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