*/ class PodcastsController extends AbstractApiCrudController { protected string $entityClass = Entity\Podcast::class; protected string $resourceRouteName = 'api:stations:podcast'; public function __construct( ReloadableEntityManagerInterface $em, Serializer $serializer, ValidatorInterface $validator, protected Entity\Repository\StationRepository $stationRepository, protected Entity\Repository\PodcastRepository $podcastRepository ) { parent::__construct($em, $serializer, $validator); } public function listAction(ServerRequest $request, Response $response): ResponseInterface { $station = $request->getStation(); $queryBuilder = $this->em->createQueryBuilder() ->select('p, pc') ->from(Entity\Podcast::class, 'p') ->leftJoin('p.categories', 'pc') ->where('p.storage_location = :storageLocation') ->orderBy('p.title', 'ASC') ->setParameter('storageLocation', $station->getPodcastsStorageLocation()); $searchPhrase = trim($request->getParam('searchPhrase', '')); if (!empty($searchPhrase)) { $queryBuilder->andWhere('p.title LIKE :title') ->setParameter('title', '%' . $searchPhrase . '%'); } return $this->listPaginatedFromQuery($request, $response, $queryBuilder->getQuery()); } public function getAction( ServerRequest $request, Response $response, string $podcast_id ): ResponseInterface { $station = $request->getStation(); $record = $this->getRecord($station, $podcast_id); if (null === $record) { return $response->withStatus(404) ->withJson(Entity\Api\Error::notFound()); } $return = $this->viewRecord($record, $request); return $response->withJson($return); } public function createAction(ServerRequest $request, Response $response): ResponseInterface { $station = $request->getStation(); $parsedBody = (array)$request->getParsedBody(); /** @var Entity\Podcast $record */ $record = $this->editRecord( $parsedBody, new Entity\Podcast($station->getPodcastsStorageLocation()) ); if (!empty($parsedBody['artwork_file'])) { $artwork = UploadedFile::fromArray($parsedBody['artwork_file'], $station->getRadioTempDir()); $this->podcastRepository->writePodcastArt( $record, $artwork->readAndDeleteUploadedFile() ); $this->em->persist($record); $this->em->flush(); } return $response->withJson($this->viewRecord($record, $request)); } public function editAction( ServerRequest $request, Response $response, string $podcast_id ): ResponseInterface { $podcast = $this->getRecord($request->getStation(), $podcast_id); if ($podcast === null) { return $response->withStatus(404) ->withJson(Entity\Api\Error::notFound()); } $this->editRecord((array)$request->getParsedBody(), $podcast); return $response->withJson(Entity\Api\Status::updated()); } public function deleteAction( ServerRequest $request, Response $response, string $podcast_id ): ResponseInterface { $station = $request->getStation(); $record = $this->getRecord($station, $podcast_id); if (null === $record) { return $response->withStatus(404) ->withJson(Entity\Api\Error::notFound()); } $fsStation = new StationFilesystems($station); $this->podcastRepository->delete($record, $fsStation->getPodcastsFilesystem()); return $response->withJson(Entity\Api\Status::deleted()); } /** * @param Entity\Station $station * @param string $id * * @return Entity\Podcast|null */ protected function getRecord(Entity\Station $station, string $id): ?object { $record = $this->podcastRepository->fetchPodcastForStation($station, $id); return $record; } /** * @param Entity\Podcast $record * @param ServerRequest $request * */ protected function viewRecord(object $record, ServerRequest $request): mixed { if (!($record instanceof Entity\Podcast)) { throw new InvalidArgumentException(sprintf('Record must be an instance of %s.', $this->entityClass)); } $isInternal = ('true' === $request->getParam('internal', 'false')); $router = $request->getRouter(); $station = $request->getStation(); $return = new Entity\Api\Podcast(); $return->id = $record->getId(); $return->storage_location_id = $record->getStorageLocation()->getId(); $return->title = $record->getTitle(); $return->link = $record->getLink(); $return->description = $record->getDescription(); $return->language = $record->getLanguage(); $return->author = $record->getAuthor(); $return->email = $record->getEmail(); $categories = []; foreach ($record->getCategories() as $category) { $categories[] = $category->getCategory(); } $return->categories = $categories; $episodes = []; foreach ($record->getEpisodes() as $episode) { $episodes[] = $episode->getId(); } $return->episodes = $episodes; $return->has_custom_art = (0 !== $record->getArtUpdatedAt()); $return->art = (string)$router->fromHere( route_name: 'api:stations:podcast:art', route_params: ['podcast_id' => $record->getId() . '|' . $record->getArtUpdatedAt()], absolute: true ); $return->links = [ 'self' => (string)$router->fromHere( route_name: $this->resourceRouteName, route_params: ['podcast_id' => $record->getId()], absolute: !$isInternal ), 'episodes' => (string)$router->fromHere( route_name: 'api:stations:podcast:episodes', route_params: ['podcast_id' => $record->getId()], absolute: !$isInternal ), 'public_episodes' => (string)$router->fromHere( route_name: 'public:podcast:episodes', route_params: ['podcast_id' => $record->getId()], absolute: !$isInternal ), 'public_feed' => (string)$router->fromHere( route_name: 'public:podcast:feed', route_params: ['podcast_id' => $record->getId()], absolute: !$isInternal ), ]; $acl = $request->getAcl(); if ($acl->isAllowed(Acl::STATION_PODCASTS, $station)) { $return->links['art'] = (string)$router->fromHere( route_name: 'api:stations:podcast:art-internal', route_params: ['podcast_id' => $record->getId()], absolute: !$isInternal ); $return->links['episode_new_art'] = (string)$router->fromHere( route_name: 'api:stations:podcast:episodes:new-art', route_params: ['podcast_id' => $record->getId()], absolute: !$isInternal ); $return->links['episode_new_media'] = (string)$router->fromHere( route_name: 'api:stations:podcast:episodes:new-media', route_params: ['podcast_id' => $record->getId()], absolute: !$isInternal ); } return $return; } /** * @param mixed[] $data * @param Entity\Podcast|null $record * @param array $context * * @return Entity\Podcast */ protected function fromArray($data, $record = null, array $context = []): object { return parent::fromArray( $data, $record, array_merge( $context, [ AbstractNormalizer::CALLBACKS => [ 'categories' => function (array $newCategories, $record): void { if (!($record instanceof Entity\Podcast)) { return; } $categories = $record->getCategories(); if ($categories->count() > 0) { foreach ($categories as $existingCategories) { $this->em->remove($existingCategories); } $categories->clear(); } foreach ($newCategories as $category) { $podcastCategory = new Entity\PodcastCategory($record, $category); $this->em->persist($podcastCategory); $categories->add($podcastCategory); } }, ], ] ) ); } }