Browse Source

Revise routing, part 2

imwald
Nuša Pukšič 4 months ago
parent
commit
ec6bac1a93
  1. 5
      assets/styles/app.css
  2. 39
      src/Controller/DefaultController.php
  3. 40
      src/Service/RedisCacheService.php
  4. 3
      templates/layout.html.twig
  5. 37
      templates/magazine/magazine-front.html.twig
  6. 26
      templates/pages/category.html.twig
  7. 2
      templates/pages/latest.html.twig

5
assets/styles/app.css

@ -207,11 +207,6 @@ div:nth-child(odd) .featured-list { @@ -207,11 +207,6 @@ div:nth-child(odd) .featured-list {
border-bottom: 1px solid var(--color-border);
}
.featured-list .card-header img {
max-height: 500px;
aspect-ratio: 1;
}
.article-list .metadata {
display: flex;
flex-direction: row;

39
src/Controller/DefaultController.php

@ -4,6 +4,10 @@ declare(strict_types=1); @@ -4,6 +4,10 @@ declare(strict_types=1);
namespace App\Controller;
use App\Entity\Event;
use App\Enum\KindsEnum;
use App\Service\RedisCacheService;
use Doctrine\ORM\EntityManagerInterface;
use Elastica\Collapse;
use Elastica\Query;
use Elastica\Query\Terms;
@ -32,22 +36,7 @@ class DefaultController extends AbstractController @@ -32,22 +36,7 @@ class DefaultController extends AbstractController
#[Route('/', name: 'home')]
public function index(): Response
{
$cacheKey = 'home-latest-articles';
$latest = $this->redisCache->get($cacheKey, function (ItemInterface $item) {
$item->expiresAfter(13600); // about 4 hours
// get latest articles
$q = new Query();
$q->setSize(12);
$q->setSort(['createdAt' => ['order' => 'desc']]);
$col = new Collapse();
$col->setFieldname('pubkey');
$q->setCollapse($col);
return $this->finder->find($q);
});
return $this->render('home.html.twig', [
'latest' => $latest
]);
return $this->render('home.html.twig');
}
/**
@ -87,18 +76,25 @@ class DefaultController extends AbstractController @@ -87,18 +76,25 @@ class DefaultController extends AbstractController
return $this->finder->find($q);
});
return $this->render('home.html.twig', [
return $this->render('pages/latest.html.twig', [
'latest' => $latest
]);
}
/**
* Magazine front page: title, summary, category links, featured list.
* @throws InvalidArgumentException
*/
#[Route('/mag/{mag}', name: 'magazine-index')]
public function magIndex($mag) : Response
public function magIndex(string $mag, RedisCacheService $redisCache) : Response
{
return new Response('Not implemented yet', 501);
// redis cache lookup of magazine index by slug
$magazine = $redisCache->getMagazineIndex($mag);
return $this->render('magazine/magazine-front.html.twig', [
'magazine' => $magazine,
'mag' => $mag,
]);
}
/**
@ -106,9 +102,12 @@ class DefaultController extends AbstractController @@ -106,9 +102,12 @@ class DefaultController extends AbstractController
*/
#[Route('/mag/{mag}/cat/{slug}', name: 'magazine-category')]
public function magCategory($mag, $slug, CacheInterface $redisCache,
RedisCacheService $redisCacheService,
FinderInterface $finder,
LoggerInterface $logger): Response
{
$magazine = $redisCacheService->getMagazineIndex($mag);
$catIndex = $redisCache->get('magazine-' . $slug, function (){
throw new Exception('Not found');
});
@ -211,6 +210,8 @@ class DefaultController extends AbstractController @@ -211,6 +210,8 @@ class DefaultController extends AbstractController
}
return $this->render('pages/category.html.twig', [
'mag' => $mag,
'magazine' => $magazine,
'list' => $list,
'category' => $category,
'index' => $catIndex

40
src/Service/RedisCacheService.php

@ -2,8 +2,12 @@ @@ -2,8 +2,12 @@
namespace App\Service;
use App\Entity\Event;
use App\Enum\KindsEnum;
use Doctrine\ORM\EntityManagerInterface;
use Psr\Cache\InvalidArgumentException;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Cache\ItemInterface;
@ -13,6 +17,7 @@ readonly class RedisCacheService @@ -13,6 +17,7 @@ readonly class RedisCacheService
public function __construct(
private NostrClient $nostrClient,
private CacheInterface $redisCache,
private EntityManagerInterface $entityManager,
private LoggerInterface $logger
)
{
@ -70,18 +75,37 @@ readonly class RedisCacheService @@ -70,18 +75,37 @@ readonly class RedisCacheService
/**
* Get a magazine index object by key.
* @param string $key
* @param string $slug
* @return object|null
* @throws InvalidArgumentException
*/
public function getMagazineIndex(string $key): ?object
public function getMagazineIndex(string $slug): ?object
{
try {
$item = $this->redisCache->getItem($key);
return $item->isHit() ? $item->get() : null;
} catch (\Exception $e) {
$this->logger->error('Error fetching magazine index.', ['exception' => $e]);
return null;
// redis cache lookup of magazine index by slug
$key = 'magazine-index-' . $slug;
return $this->redisCache->get($key, function (ItemInterface $item) use ($slug) {
$item->expiresAfter(3600); // 1 hour
$nzines = $this->entityManager->getRepository(Event::class)->findBy(['kind' => KindsEnum::PUBLICATION_INDEX]);
// filter, only keep type === magazine and slug === $mag
$nzines = array_filter($nzines, function ($index) use ($slug) {
// look for slug
return $index->getSlug() === $slug;
});
if (count($nzines) === 0) {
return new Response('Magazine not found', 404);
}
// sort by createdAt, keep newest
usort($nzines, function ($a, $b) {
return $b->getCreatedAt() <=> $a->getCreatedAt();
});
$nzine = array_pop($nzines);
$this->logger->info('Magazine lookup', ['mag' => $slug, 'found' => json_encode($nzine)]);
return $nzine;
});
}
/**

3
templates/layout.html.twig

@ -17,6 +17,9 @@ @@ -17,6 +17,9 @@
<li>
<a href="{{ path('app_search_index') }}">{{ 'heading.search'|trans }}</a>
</li>
<li>
<a href="{{ path('latest') }}">Latest</a>
</li>
</ul>
<twig:UserMenu />
{% block nav %}{% endblock %}

37
templates/magazine/magazine-front.html.twig

@ -0,0 +1,37 @@ @@ -0,0 +1,37 @@
{% extends 'layout.html.twig' %}
{% block body %}
<section class="d-flex gap-3 center ln-section--newsstand mb-1">
<div class="container mt-5 mb-5">
<h1>{{ magazine.title|default(magazine.slug|default(mag)) }}</h1>
{% if magazine.summary is defined and magazine.summary %}
<p class="eyebrow">{{ magazine.summary }}</p>
{% endif %}
{# Category links (from 'a' tags) #}
{% set categoryTags = [] %}
{% if magazine.tags is defined %}
{% for tag in magazine.tags %}
{% if tag[0] is defined and tag[0] == 'a' %}
{% set categoryTags = categoryTags|merge([tag]) %}
{% endif %}
{% endfor %}
{% endif %}
{% if categoryTags is not empty %}
<ul class="list-unstyled d-flex flex-row gap-3 justify-content-center mt-3">
{% for catTag in categoryTags %}
<li class="list-inline-item"><twig:Molecules:CategoryLink :coordinate="catTag" :mag="mag" /></li>
{% endfor %}
</ul>
{% else %}
<p class="text-muted mt-4">No categories yet.</p>
{% endif %}
</div>
</section>
<section class="mb-5">
{% if categoryTags is not empty %}
{% for cat in categoryTags %}
<twig:Organisms:FeaturedList category="{{ cat }}" class="featured-list"/>
{% endfor %}
{% endif %}
</section>
{% endblock %}

26
templates/pages/category.html.twig

@ -12,6 +12,32 @@ @@ -12,6 +12,32 @@
{% endblock %}
{% block body %}
<section class="d-flex gap-3 center ln-section--newsstand mb-3">
<div class="container mt-5 mb-5">
<h1>{{ magazine.title|default(magazine.slug|default(mag)) }}</h1>
{% if magazine.summary is defined and magazine.summary %}
<p class="eyebrow">{{ magazine.summary }}</p>
{% endif %}
{# Category links (from 'a' tags) #}
{% set categoryTags = [] %}
{% if magazine.tags is defined %}
{% for tag in magazine.tags %}
{% if tag[0] is defined and tag[0] == 'a' %}
{% set categoryTags = categoryTags|merge([tag]) %}
{% endif %}
{% endfor %}
{% endif %}
{% if categoryTags is not empty %}
<ul class="list-unstyled d-flex flex-row gap-3 justify-content-center mt-3">
{% for catTag in categoryTags %}
<li class="list-inline-item"><twig:Molecules:CategoryLink :coordinate="catTag" :mag="mag" /></li>
{% endfor %}
</ul>
{% else %}
<p class="text-muted mt-4">No categories yet.</p>
{% endif %}
</div>
</section>
<twig:Organisms:CardList :list="list" class="article-list" />
{% endblock %}

2
templates/pages/latest.html.twig

@ -1,5 +1,7 @@ @@ -1,5 +1,7 @@
{% extends 'layout.html.twig' %}
{% block body %}
<div class="w-container">
<twig:Organisms:CardList :list="latest" />
</div>
{% endblock %}

Loading…
Cancel
Save