Configure all mutual toOne relationships explicitly.

This commit is contained in:
Buster Neece 2024-03-03 16:59:59 -06:00
parent 24c56345df
commit 2f14540477
No known key found for this signature in database
20 changed files with 117 additions and 42 deletions

View File

@ -15,7 +15,7 @@ use RuntimeException;
#[
ORM\Entity(readOnly: true),
ORM\Table(name: 'analytics'),
ORM\Index(columns: ['type', 'moment'], name: 'search_idx'),
ORM\Index(name: 'search_idx', columns: ['type', 'moment']),
ORM\UniqueConstraint(name: 'stats_unique_idx', columns: ['station_id', 'type', 'moment'])
]
class Analytics implements IdentifiableEntityInterface

View File

@ -11,7 +11,7 @@ use Doctrine\ORM\Mapping as ORM;
#[
ORM\Entity(readOnly: true),
ORM\Table(name: 'audit_log'),
ORM\Index(columns: ['class', 'user', 'identifier'], name: 'idx_search')
ORM\Index(name: 'idx_search', columns: ['class', 'user', 'identifier'])
]
class AuditLog implements IdentifiableEntityInterface
{

View File

@ -6,6 +6,8 @@ namespace App\Entity;
use App\Entity\Interfaces\IdentifiableEntityInterface;
use App\Utilities\File;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use OpenApi\Attributes as OA;
use Stringable;
@ -45,6 +47,15 @@ class CustomField implements Stringable, IdentifiableEntityInterface
]
protected ?string $auto_assign = null;
/** @var Collection<int, StationMediaCustomField> */
#[ORM\OneToMany(targetEntity: StationMediaCustomField::class, mappedBy: 'field')]
protected Collection $media_fields;
public function __construct()
{
$this->media_fields = new ArrayCollection();
}
public function getName(): string
{
return $this->name;

View File

@ -10,10 +10,10 @@ use NowPlaying\Result\Client;
#[
ORM\Entity,
ORM\Table(name: 'listener'),
ORM\Index(columns: ['timestamp_end', 'timestamp_start'], name: 'idx_timestamps'),
ORM\Index(columns: ['location_country'], name: 'idx_statistics_country'),
ORM\Index(columns: ['device_os_family'], name: 'idx_statistics_os'),
ORM\Index(columns: ['device_browser_family'], name: 'idx_statistics_browser')
ORM\Index(name: 'idx_timestamps', columns: ['timestamp_end', 'timestamp_start']),
ORM\Index(name: 'idx_statistics_country', columns: ['location_country']),
ORM\Index(name: 'idx_statistics_os', columns: ['device_os_family']),
ORM\Index(name: 'idx_statistics_browser', columns: ['device_browser_family'])
]
class Listener implements
Interfaces\IdentifiableEntityInterface,

View File

@ -71,11 +71,11 @@ class Podcast implements Interfaces\IdentifiableEntityInterface
protected bool $playlist_auto_publish = true;
/** @var Collection<int, PodcastCategory> */
#[ORM\OneToMany(mappedBy: 'podcast', targetEntity: PodcastCategory::class)]
#[ORM\OneToMany(targetEntity: PodcastCategory::class, mappedBy: 'podcast')]
protected Collection $categories;
/** @var Collection<int, PodcastEpisode> */
#[ORM\OneToMany(mappedBy: 'podcast', targetEntity: PodcastEpisode::class, fetch: 'EXTRA_LAZY')]
#[ORM\OneToMany(targetEntity: PodcastEpisode::class, mappedBy: 'podcast', fetch: 'EXTRA_LAZY')]
protected Collection $episodes;
public function __construct(StorageLocation $storageLocation)

View File

@ -25,7 +25,7 @@ class PodcastEpisode implements IdentifiableEntityInterface
#[ORM\JoinColumn(name: 'podcast_id', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')]
protected Podcast $podcast;
#[ORM\OneToOne(inversedBy: 'podcast_episode')]
#[ORM\ManyToOne(inversedBy: 'podcast_episodes')]
#[ORM\JoinColumn(name: 'playlist_media_id', referencedColumnName: 'id', nullable: true, onDelete: 'CASCADE')]
protected ?StationMedia $playlist_media = null;

View File

@ -53,7 +53,7 @@ class Relay implements IdentifiableEntityInterface
protected int $updated_at;
/** @var Collection<int, StationRemote> */
#[ORM\OneToMany(mappedBy: 'relay', targetEntity: StationRemote::class)]
#[ORM\OneToMany(targetEntity: StationRemote::class, mappedBy: 'relay')]
protected Collection $remotes;
public function __construct(string $baseUrl)

View File

@ -38,7 +38,7 @@ class Role implements JsonSerializable, Stringable, IdentifiableEntityInterface
/** @var Collection<int, RolePermission> */
#[
OA\Property(type: "array", items: new OA\Items()),
ORM\OneToMany(mappedBy: 'role', targetEntity: RolePermission::class)
ORM\OneToMany(targetEntity: RolePermission::class, mappedBy: 'role')
]
protected Collection $permissions;

View File

@ -10,9 +10,9 @@ use Doctrine\ORM\Mapping as ORM;
#[
ORM\Entity,
ORM\Table(name: 'song_history'),
ORM\Index(columns: ['is_visible'], name: 'idx_is_visible'),
ORM\Index(columns: ['timestamp_start'], name: 'idx_timestamp_start'),
ORM\Index(columns: ['timestamp_end'], name: 'idx_timestamp_end')
ORM\Index(name: 'idx_is_visible', columns: ['is_visible']),
ORM\Index(name: 'idx_timestamp_start', columns: ['timestamp_start']),
ORM\Index(name: 'idx_timestamp_end', columns: ['timestamp_end'])
]
class SongHistory implements
Interfaces\SongInterface,

View File

@ -33,7 +33,7 @@ use Symfony\Component\Validator\Constraints as Assert;
OA\Schema(schema: "Station", type: "object"),
ORM\Entity,
ORM\Table(name: 'station'),
ORM\Index(columns: ['short_name'], name: 'idx_short_name'),
ORM\Index(name: 'idx_short_name', columns: ['short_name']),
ORM\HasLifecycleCallbacks,
Attributes\Auditable,
AppAssert\StationPortChecker,
@ -285,7 +285,7 @@ class Station implements Stringable, IdentifiableEntityInterface
/** @var Collection<int, SongHistory> */
#[
ORM\OneToMany(mappedBy: 'station', targetEntity: SongHistory::class),
ORM\OneToMany(targetEntity: SongHistory::class, mappedBy: 'station'),
ORM\OrderBy(['timestamp_start' => 'desc'])
]
protected Collection $history;
@ -333,7 +333,7 @@ class Station implements Stringable, IdentifiableEntityInterface
protected ?StorageLocation $podcasts_storage_location = null;
/** @var Collection<int, StationStreamer> */
#[ORM\OneToMany(mappedBy: 'station', targetEntity: StationStreamer::class)]
#[ORM\OneToMany(targetEntity: StationStreamer::class, mappedBy: 'station')]
protected Collection $streamers;
#[
@ -353,41 +353,49 @@ class Station implements Stringable, IdentifiableEntityInterface
protected ?string $fallback_path = null;
/** @var Collection<int, RolePermission> */
#[ORM\OneToMany(mappedBy: 'station', targetEntity: RolePermission::class)]
#[ORM\OneToMany(targetEntity: RolePermission::class, mappedBy: 'station')]
protected Collection $permissions;
/** @var Collection<int, StationPlaylist> */
#[
ORM\OneToMany(mappedBy: 'station', targetEntity: StationPlaylist::class),
ORM\OneToMany(targetEntity: StationPlaylist::class, mappedBy: 'station'),
ORM\OrderBy(['type' => 'ASC', 'weight' => 'DESC'])
]
protected Collection $playlists;
/** @var Collection<int, StationMount> */
#[ORM\OneToMany(mappedBy: 'station', targetEntity: StationMount::class)]
#[ORM\OneToMany(targetEntity: StationMount::class, mappedBy: 'station')]
protected Collection $mounts;
/** @var Collection<int, StationRemote> */
#[ORM\OneToMany(mappedBy: 'station', targetEntity: StationRemote::class)]
#[ORM\OneToMany(targetEntity: StationRemote::class, mappedBy: 'station')]
protected Collection $remotes;
/** @var Collection<int, StationHlsStream> */
#[ORM\OneToMany(mappedBy: 'station', targetEntity: StationHlsStream::class)]
#[ORM\OneToMany(targetEntity: StationHlsStream::class, mappedBy: 'station')]
protected Collection $hls_streams;
/** @var Collection<int, StationWebhook> */
#[ORM\OneToMany(
mappedBy: 'station',
targetEntity: StationWebhook::class,
mappedBy: 'station',
cascade: ['persist'],
fetch: 'EXTRA_LAZY'
)]
protected Collection $webhooks;
/** @var Collection<int, StationStreamerBroadcast> */
#[ORM\OneToMany(targetEntity: StationStreamerBroadcast::class, mappedBy: 'station')]
protected Collection $streamer_broadcasts;
/** @var Collection<int, SftpUser> */
#[ORM\OneToMany(mappedBy: 'station', targetEntity: SftpUser::class)]
#[ORM\OneToMany(targetEntity: SftpUser::class, mappedBy: 'station')]
protected Collection $sftp_users;
/** @var Collection<int, StationRequest> */
#[ORM\OneToMany(targetEntity: StationRequest::class, mappedBy: 'station')]
protected Collection $requests;
#[
ORM\ManyToOne,
ORM\JoinColumn(name: 'current_song_id', referencedColumnName: 'id', nullable: true, onDelete: 'SET NULL'),
@ -408,7 +416,9 @@ class Station implements Stringable, IdentifiableEntityInterface
$this->hls_streams = new ArrayCollection();
$this->webhooks = new ArrayCollection();
$this->streamers = new ArrayCollection();
$this->streamer_broadcasts = new ArrayCollection();
$this->sftp_users = new ArrayCollection();
$this->requests = new ArrayCollection();
}
public function getName(): ?string

View File

@ -23,7 +23,7 @@ use Symfony\Component\Serializer\Annotation as Serializer;
OA\Schema(type: "object"),
ORM\Entity,
ORM\Table(name: 'station_media'),
ORM\Index(columns: ['title', 'artist', 'album'], name: 'search_idx'),
ORM\Index(name: 'search_idx', columns: ['title', 'artist', 'album']),
ORM\UniqueConstraint(name: 'path_unique_idx', columns: ['path', 'storage_location_id'])
]
class StationMedia implements
@ -193,22 +193,27 @@ class StationMedia implements
/** @var Collection<int, StationPlaylistMedia> */
#[
OA\Property(type: "array", items: new OA\Items()),
ORM\OneToMany(mappedBy: 'media', targetEntity: StationPlaylistMedia::class),
ORM\OneToMany(targetEntity: StationPlaylistMedia::class, mappedBy: 'media'),
DeepNormalize(true),
Serializer\MaxDepth(1)
]
protected Collection $playlists;
/** @var Collection<int, StationMediaCustomField> */
#[ORM\OneToMany(mappedBy: 'media', targetEntity: StationMediaCustomField::class)]
#[ORM\OneToMany(targetEntity: StationMediaCustomField::class, mappedBy: 'media')]
protected Collection $custom_fields;
/** @var Collection<int, PodcastEpisode> */
#[ORM\OneToMany(targetEntity: PodcastEpisode::class, mappedBy: 'playlist_media')]
protected Collection $podcast_episodes;
public function __construct(StorageLocation $storageLocation, string $path)
{
$this->storage_location = $storageLocation;
$this->playlists = new ArrayCollection();
$this->custom_fields = new ArrayCollection();
$this->podcast_episodes = new ArrayCollection();
$this->setPath($path);
$this->generateUniqueId();
@ -445,6 +450,14 @@ class StationMedia implements
$this->custom_fields = $customFields;
}
/**
* @return Collection<int, PodcastEpisode>
*/
public function getPodcastEpisodes(): Collection
{
return $this->podcast_episodes;
}
public static function needsReprocessing(int $fileModifiedTime = 0, int $dbModifiedTime = 0): bool
{
return $fileModifiedTime > $dbModifiedTime;

View File

@ -16,7 +16,7 @@ class StationMediaCustomField implements IdentifiableEntityInterface
use Traits\HasAutoIncrementId;
use Traits\TruncateStrings;
#[ORM\ManyToOne(inversedBy: 'metadata')]
#[ORM\ManyToOne(inversedBy: 'custom_fields')]
#[ORM\JoinColumn(name: 'media_id', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')]
protected StationMedia $media;

View File

@ -175,26 +175,35 @@ class StationPlaylist implements
/** @var Collection<int, StationPlaylistMedia> */
#[
ORM\OneToMany(mappedBy: 'playlist', targetEntity: StationPlaylistMedia::class, fetch: 'EXTRA_LAZY'),
ORM\OneToMany(targetEntity: StationPlaylistMedia::class, mappedBy: 'playlist', fetch: 'EXTRA_LAZY'),
ORM\OrderBy(['weight' => 'ASC'])
]
protected Collection $media_items;
/** @var Collection<int, StationPlaylistFolder> */
#[
ORM\OneToMany(mappedBy: 'playlist', targetEntity: StationPlaylistFolder::class, fetch: 'EXTRA_LAZY')
ORM\OneToMany(targetEntity: StationPlaylistFolder::class, mappedBy: 'playlist', fetch: 'EXTRA_LAZY')
]
protected Collection $folders;
/** @var Collection<int, StationSchedule> */
#[
OA\Property(type: "array", items: new OA\Items()),
ORM\OneToMany(mappedBy: 'playlist', targetEntity: StationSchedule::class, fetch: 'EXTRA_LAZY'),
ORM\OneToMany(targetEntity: StationSchedule::class, mappedBy: 'playlist', fetch: 'EXTRA_LAZY'),
DeepNormalize(true),
Serializer\MaxDepth(1)
]
protected Collection $schedule_items;
/** @var Collection<int, Podcast> */
#[
OA\Property(type: "array", items: new OA\Items()),
ORM\OneToMany(targetEntity: Podcast::class, mappedBy: 'playlist', fetch: 'EXTRA_LAZY'),
DeepNormalize(true),
Serializer\MaxDepth(1)
]
protected Collection $podcasts;
public function __construct(Station $station)
{
$this->station = $station;
@ -207,6 +216,7 @@ class StationPlaylist implements
$this->media_items = new ArrayCollection();
$this->folders = new ArrayCollection();
$this->schedule_items = new ArrayCollection();
$this->podcasts = new ArrayCollection();
}
public function getStation(): Station
@ -414,6 +424,14 @@ class StationPlaylist implements
return $this->schedule_items;
}
/**
* @return Collection<int, Podcast>
*/
public function getPodcasts(): Collection
{
return $this->podcasts;
}
/**
* Indicates whether a playlist is enabled and has content which can be scheduled by an AutoDJ scheduler.
*

View File

@ -9,10 +9,10 @@ use Doctrine\ORM\Mapping as ORM;
#[
ORM\Entity,
ORM\Table(name: 'station_queue'),
ORM\Index(columns: ['is_played'], name: 'idx_is_played'),
ORM\Index(columns: ['timestamp_played'], name: 'idx_timestamp_played'),
ORM\Index(columns: ['sent_to_autodj'], name: 'idx_sent_to_autodj'),
ORM\Index(columns: ['timestamp_cued'], name: 'idx_timestamp_cued')
ORM\Index(name: 'idx_is_played', columns: ['is_played']),
ORM\Index(name: 'idx_timestamp_played', columns: ['timestamp_played']),
ORM\Index(name: 'idx_sent_to_autodj', columns: ['sent_to_autodj']),
ORM\Index(name: 'idx_timestamp_cued', columns: ['timestamp_cued'])
]
class StationQueue implements
Interfaces\SongInterface,

View File

@ -19,7 +19,7 @@ class StationRequest implements
{
use Traits\HasAutoIncrementId;
#[ORM\ManyToOne(inversedBy: 'media')]
#[ORM\ManyToOne(inversedBy: 'requests')]
#[ORM\JoinColumn(name: 'station_id', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')]
protected Station $station;

View File

@ -101,16 +101,25 @@ class StationStreamer implements
/** @var Collection<int, StationSchedule> */
#[
OA\Property(type: "array", items: new OA\Items()),
ORM\OneToMany(mappedBy: 'streamer', targetEntity: StationSchedule::class),
ORM\OneToMany(targetEntity: StationSchedule::class, mappedBy: 'streamer'),
DeepNormalize(true),
Serializer\MaxDepth(1)
]
protected Collection $schedule_items;
/** @var Collection<int, StationStreamerBroadcast> */
#[
ORM\OneToMany(targetEntity: StationStreamerBroadcast::class, mappedBy: 'streamer'),
DeepNormalize(true)
]
protected Collection $broadcasts;
public function __construct(Station $station)
{
$this->station = $station;
$this->schedule_items = new ArrayCollection();
$this->broadcasts = new ArrayCollection();
}
public function getStation(): Station

View File

@ -98,9 +98,13 @@ class StorageLocation implements Stringable, IdentifiableEntityInterface
protected string|int|null $storageUsed = null;
/** @var Collection<int, StationMedia> */
#[ORM\OneToMany(mappedBy: 'storage_location', targetEntity: StationMedia::class)]
#[ORM\OneToMany(targetEntity: StationMedia::class, mappedBy: 'storage_location')]
protected Collection $media;
/** @var Collection<int, UnprocessableMedia> */
#[ORM\OneToMany(targetEntity: UnprocessableMedia::class, mappedBy: 'storage_location')]
protected Collection $unprocessable_media;
public function __construct(
StorageLocationTypes $type,
StorageLocationAdapters $adapter
@ -109,6 +113,7 @@ class StorageLocation implements Stringable, IdentifiableEntityInterface
$this->adapter = $adapter;
$this->media = new ArrayCollection();
$this->unprocessable_media = new ArrayCollection();
}
public function getType(): StorageLocationTypes

View File

@ -20,7 +20,7 @@ class UnprocessableMedia implements ProcessableMediaInterface, PathAwareInterfac
public const REPROCESS_THRESHOLD_MINIMUM = 604800; // One week
#[ORM\ManyToOne(inversedBy: 'media')]
#[ORM\ManyToOne(inversedBy: 'unprocessable_media')]
#[ORM\JoinColumn(name: 'storage_location_id', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')]
protected StorageLocation $storage_location;

View File

@ -119,7 +119,7 @@ class User implements Stringable, IdentifiableEntityInterface
/** @var Collection<int, ApiKey> */
#[
ORM\OneToMany(mappedBy: 'user', targetEntity: ApiKey::class),
ORM\OneToMany(targetEntity: ApiKey::class, mappedBy: 'user'),
Groups([EntityGroupsInterface::GROUP_ADMIN, EntityGroupsInterface::GROUP_ALL]),
DeepNormalize(true)
]
@ -127,12 +127,20 @@ class User implements Stringable, IdentifiableEntityInterface
/** @var Collection<int, UserPasskey> */
#[
ORM\OneToMany(mappedBy: 'user', targetEntity: UserPasskey::class),
ORM\OneToMany(targetEntity: UserPasskey::class, mappedBy: 'user'),
Groups([EntityGroupsInterface::GROUP_ADMIN, EntityGroupsInterface::GROUP_ALL]),
DeepNormalize(true)
]
protected Collection $passkeys;
/** @var Collection<int, UserLoginToken> */
#[
ORM\OneToMany(targetEntity: UserLoginToken::class, mappedBy: 'user'),
Groups([EntityGroupsInterface::GROUP_ADMIN, EntityGroupsInterface::GROUP_ALL]),
DeepNormalize(true)
]
protected Collection $login_tokens;
public function __construct()
{
$this->created_at = time();
@ -140,6 +148,7 @@ class User implements Stringable, IdentifiableEntityInterface
$this->roles = new ArrayCollection();
$this->api_keys = new ArrayCollection();
$this->login_tokens = new ArrayCollection();
}
#[ORM\PreUpdate]

View File

@ -15,7 +15,7 @@ class UserLoginToken
{
use Traits\HasSplitTokenFields;
#[ORM\ManyToOne(fetch: 'EAGER', inversedBy: 'api_keys')]
#[ORM\ManyToOne(fetch: 'EAGER', inversedBy: 'login_tokens')]
#[ORM\JoinColumn(name: 'user_id', referencedColumnName: 'id', nullable: false, onDelete: 'CASCADE')]
protected User $user;