Files
ops-Gazelle/app/ArtistRole.php
2025-09-10 13:01:53 +02:00

200 lines
7.4 KiB
PHP

<?php
namespace Gazelle;
use Gazelle\Intf\CategoryHasArtist;
abstract class ArtistRole extends Base {
protected const RENDER_TEXT = 1;
protected const RENDER_HTML = 2;
protected array $artistList;
protected array $roleList;
protected array $idList;
abstract protected function artistListQuery(): \mysqli_result|bool;
abstract public function idList(): array;
abstract public function roleList(): array;
public function __construct(
protected readonly CategoryHasArtist $object,
protected readonly Manager\Artist $manager,
) {}
protected function artistList(): array {
if (!isset($this->artistList)) {
if ($this->artistListQuery()) {
$this->artistList = self::$db->to_array(false, MYSQLI_ASSOC);
} else {
$this->artistList = [];
}
}
return $this->artistList;
}
/**
* Generate the artist name. (Individual artists will be clickable, or VA)
*/
public function link(): string {
return $this->renderRole(self::RENDER_HTML);
}
/**
* Generate the artist name as text.
*/
public function text(): string {
return $this->renderRole(self::RENDER_TEXT);
}
public function nameList(): array {
$list = [];
foreach ($this->idList() as $role => $artistList) {
$list[$role] = array_map(fn ($a) => $a['name'], $artistList);
}
return $list;
}
/**
* A readable representation of the artists grouped by their roles in a
* release group. All artist roles are present as arrays (no need to see if
* the key exists). Like roleList() but some of the key names change.
* 'main' becomes 'artists'
* 'guest' becomes 'with'
* 'remixer' becomes 'remixedBy'
* 'composer' becomes 'composers'
* A role is an array of three keys: ["id" => 801, "aliasid" => 768, "name" => "The Group"]
*/
public function roleListByType(): array {
$list = $this->idList();
return [
'artists' => $list[ARTIST_MAIN] ?? [],
'with' => $list[ARTIST_GUEST] ?? [],
'remixedBy' => $list[ARTIST_REMIXER] ?? [],
'composers' => $list[ARTIST_COMPOSER] ?? [],
'conductor' => $list[ARTIST_CONDUCTOR] ?? [],
'dj' => $list[ARTIST_DJ] ?? [],
'producer' => $list[ARTIST_PRODUCER] ?? [],
'arranger' => $list[ARTIST_ARRANGER] ?? [],
];
}
/**
* Returns the id of the first artist that would be shown in the renderRole
* display. If it would be shown as "various ...", then it's the first
* artist of that role type.
*
* @return int|null
*/
public function primaryId(): int|null {
$roleList = $this->roleList();
$composerCount = count($roleList['composer'] ?? []);
$conductorCount = count($roleList['conductor'] ?? []);
$djCount = count($roleList['dj'] ?? []);
$mainCount = count($roleList['main'] ?? []);
if ($djCount) {
return $roleList['dj'][0]['id'];
} elseif ($composerCount) {
return $roleList['composer'][0]['id'];
} elseif ($mainCount) {
return $roleList['main'][0]['id'];
} elseif ($conductorCount) {
return $roleList['conductor'][0]['id'];
}
return null;
}
protected function renderRole(int $mode): string {
$roleList = $this->roleList();
$arrangerCount = count($roleList['arranger'] ?? []);
$composerCount = count($roleList['composer'] ?? []);
$conductorCount = count($roleList['conductor'] ?? []);
$djCount = count($roleList['dj'] ?? []);
$mainCount = count($roleList['main'] ?? []);
if ($composerCount + $mainCount + $conductorCount + $djCount == 0) {
return '';
}
$and = match ($mode) {
self::RENDER_HTML => ' &amp; ',
default => ' and ',
};
$chunk = [];
if ($djCount > 0) {
$chunk[] = match ($djCount) {
1 => $this->artistLink($mode, $roleList['dj'][0]),
2 => $this->artistLink($mode, $roleList['dj'][0]) . $and . $this->artistLink($mode, $roleList['dj'][1]),
default => $this->various('DJs', $roleList['dj'], $mode),
};
} else {
if ($composerCount > 0) {
$chunk[] = match ($composerCount) {
1 => $this->artistLink($mode, $roleList['composer'][0]),
2 => $this->artistLink($mode, $roleList['composer'][0]) . $and . $this->artistLink($mode, $roleList['composer'][1]),
default => $this->various('Composers', $roleList['composer'], $mode),
};
if ($arrangerCount > 0) {
$chunk[] = 'arranged by';
$chunk[] = match ($arrangerCount) {
1 => $this->artistLink($mode, $roleList['arranger'][0]),
2 => $this->artistLink($mode, $roleList['arranger'][0]) . $and . $this->artistLink($mode, $roleList['arranger'][1]),
default => $this->various('Arrangers', $roleList['arranger'], $mode),
};
}
if ($mainCount + $conductorCount > 0) {
$chunk[] = 'performed by';
}
}
if (
$composerCount > 0
&& $mainCount > 1
&& $conductorCount > 1
) {
$chunk[] = 'Various Artists';
} else {
if ($mainCount > 0) {
$chunk[] = match ($mainCount) {
1 => $this->artistLink($mode, $roleList['main'][0]),
2 => $this->artistLink($mode, $roleList['main'][0]) . $and . $this->artistLink($mode, $roleList['main'][1]),
default => $this->various('Artists', $roleList['main'], $mode),
};
}
if ($conductorCount > 0) {
if ($mainCount + $composerCount > 0 && ($composerCount < 3 || $mainCount > 0)) {
$chunk[] = 'under';
}
$chunk[] = match ($conductorCount) {
1 => $this->artistLink($mode, $roleList['conductor'][0]),
2 => $this->artistLink($mode, $roleList['conductor'][0]) . $and . $this->artistLink($mode, $roleList['conductor'][1]),
default => $this->various('Conductors', $roleList['conductor'], $mode),
};
}
}
}
return implode(' ', $chunk);
}
protected function various(string $role, array $artistList, int $mode): string {
return match ($mode) {
self::RENDER_HTML => '<span class="tooltip" style="float: none" title="' . implode(' ⁝ ', array_map(fn ($a) => $a['name'], $artistList)) . "\">Various $role</span>",
default => "Various $role",
};
}
/**
* Generate an HTML anchor for an artist
*/
protected function artistLink(int $mode, array $info): string {
return match ($mode) {
self::RENDER_HTML => '<a href="artist.php?id=' . $info['id'] . '" dir="ltr">' . html_escape($info['name']) . '</a>',
default => $info['name'],
};
}
}