Ensure podcast publish_at field is always set and sortable and fix sorting issues with the Podcast Episodes admin UI.

This commit is contained in:
Buster Neece 2024-03-19 07:27:48 -05:00
parent eed6811b38
commit 838095eee6
No known key found for this signature in database
8 changed files with 56 additions and 31 deletions

View File

@ -189,7 +189,7 @@ const fields: DataTableField[] = [
key: 'is_published', key: 'is_published',
label: $gettext('Is Published'), label: $gettext('Is Published'),
visible: false, visible: false,
sortable: true, sortable: false,
selectable: true selectable: true
}, },
{ {

View File

@ -14,7 +14,6 @@ use App\Entity\Repository\PodcastEpisodeRepository;
use App\Http\Response; use App\Http\Response;
use App\Http\ServerRequest; use App\Http\ServerRequest;
use App\OpenApi; use App\OpenApi;
use App\Paginator;
use App\Service\Flow\UploadedFile; use App\Service\Flow\UploadedFile;
use Doctrine\Common\Collections\Order; use Doctrine\Common\Collections\Order;
use InvalidArgumentException; use InvalidArgumentException;
@ -213,7 +212,6 @@ final class PodcastEpisodesController extends AbstractApiCrudController
->join('e.podcast', 'p') ->join('e.podcast', 'p')
->leftJoin('e.media', 'pm') ->leftJoin('e.media', 'pm')
->where('e.podcast = :podcast') ->where('e.podcast = :podcast')
->orderBy('e.created_at', 'DESC')
->setParameter('podcast', $podcast); ->setParameter('podcast', $podcast);
$queryBuilder = $this->searchQueryBuilder( $queryBuilder = $this->searchQueryBuilder(
@ -224,25 +222,18 @@ final class PodcastEpisodesController extends AbstractApiCrudController
] ]
); );
$episodes = array_map( $queryBuilder = $this->sortQueryBuilder(
fn($row) => $this->viewRecord($row, $request),
$queryBuilder->getQuery()->getResult()
);
$episodes = $this->sortArray(
$request, $request,
$episodes, $queryBuilder,
[ [
'is_published' => 'is_published', 'publish_at' => 'e.publish_at',
'publish_at' => 'publish_at', 'is_explicit' => 'e.is_explicit',
'is_explicit' => 'is_explicit',
], ],
'id', 'e.publish_at',
Order::Descending Order::Descending
); );
$paginator = Paginator::fromArray($episodes, $request); return $this->listPaginatedFromQuery($request, $response, $queryBuilder->getQuery());
return $paginator->write($response);
} }
/** /**

View File

@ -37,7 +37,7 @@ final class ListEpisodesAction implements SingleActionInterface
->leftJoin('e.playlist_media', 'sm') ->leftJoin('e.playlist_media', 'sm')
->where('e.podcast = :podcast') ->where('e.podcast = :podcast')
->setParameter('podcast', $podcast) ->setParameter('podcast', $podcast)
->andWhere('e.publish_at IS NULL OR e.publish_at <= :publishTime') ->andWhere('e.publish_at <= :publishTime')
->setParameter('publishTime', time()) ->setParameter('publishTime', time())
->andWhere( ->andWhere(
'(p.source = :sourceManual AND pm.id IS NOT NULL) OR (p.source = :sourcePlaylist AND sm.id IS NOT NULL)' '(p.source = :sourceManual AND pm.id IS NOT NULL) OR (p.source = :sourcePlaylist AND sm.id IS NOT NULL)'

View File

@ -68,15 +68,14 @@ trait CanSortResults
ServerRequest $request, ServerRequest $request,
Order $defaultSortOrder = Order::Ascending Order $defaultSortOrder = Order::Ascending
): array { ): array {
$sortValue = Types::stringOrNull($request->getParam('sort'), true);
$sortOrder = Types::stringOrNull($request->getParam('sortOrder'), true); $sortOrder = Types::stringOrNull($request->getParam('sortOrder'), true);
$orderEnum = (null !== $sortOrder)
? Order::tryFrom(strtoupper($sortOrder)) ?? $defaultSortOrder
: $defaultSortOrder;
return [ return [
Types::stringOrNull($request->getParam('sort'), true), $sortValue,
$orderEnum, (null !== $sortValue && null !== $sortOrder)
? Order::tryFrom(strtoupper($sortOrder)) ?? $defaultSortOrder
: $defaultSortOrder,
]; ];
} }

View File

@ -34,10 +34,10 @@ final class PodcastEpisode
public int $created_at; public int $created_at;
#[OA\Property] #[OA\Property]
public bool $is_published = true; public int $publish_at;
#[OA\Property] #[OA\Property]
public ?int $publish_at = null; public bool $is_published = true;
#[OA\Property] #[OA\Property]
public bool $has_media = false; public bool $has_media = false;

View File

@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
namespace App\Entity\Migration;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20240319115513 extends AbstractMigration
{
public function getDescription(): string
{
return 'Make Podcast Episode publish date always have a value.';
}
public function up(Schema $schema): void
{
$this->addSql(
<<<'SQL'
UPDATE podcast_episode
SET publish_at = created_at
WHERE publish_at IS NULL
SQL
);
$this->addSql('ALTER TABLE podcast_episode CHANGE publish_at publish_at INT NOT NULL');
}
public function down(Schema $schema): void
{
$this->addSql('ALTER TABLE podcast_episode CHANGE publish_at publish_at INT DEFAULT NULL');
}
}

View File

@ -46,8 +46,8 @@ class PodcastEpisode implements IdentifiableEntityInterface
#[Assert\NotBlank] #[Assert\NotBlank]
protected string $description; protected string $description;
#[ORM\Column(nullable: true)] #[ORM\Column]
protected ?int $publish_at = null; protected int $publish_at;
#[ORM\Column] #[ORM\Column]
protected bool $explicit; protected bool $explicit;
@ -63,6 +63,7 @@ class PodcastEpisode implements IdentifiableEntityInterface
{ {
$this->podcast = $podcast; $this->podcast = $podcast;
$this->created_at = time(); $this->created_at = time();
$this->publish_at = time();
} }
public function getPodcast(): Podcast public function getPodcast(): Podcast
@ -126,14 +127,14 @@ class PodcastEpisode implements IdentifiableEntityInterface
return $this; return $this;
} }
public function getPublishAt(): ?int public function getPublishAt(): int
{ {
return $this->publish_at; return $this->publish_at;
} }
public function setPublishAt(?int $publishAt): self public function setPublishAt(?int $publishAt): self
{ {
$this->publish_at = $publishAt; $this->publish_at = $publishAt ?? $this->created_at;
return $this; return $this;
} }
@ -181,7 +182,7 @@ class PodcastEpisode implements IdentifiableEntityInterface
public function isPublished(): bool public function isPublished(): bool
{ {
if ($this->getPublishAt() !== null && $this->getPublishAt() > time()) { if ($this->getPublishAt() > time()) {
return false; return false;
} }

View File

@ -52,7 +52,7 @@ final class PodcastRepository extends Repository
LEFT JOIN pe.playlist_media sm LEFT JOIN pe.playlist_media sm
WHERE WHERE
((p.source = :sourceManual AND pm.id IS NOT NULL) OR (p.source = :sourcePlaylist AND sm.id IS NOT NULL)) ((p.source = :sourceManual AND pm.id IS NOT NULL) OR (p.source = :sourcePlaylist AND sm.id IS NOT NULL))
AND (pe.publish_at IS NULL OR pe.publish_at <= :time) AND (pe.publish_at <= :time)
DQL DQL
)->setParameter('time', time()) )->setParameter('time', time())
->setParameter('sourceManual', PodcastSources::Manual->value) ->setParameter('sourceManual', PodcastSources::Manual->value)