mirror of
https://github.com/standardebooks/web.git
synced 2025-07-07 07:10:29 -04:00
Add a CollectionMembership class
This commit is contained in:
parent
a25660bc8b
commit
ee29c526f8
7 changed files with 184 additions and 62 deletions
|
@ -7,9 +7,9 @@ use function Safe\preg_replace;
|
||||||
class Collection{
|
class Collection{
|
||||||
use Traits\Accessor;
|
use Traits\Accessor;
|
||||||
|
|
||||||
|
public int $CollectionId;
|
||||||
public string $Name;
|
public string $Name;
|
||||||
public string $UrlName;
|
public string $UrlName;
|
||||||
public ?int $SequenceNumber = null;
|
|
||||||
public ?string $Type = null;
|
public ?string $Type = null;
|
||||||
protected ?string $_Url = null;
|
protected ?string $_Url = null;
|
||||||
|
|
||||||
|
@ -28,7 +28,77 @@ class Collection{
|
||||||
return $instance;
|
return $instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exceptions\CollectionNotFoundException
|
||||||
|
*/
|
||||||
|
public static function Get(?int $collectionId): Collection{
|
||||||
|
if($collectionId === null){
|
||||||
|
throw new Exceptions\CollectionNotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = Db::Query('
|
||||||
|
SELECT *
|
||||||
|
from Collections
|
||||||
|
where CollectionId = ?
|
||||||
|
', [$collectionId], Collection::class);
|
||||||
|
|
||||||
|
return $result[0] ?? throw new Exceptions\CollectionNotFoundException();;
|
||||||
|
}
|
||||||
|
|
||||||
public function GetSortedName(): string{
|
public function GetSortedName(): string{
|
||||||
return preg_replace('/^(the|and|a|)\s/ius', '', $this->Name);
|
return preg_replace('/^(the|and|a|)\s/ius', '', $this->Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exceptions\ValidationException
|
||||||
|
*/
|
||||||
|
public function Validate(): void{
|
||||||
|
$error = new Exceptions\ValidationException();
|
||||||
|
|
||||||
|
if(strlen($this->Name) > EBOOKS_MAX_STRING_LENGTH){
|
||||||
|
$error->Add(new Exceptions\StringTooLongException('Collection name: '. $this->Name));
|
||||||
|
}
|
||||||
|
|
||||||
|
if($this->Type !== null && ($this->Type != 'series' && $this->Type != 'set')){
|
||||||
|
$error->Add(new Exceptions\InvalidCollectionTypeException($this->Type));
|
||||||
|
}
|
||||||
|
|
||||||
|
if($error->HasExceptions){
|
||||||
|
throw $error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exceptions\ValidationException
|
||||||
|
*/
|
||||||
|
public function Create(): void{
|
||||||
|
$this->Validate();
|
||||||
|
|
||||||
|
Db::Query('
|
||||||
|
INSERT into Collections (Name, UrlName, Type)
|
||||||
|
values (?,
|
||||||
|
?,
|
||||||
|
?)
|
||||||
|
', [$this->Name, $this->UrlName, $this->Type]);
|
||||||
|
$this->CollectionId = Db::GetLastInsertedId();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exceptions\ValidationException
|
||||||
|
*/
|
||||||
|
public function GetByUrlNameOrCreate(string $urlName): Collection{
|
||||||
|
$result = Db::Query('
|
||||||
|
SELECT *
|
||||||
|
from Collections
|
||||||
|
where UrlName = ?
|
||||||
|
', [$urlName], Collection::class);
|
||||||
|
|
||||||
|
if(isset($result[0])){
|
||||||
|
return $result[0];
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
$this->Create();
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
12
lib/CollectionMembership.php
Normal file
12
lib/CollectionMembership.php
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?
|
||||||
|
use function Safe\preg_replace;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @property Collection $Collection
|
||||||
|
*/
|
||||||
|
class CollectionMembership{
|
||||||
|
use Traits\Accessor;
|
||||||
|
|
||||||
|
public ?int $SequenceNumber = null;
|
||||||
|
protected ?Collection $_Collection = null;
|
||||||
|
}
|
121
lib/Ebook.php
121
lib/Ebook.php
|
@ -15,7 +15,7 @@ use function Safe\shell_exec;
|
||||||
* @property array<GitCommit> $GitCommits
|
* @property array<GitCommit> $GitCommits
|
||||||
* @property array<EbookTag> $Tags
|
* @property array<EbookTag> $Tags
|
||||||
* @property array<LocSubject> $LocSubjects
|
* @property array<LocSubject> $LocSubjects
|
||||||
* @property array<Collection> $Collections
|
* @property array<CollectionMembership> $CollectionMemberships
|
||||||
* @property array<EbookSource> $Sources
|
* @property array<EbookSource> $Sources
|
||||||
* @property array<Contributor> $Authors
|
* @property array<Contributor> $Authors
|
||||||
* @property array<Contributor> $Illustrators
|
* @property array<Contributor> $Illustrators
|
||||||
|
@ -76,8 +76,8 @@ class Ebook{
|
||||||
protected $_Tags = null;
|
protected $_Tags = null;
|
||||||
/** @var array<LocSubject> $_LocSubjects */
|
/** @var array<LocSubject> $_LocSubjects */
|
||||||
protected $_LocSubjects = null;
|
protected $_LocSubjects = null;
|
||||||
/** @var array<Collection> $_Collections */
|
/** @var array<CollectionMembership> $_CollectionMemberships */
|
||||||
protected $_Collections = null;
|
protected $_CollectionMemberships = null;
|
||||||
/** @var array<EbookSource> $_Sources */
|
/** @var array<EbookSource> $_Sources */
|
||||||
protected $_Sources = null;
|
protected $_Sources = null;
|
||||||
/** @var array<Contributor> $_Authors */
|
/** @var array<Contributor> $_Authors */
|
||||||
|
@ -167,18 +167,19 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return array<Collection>
|
* @return array<CollectionMembership>
|
||||||
*/
|
*/
|
||||||
protected function GetCollections(): array{
|
protected function GetCollectionMemberships(): array{
|
||||||
if($this->_Collections === null){
|
if($this->_CollectionMemberships === null){
|
||||||
$this->_Collections = Db::Query('
|
$this->_CollectionMemberships = Db::Query('
|
||||||
SELECT *
|
SELECT *
|
||||||
from Collections
|
from CollectionEbooks
|
||||||
where EbookId = ?
|
where EbookId = ?
|
||||||
', [$this->EbookId], Collection::class);
|
order by CollectionEbookId
|
||||||
|
', [$this->EbookId], CollectionMembership::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->_Collections;
|
return $this->_CollectionMemberships;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -549,8 +550,8 @@ class Ebook{
|
||||||
|
|
||||||
$this->_IndexableText .= ' ' . $this->AlternateTitle;
|
$this->_IndexableText .= ' ' . $this->AlternateTitle;
|
||||||
|
|
||||||
foreach($this->Collections as $collection){
|
foreach($this->CollectionMemberships as $collectionMembership){
|
||||||
$this->_IndexableText .= ' ' . $collection->Name;
|
$this->_IndexableText .= ' ' . $collectionMembership->Collection->Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach($this->Authors as $author){
|
foreach($this->Authors as $author){
|
||||||
|
@ -749,20 +750,21 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get SE collections
|
// Get SE collections
|
||||||
$collections = [];
|
$collectionMemberships = [];
|
||||||
foreach($xml->xpath('/package/metadata/meta[@property="belongs-to-collection"]') ?: [] as $collection){
|
foreach($xml->xpath('/package/metadata/meta[@property="belongs-to-collection"]') ?: [] as $collection){
|
||||||
$c = Collection::FromName($collection);
|
$cm = new CollectionMembership();
|
||||||
$id = $collection->attributes()->id ?? '';
|
$cm->Collection = Collection::FromName($collection);
|
||||||
|
|
||||||
|
$id = $collection->attributes()->id ?? '';
|
||||||
foreach($xml->xpath('/package/metadata/meta[@refines="#' . $id . '"][@property="group-position"]') ?: [] as $s){
|
foreach($xml->xpath('/package/metadata/meta[@refines="#' . $id . '"][@property="group-position"]') ?: [] as $s){
|
||||||
$c->SequenceNumber = (int)$s;
|
$cm->SequenceNumber = (int)$s;
|
||||||
}
|
}
|
||||||
foreach($xml->xpath('/package/metadata/meta[@refines="#' . $id . '"][@property="collection-type"]') ?: [] as $s){
|
foreach($xml->xpath('/package/metadata/meta[@refines="#' . $id . '"][@property="collection-type"]') ?: [] as $s){
|
||||||
$c->Type = (string)$s;
|
$cm->Collection->Type = (string)$s;
|
||||||
}
|
}
|
||||||
$collections[] = $c;
|
$collectionMemberships[] = $cm;
|
||||||
}
|
}
|
||||||
$ebookFromFilesystem->Collections = $collections;
|
$ebookFromFilesystem->CollectionMemberships = $collectionMemberships;
|
||||||
|
|
||||||
// Get LoC tags
|
// Get LoC tags
|
||||||
$locSubjects = [];
|
$locSubjects = [];
|
||||||
|
@ -1125,10 +1127,25 @@ class Ebook{
|
||||||
$this->LocSubjects = $subjects;
|
$this->LocSubjects = $subjects;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws Exceptions\ValidationException
|
||||||
|
*/
|
||||||
|
private function InsertCollections(): void{
|
||||||
|
$collectionMemberships = [];
|
||||||
|
foreach($this->CollectionMemberships as $collectionMembership){
|
||||||
|
$collection = $collectionMembership->Collection;
|
||||||
|
// The updated collection has the CollectionId set for newly-created Collection objects.
|
||||||
|
$updatedCollection = $collection->GetByUrlNameOrCreate($collection->UrlName);
|
||||||
|
$collectionMembership->Collection = $updatedCollection;
|
||||||
|
$collectionMemberships[] = $collectionMembership;
|
||||||
|
}
|
||||||
|
$this->CollectionMemberships = $collectionMemberships;
|
||||||
|
}
|
||||||
|
|
||||||
public function GetCollectionPosition(Collection $collection): ?int{
|
public function GetCollectionPosition(Collection $collection): ?int{
|
||||||
foreach($this->Collections as $c){
|
foreach($this->CollectionMemberships as $cm){
|
||||||
if($c->Name == $collection->Name){
|
if($cm->Collection->Name == $collection->Name){
|
||||||
return $c->SequenceNumber;
|
return $cm->SequenceNumber;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1143,8 +1160,8 @@ class Ebook{
|
||||||
|
|
||||||
$searchString .= ' ' . $this->AlternateTitle;
|
$searchString .= ' ' . $this->AlternateTitle;
|
||||||
|
|
||||||
foreach($this->Collections as $collection){
|
foreach($this->CollectionMemberships as $collectionMembership){
|
||||||
$searchString .= ' ' . $collection->Name;
|
$searchString .= ' ' . $collectionMembership->Collection->Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach($this->Authors as $author){
|
foreach($this->Authors as $author){
|
||||||
|
@ -1414,8 +1431,8 @@ class Ebook{
|
||||||
}
|
}
|
||||||
|
|
||||||
public function IsInCollection(string $collection): bool{
|
public function IsInCollection(string $collection): bool{
|
||||||
foreach($this->Collections as $c){
|
foreach($this->CollectionMemberships as $cm){
|
||||||
if(strtolower(Formatter::RemoveDiacritics($c->Name)) == strtolower(Formatter::RemoveDiacritics($collection))){
|
if(strtolower(Formatter::RemoveDiacritics($cm->Collection->Name)) == strtolower(Formatter::RemoveDiacritics($collection))){
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1456,6 +1473,7 @@ class Ebook{
|
||||||
|
|
||||||
$this->InsertTagStrings();
|
$this->InsertTagStrings();
|
||||||
$this->InsertLocSubjectStrings();
|
$this->InsertLocSubjectStrings();
|
||||||
|
$this->InsertCollections();
|
||||||
|
|
||||||
Db::Query('
|
Db::Query('
|
||||||
INSERT into Ebooks (Identifier, WwwFilesystemPath, RepoFilesystemPath, KindleCoverUrl, EpubUrl,
|
INSERT into Ebooks (Identifier, WwwFilesystemPath, RepoFilesystemPath, KindleCoverUrl, EpubUrl,
|
||||||
|
@ -1495,8 +1513,8 @@ class Ebook{
|
||||||
|
|
||||||
$this->InsertTags();
|
$this->InsertTags();
|
||||||
$this->InsertLocSubjects();
|
$this->InsertLocSubjects();
|
||||||
|
$this->InsertCollectionMemberships();
|
||||||
$this->InsertGitCommits();
|
$this->InsertGitCommits();
|
||||||
$this->InsertCollections();
|
|
||||||
$this->InsertSources();
|
$this->InsertSources();
|
||||||
$this->InsertContributors();
|
$this->InsertContributors();
|
||||||
$this->InsertTocEntries();
|
$this->InsertTocEntries();
|
||||||
|
@ -1510,6 +1528,7 @@ class Ebook{
|
||||||
|
|
||||||
$this->InsertTagStrings();
|
$this->InsertTagStrings();
|
||||||
$this->InsertLocSubjectStrings();
|
$this->InsertLocSubjectStrings();
|
||||||
|
$this->InsertCollections();
|
||||||
|
|
||||||
Db::Query('
|
Db::Query('
|
||||||
UPDATE Ebooks
|
UPDATE Ebooks
|
||||||
|
@ -1552,12 +1571,12 @@ class Ebook{
|
||||||
$this->DeleteLocSubjects();
|
$this->DeleteLocSubjects();
|
||||||
$this->InsertLocSubjects();
|
$this->InsertLocSubjects();
|
||||||
|
|
||||||
|
$this->DeleteCollectionMemberships();
|
||||||
|
$this->InsertCollectionMemberships();
|
||||||
|
|
||||||
$this->DeleteGitCommits();
|
$this->DeleteGitCommits();
|
||||||
$this->InsertGitCommits();
|
$this->InsertGitCommits();
|
||||||
|
|
||||||
$this->DeleteCollections();
|
|
||||||
$this->InsertCollections();
|
|
||||||
|
|
||||||
$this->DeleteSources();
|
$this->DeleteSources();
|
||||||
$this->InsertSources();
|
$this->InsertSources();
|
||||||
|
|
||||||
|
@ -1606,6 +1625,26 @@ class Ebook{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function DeleteCollectionMemberships(): void{
|
||||||
|
Db::Query('
|
||||||
|
DELETE from CollectionEbooks
|
||||||
|
where
|
||||||
|
EbookId = ?
|
||||||
|
', [$this->EbookId]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function InsertCollectionMemberships(): void{
|
||||||
|
foreach($this->CollectionMemberships as $collectionMembership){
|
||||||
|
Db::Query('
|
||||||
|
INSERT into CollectionEbooks (EbookId, CollectionId, SequenceNumber)
|
||||||
|
values (?,
|
||||||
|
?,
|
||||||
|
?)
|
||||||
|
', [$this->EbookId, $collectionMembership->Collection->CollectionId, $collectionMembership->SequenceNumber]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function DeleteGitCommits(): void{
|
private function DeleteGitCommits(): void{
|
||||||
Db::Query('
|
Db::Query('
|
||||||
DELETE from GitCommits
|
DELETE from GitCommits
|
||||||
|
@ -1627,28 +1666,6 @@ class Ebook{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function DeleteCollections(): void{
|
|
||||||
Db::Query('
|
|
||||||
DELETE from Collections
|
|
||||||
where
|
|
||||||
EbookId = ?
|
|
||||||
', [$this->EbookId]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function InsertCollections(): void{
|
|
||||||
foreach($this->Collections as $collection){
|
|
||||||
Db::Query('
|
|
||||||
INSERT into Collections (EbookId, Name, UrlName, SequenceNumber, Type)
|
|
||||||
values (?,
|
|
||||||
?,
|
|
||||||
?,
|
|
||||||
?,
|
|
||||||
?)
|
|
||||||
', [$this->EbookId, $collection->Name, $collection->UrlName, $collection->SequenceNumber, $collection->Type]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private function DeleteSources(): void{
|
private function DeleteSources(): void{
|
||||||
Db::Query('
|
Db::Query('
|
||||||
DELETE from EbookSources
|
DELETE from EbookSources
|
||||||
|
|
16
lib/Exceptions/InvalidCollectionTypeException.php
Normal file
16
lib/Exceptions/InvalidCollectionTypeException.php
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Exceptions;
|
||||||
|
|
||||||
|
class InvalidCollectionTypeException extends AppException{
|
||||||
|
/** @var string $message */
|
||||||
|
protected $message = 'Collection type should be `series` or `set` according to the EPUB specification.';
|
||||||
|
|
||||||
|
public function __construct(?string $collectionType){
|
||||||
|
if($collectionType !== null && trim($collectionType) != ''){
|
||||||
|
$this->message .= ' Type provided: ' . $collectionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent::__construct($this->message);
|
||||||
|
}
|
||||||
|
}
|
|
@ -163,9 +163,11 @@ class Library{
|
||||||
public static function GetEbooksByCollection(string $collection): array{
|
public static function GetEbooksByCollection(string $collection): array{
|
||||||
$ebooks = Db::Query('
|
$ebooks = Db::Query('
|
||||||
SELECT e.*
|
SELECT e.*
|
||||||
from Ebooks e inner join Collections c using (EbookId)
|
from Ebooks e
|
||||||
|
inner join CollectionEbooks ce using (EbookId)
|
||||||
|
inner join Collections c using (CollectionId)
|
||||||
where c.UrlName = ?
|
where c.UrlName = ?
|
||||||
order by c.SequenceNumber, e.Title
|
order by ce.SequenceNumber, e.EbookCreated desc
|
||||||
', [$collection], Ebook::class);
|
', [$collection], Ebook::class);
|
||||||
|
|
||||||
return $ebooks;
|
return $ebooks;
|
||||||
|
@ -708,7 +710,9 @@ class Library{
|
||||||
$ebooks[$ebookWwwFilesystemPath] = $ebook;
|
$ebooks[$ebookWwwFilesystemPath] = $ebook;
|
||||||
|
|
||||||
// Create the collections cache
|
// Create the collections cache
|
||||||
foreach($ebook->Collections as $collection){
|
foreach($ebook->CollectionMemberships as $collectionMembership){
|
||||||
|
$collection = $collectionMembership->Collection;
|
||||||
|
$sequenceNumber = $collectionMembership->SequenceNumber;
|
||||||
$urlSafeCollection = Formatter::MakeUrlSafe($collection->Name);
|
$urlSafeCollection = Formatter::MakeUrlSafe($collection->Name);
|
||||||
if(!array_key_exists($urlSafeCollection, $ebooksByCollection)){
|
if(!array_key_exists($urlSafeCollection, $ebooksByCollection)){
|
||||||
$ebooksByCollection[$urlSafeCollection] = [];
|
$ebooksByCollection[$urlSafeCollection] = [];
|
||||||
|
@ -721,8 +725,8 @@ class Library{
|
||||||
// then later we sort by that instead of by array index.
|
// then later we sort by that instead of by array index.
|
||||||
$sortItem = new stdClass();
|
$sortItem = new stdClass();
|
||||||
$sortItem->Ebook = $ebook;
|
$sortItem->Ebook = $ebook;
|
||||||
if($collection->SequenceNumber !== null){
|
if($sequenceNumber !== null){
|
||||||
$sortItem->Ordinal = $collection->SequenceNumber;
|
$sortItem->Ordinal = $sequenceNumber;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
$sortItem->Ordinal = 1;
|
$sortItem->Ordinal = 1;
|
||||||
|
|
|
@ -10,7 +10,8 @@ try{
|
||||||
$ebooks = Library::GetEbooksByCollection($collection);
|
$ebooks = Library::GetEbooksByCollection($collection);
|
||||||
// Get the *actual* name of the collection, in case there are accent marks (like "Arsène Lupin")
|
// Get the *actual* name of the collection, in case there are accent marks (like "Arsène Lupin")
|
||||||
if(sizeof($ebooks) > 0){
|
if(sizeof($ebooks) > 0){
|
||||||
foreach($ebooks[0]->Collections as $c){
|
foreach($ebooks[0]->CollectionMemberships as $cm){
|
||||||
|
$c = $cm->Collection;
|
||||||
if($collection == Formatter::MakeUrlSafe($c->Name)){
|
if($collection == Formatter::MakeUrlSafe($c->Name)){
|
||||||
$collectionObject = $c;
|
$collectionObject = $c;
|
||||||
}
|
}
|
||||||
|
|
|
@ -148,9 +148,11 @@ catch(Exceptions\EbookNotFoundException){
|
||||||
<? if($ebook->ContributorsHtml !== null){ ?>
|
<? if($ebook->ContributorsHtml !== null){ ?>
|
||||||
<p><?= $ebook->ContributorsHtml ?></p>
|
<p><?= $ebook->ContributorsHtml ?></p>
|
||||||
<? } ?>
|
<? } ?>
|
||||||
<? if(sizeof($ebook->Collections) > 0){ ?>
|
<? if(sizeof($ebook->CollectionMemberships) > 0){ ?>
|
||||||
<? foreach($ebook->Collections as $collection){ ?>
|
<? foreach($ebook->CollectionMemberships as $collectionMembership){ ?>
|
||||||
<p><? if($collection->SequenceNumber !== null){ ?>№ <?= number_format($collection->SequenceNumber) ?> in the<? }else{ ?>Part of the<? } ?> <a href="<?= $collection->Url ?>" property="schema:isPartOf"><?= Formatter::EscapeHtml(preg_replace('/^The /ius', '', (string)$collection->Name)) ?></a>
|
<? $collection = $collectionMembership->Collection; ?>
|
||||||
|
<? $sequenceNumber = $collectionMembership->SequenceNumber; ?>
|
||||||
|
<p><? if($sequenceNumber !== null){ ?>№ <?= number_format($sequenceNumber) ?> in the<? }else{ ?>Part of the<? } ?> <a href="<?= $collection->Url ?>" property="schema:isPartOf"><?= Formatter::EscapeHtml(preg_replace('/^The /ius', '', (string)$collection->Name)) ?></a>
|
||||||
<? if($collection->Type !== null){ ?>
|
<? if($collection->Type !== null){ ?>
|
||||||
<? if(substr_compare(mb_strtolower($collection->Name), mb_strtolower($collection->Type), -strlen(mb_strtolower($collection->Type))) !== 0){ ?>
|
<? if(substr_compare(mb_strtolower($collection->Name), mb_strtolower($collection->Type), -strlen(mb_strtolower($collection->Type))) !== 0){ ?>
|
||||||
<?= $collection->Type ?>.
|
<?= $collection->Type ?>.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue