Move symfony stuff to own folder
This commit is contained in:
0
symfony/src/Controller/.gitignore
vendored
Normal file
0
symfony/src/Controller/.gitignore
vendored
Normal file
257
symfony/src/Controller/GuiController.php
Normal file
257
symfony/src/Controller/GuiController.php
Normal file
@@ -0,0 +1,257 @@
|
||||
<?php
|
||||
namespace App\Controller;
|
||||
|
||||
use App\Entity\BlogPost;
|
||||
use App\Entity\Note;
|
||||
use App\Form\Type\NoteType;
|
||||
use DateTime;
|
||||
use DateTimeZone;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use Symfony\Bridge\Twig\Attribute\Template;
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Annotation\Route;
|
||||
use Symfony\Component\Security\Http\Attribute\IsGranted;
|
||||
use function Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
const MONTH_NAMES = [
|
||||
'01' => 'January',
|
||||
'02' => 'February',
|
||||
'03' => 'March',
|
||||
'04' => 'April',
|
||||
'05' => 'May',
|
||||
'06' => 'June',
|
||||
'07' => 'July',
|
||||
'08' => 'August',
|
||||
'09' => 'September',
|
||||
'10' => 'October',
|
||||
'11' => 'November',
|
||||
'12' => 'December',
|
||||
];
|
||||
|
||||
class GuiController extends AbstractController {
|
||||
/**
|
||||
* @return array<string,string>
|
||||
*/
|
||||
#[Route('/', name: 'index')]
|
||||
#[Template('/index.html.twig')]
|
||||
public function index(): array {
|
||||
return [
|
||||
'title' => 'Joe Carstairs',
|
||||
'description' => 'Joe Carstairs\' personal website',
|
||||
];
|
||||
}
|
||||
/**
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
#[Route('/notes', name: 'notes')]
|
||||
#[Template('/notes.html.twig')]
|
||||
public function notes(
|
||||
EntityManagerInterface $entityManager,
|
||||
): array {
|
||||
$notes = $entityManager->getRepository(Note::class)->findAllOrderedBySlugDesc();
|
||||
$years = $this->getYears($notes);
|
||||
$notesByYear = $this->groupByYear($notes);
|
||||
$months = array_map(array: $years, callback: fn($year) => $this->getMonths($notesByYear[$year]));
|
||||
$monthsByYear = array_combine(keys: $years, values: $months);
|
||||
$groupByMonth = fn($notes) => $this->groupByMonth($notes);
|
||||
$notesByYearAndMonth = array_map(
|
||||
array: $notesByYear,
|
||||
callback: $groupByMonth,
|
||||
);
|
||||
|
||||
return [
|
||||
'title' => 'Joe Carstairs\' notes',
|
||||
'description' => 'Joe Carstairs\' notes',
|
||||
'isFeed' => True,
|
||||
'notes' => $notesByYearAndMonth,
|
||||
'years' => $years,
|
||||
'months' => $monthsByYear,
|
||||
'monthNames' => MONTH_NAMES,
|
||||
];
|
||||
}
|
||||
/**
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
#[Route('/blog', name: 'blog_posts')]
|
||||
#[Template('/blog_posts.html.twig')]
|
||||
public function blogPosts(
|
||||
EntityManagerInterface $entityManager,
|
||||
): array {
|
||||
$posts = $entityManager->getRepository(BlogPost::class)->findAllOrderedBySlugDesc();
|
||||
$years = $this->getYears($posts);
|
||||
$postsByYear = $this->groupByYear($posts);
|
||||
$months = array_map(array: $years, callback: fn($year) => $this->getMonths($postsByYear[$year]));
|
||||
$monthsByYear = array_combine(keys: $years, values: $months);
|
||||
$groupByMonth = fn($posts) => $this->groupByMonth($posts);
|
||||
$postsByYearAndMonth = array_map(
|
||||
array: $postsByYear,
|
||||
callback: $groupByMonth,
|
||||
);
|
||||
|
||||
return [
|
||||
'title' => 'Joe Carstairs\' blog',
|
||||
'description' => 'Joe Carstairs\' blog',
|
||||
'isFeed' => True,
|
||||
'posts' => $postsByYearAndMonth,
|
||||
'years' => $years,
|
||||
'months' => $monthsByYear,
|
||||
'monthNames' => MONTH_NAMES,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @@template T of \FeedEntry
|
||||
* @param $entries T[]
|
||||
* @return T[][]
|
||||
*/
|
||||
function groupByYear(array $entries): array {
|
||||
$years = $this->getYears($entries);
|
||||
$filterByYear = fn(string $year) =>
|
||||
fn($entry) => ($entry->getPublishedDate()->format('Y') == $year);
|
||||
$entriesForYear = fn(string $year) =>
|
||||
array_filter(array: $entries, callback: $filterByYear($year));
|
||||
$entriesByYear = array_map(
|
||||
array: $years,
|
||||
callback: $entriesForYear,
|
||||
);
|
||||
return array_combine(keys: $years, values: $entriesByYear);
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T of \FeedEntry
|
||||
* @param $entries T[]
|
||||
* @return string[]
|
||||
*/
|
||||
function getYears(array $entries): array {
|
||||
$getYear = fn($note): string => $note->getPublishedDate()->format('Y');
|
||||
$years = array_map(array: $entries, callback: $getYear);
|
||||
$years = array_unique($years);
|
||||
arsort($years);
|
||||
return $years;
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T of \FeedEntry
|
||||
* @param $entries T[]
|
||||
* @return T[]
|
||||
*/
|
||||
function groupByMonth(array $entries): array {
|
||||
$months = $this->getMonths($entries);
|
||||
$filterByMonth = fn(string $month) =>
|
||||
fn($entry) => ($entry->getPublishedDate()->format('m') == $month);
|
||||
$entriesByMonth = array_map(
|
||||
array: $months,
|
||||
callback: fn(string $month) =>
|
||||
array_filter(array: $entries, callback: $filterByMonth($month)),
|
||||
);
|
||||
return array_combine(keys: $months, values: $entriesByMonth);
|
||||
}
|
||||
|
||||
/**
|
||||
* @@template T of \FeedEntry
|
||||
* @param $entries T[]
|
||||
* @return string[]
|
||||
*/
|
||||
function getMonths(array $entries): array {
|
||||
$getMonth = fn($entry) => $entry->getPublishedDate()->format('m');
|
||||
$months = array_map(array: $entries, callback: $getMonth);
|
||||
$months = array_unique($months);
|
||||
arsort($months);
|
||||
return $months;
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T of \FeedEntry
|
||||
* @param $entries T[]
|
||||
* @return T[]
|
||||
*/
|
||||
function sortBySlug(array $entries): array {
|
||||
$getSlug = fn($entry): string => $entry->getSlug();
|
||||
$slugs = array_map(array: $entries, callback: $getSlug);
|
||||
$entriesBySlug = array_combine(keys: $slugs, values: $entries);
|
||||
krsort($entriesBySlug);
|
||||
return array_values($entriesBySlug);
|
||||
}
|
||||
|
||||
#[IsGranted('ROLE_EDITOR')]
|
||||
#[Route('/notes/write')]
|
||||
#[Template('/write_note.html.twig')]
|
||||
public function writeNote(
|
||||
EntityManagerInterface $entityManager,
|
||||
Request $request,
|
||||
): Response {
|
||||
$note = new Note();
|
||||
$form = $this->createForm(NoteType::class, $note);
|
||||
|
||||
$form->handleRequest($request);
|
||||
if ($form->isSubmitted() && $form->isValid()) {
|
||||
$note = $form->getData();
|
||||
|
||||
$now = new DateTime('now');
|
||||
$note->setPublishedDate($now);
|
||||
|
||||
$num_existing_notes_today = $entityManager->getRepository(Note::class)->countWherePublishedOnDate($now);
|
||||
$note->getPublishedDate()->setTimezone(new DateTimeZone('Europe/London'));
|
||||
$slug = $note->getPublishedDate()->format('Y-m-d');
|
||||
if ($num_existing_notes_today > 0) {
|
||||
$slug = $slug . '-' . $num_existing_notes_today;
|
||||
}
|
||||
$note->setSlug($slug);
|
||||
|
||||
$entityManager->persist($note);
|
||||
$entityManager->flush();
|
||||
|
||||
return $this->redirectToRoute('note', ['slug' => $slug]);
|
||||
}
|
||||
|
||||
return $this->render('/write_note.html.twig', [
|
||||
'title' => 'Write for Joe Carstairs',
|
||||
'description' => 'The authoring page for Joe Carstairs\' personal website',
|
||||
'form' => $form,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
#[Route('/notes/{slug}', name: 'note')]
|
||||
#[Template('/note.html.twig')]
|
||||
public function note(
|
||||
EntityManagerInterface $entityManager,
|
||||
string $slug,
|
||||
): array {
|
||||
$repository = $entityManager->getRepository(Note::class);
|
||||
$note = $repository->findOneBy(['slug' => $slug]);
|
||||
|
||||
return [
|
||||
'title' => 'Joe Carstairs\' notes',
|
||||
'description' => 'Joe Carstairs\' notes',
|
||||
'isFeedEntry' => True,
|
||||
'note' => $note,
|
||||
'slug' => $slug,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string,mixed>
|
||||
*/
|
||||
#[Route('/blog/{slug}', name: 'blog_post')]
|
||||
#[Template('/blog_post.html.twig')]
|
||||
public function blogPost(
|
||||
EntityManagerInterface $entityManager,
|
||||
string $slug,
|
||||
): array {
|
||||
$repository = $entityManager->getRepository(BlogPost::class);
|
||||
$post = $repository->findOneBy(['slug' => $slug]);
|
||||
|
||||
return [
|
||||
'title' => $post->getTitle(),
|
||||
'description' => $post->getDescription(),
|
||||
'isFeedEntry' => True,
|
||||
'post' => $post,
|
||||
'slug' => $slug,
|
||||
];
|
||||
}
|
||||
}
|
||||
31
symfony/src/Controller/SecurityController.php
Normal file
31
symfony/src/Controller/SecurityController.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
namespace App\Controller;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
|
||||
|
||||
class SecurityController extends AbstractController
|
||||
{
|
||||
#[Route(path: '/login', name: 'login')]
|
||||
public function login(AuthenticationUtils $authenticationUtils): Response
|
||||
{
|
||||
$error = $authenticationUtils->getLastAuthenticationError();
|
||||
$lastUsername = $authenticationUtils->getLastUsername();
|
||||
|
||||
return $this->render('security/login.html.twig', [
|
||||
'title' => 'Log in',
|
||||
'description' => 'Log in to Joe Carstairs\' personal website',
|
||||
'lastUsername' => $lastUsername,
|
||||
'error' => $error,
|
||||
]);
|
||||
}
|
||||
|
||||
#[Route(path: '/logout', name: 'logout')]
|
||||
public function logout(): void
|
||||
{
|
||||
throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
|
||||
}
|
||||
}
|
||||
0
symfony/src/Entity/.gitignore
vendored
Normal file
0
symfony/src/Entity/.gitignore
vendored
Normal file
93
symfony/src/Entity/BlogPost.php
Normal file
93
symfony/src/Entity/BlogPost.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Interface\FeedEntry;
|
||||
use App\Repository\BlogPostRepository;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: BlogPostRepository::class)]
|
||||
class BlogPost implements FeedEntry
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $slug = null;
|
||||
|
||||
#[ORM\Column(type: Types::DATE_MUTABLE)]
|
||||
private ?\DateTime $publishedDate = null;
|
||||
|
||||
#[ORM\Column(type: Types::DATE_MUTABLE, nullable: true)]
|
||||
private ?\DateTime $updatedDate = null;
|
||||
|
||||
#[ORM\Column(type: Types::TEXT)]
|
||||
private ?string $content = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $title = null;
|
||||
|
||||
#[ORM\Column(length: 1024)]
|
||||
private ?string $description = null;
|
||||
|
||||
public function getId(): ?int {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getSlug(): ?string {
|
||||
return $this->slug;
|
||||
}
|
||||
|
||||
public function setSlug(string $slug): static {
|
||||
$this->slug = $slug;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPublishedDate(): ?\DateTime {
|
||||
return $this->publishedDate;
|
||||
}
|
||||
|
||||
public function setPublishedDate(\DateTime $publishedDate): static {
|
||||
$this->publishedDate = $publishedDate;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getUpdatedDate(): ?\DateTime {
|
||||
return $this->updatedDate;
|
||||
}
|
||||
|
||||
public function setUpdatedDate(?\DateTime $updatedDate): static {
|
||||
$this->updatedDate = $updatedDate;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getContent(): ?string {
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
public function setContent(string $content): static {
|
||||
$this->content = $content;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTitle(): ?string {
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
public function setTitle(string $title): static {
|
||||
$this->title = $title;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDescription(): ?string {
|
||||
return $this->description;
|
||||
}
|
||||
|
||||
public function setDescription(string $description): static {
|
||||
$this->description = $description;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
67
symfony/src/Entity/Note.php
Normal file
67
symfony/src/Entity/Note.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Interface\FeedEntry;
|
||||
use App\Repository\NoteRepository;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: NoteRepository::class)]
|
||||
class Note implements FeedEntry
|
||||
{
|
||||
#[ORM\Id]
|
||||
#[ORM\GeneratedValue]
|
||||
#[ORM\Column]
|
||||
private ?int $id = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $content = null;
|
||||
|
||||
#[ORM\Column(type: Types::DATE_MUTABLE)]
|
||||
private ?\DateTime $publishedDate = null;
|
||||
|
||||
#[ORM\Column(length: 255)]
|
||||
private ?string $slug = null;
|
||||
|
||||
public function getId(): ?int
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
public function getContent(): ?string
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
public function setContent(string $content): static
|
||||
{
|
||||
$this->content = $content;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getPublishedDate(): ?\DateTime
|
||||
{
|
||||
return $this->publishedDate;
|
||||
}
|
||||
|
||||
public function setPublishedDate(\DateTime $publishedDate): static
|
||||
{
|
||||
$this->publishedDate = $publishedDate;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSlug(): ?string
|
||||
{
|
||||
return $this->slug;
|
||||
}
|
||||
|
||||
public function setSlug(string $slug): static
|
||||
{
|
||||
$this->slug = $slug;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
27
symfony/src/Form/Type/NoteType.php
Normal file
27
symfony/src/Form/Type/NoteType.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
namespace App\Form\Type;
|
||||
|
||||
use App\Entity\Note;
|
||||
use Symfony\Component\Form\AbstractType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
|
||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||
use Symfony\Component\Form\FormBuilderInterface;
|
||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||
|
||||
class NoteType extends AbstractType
|
||||
{
|
||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||
{
|
||||
$builder
|
||||
->add('content', TextType::class)
|
||||
->add('post', SubmitType::class)
|
||||
;
|
||||
}
|
||||
|
||||
public function configureOptions(OptionsResolver $resolver): void
|
||||
{
|
||||
$resolver->setDefaults([
|
||||
'data_class' => Note::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
8
symfony/src/Interface/FeedEntry.php
Normal file
8
symfony/src/Interface/FeedEntry.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Interface;
|
||||
|
||||
interface FeedEntry {
|
||||
function getPublishedDate(): ?\DateTime;
|
||||
function getSlug(): ?string;
|
||||
}
|
||||
11
symfony/src/Kernel.php
Normal file
11
symfony/src/Kernel.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
|
||||
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
|
||||
|
||||
class Kernel extends BaseKernel
|
||||
{
|
||||
use MicroKernelTrait;
|
||||
}
|
||||
0
symfony/src/Repository/.gitignore
vendored
Normal file
0
symfony/src/Repository/.gitignore
vendored
Normal file
28
symfony/src/Repository/BlogPostRepository.php
Normal file
28
symfony/src/Repository/BlogPostRepository.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\BlogPost;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<BlogPost>
|
||||
*/
|
||||
class BlogPostRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry) {
|
||||
parent::__construct($registry, BlogPost::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return BlogPost[]
|
||||
*/
|
||||
public function findAllOrderedBySlugDesc(): array
|
||||
{
|
||||
return $this->createQueryBuilder('n')
|
||||
->orderBy('n.slug', 'DESC')
|
||||
->getQuery()
|
||||
->getResult();
|
||||
}
|
||||
}
|
||||
43
symfony/src/Repository/NoteRepository.php
Normal file
43
symfony/src/Repository/NoteRepository.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace App\Repository;
|
||||
|
||||
use App\Entity\Note;
|
||||
use DateInterval;
|
||||
use DateTime;
|
||||
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
|
||||
use Doctrine\Persistence\ManagerRegistry;
|
||||
|
||||
/**
|
||||
* @extends ServiceEntityRepository<Note>
|
||||
*/
|
||||
class NoteRepository extends ServiceEntityRepository
|
||||
{
|
||||
public function __construct(ManagerRegistry $registry)
|
||||
{
|
||||
parent::__construct($registry, Note::class);
|
||||
}
|
||||
|
||||
public function countWherePublishedOnDate(DateTime $date): int {
|
||||
$dateStr = substr($date->format('c'), 0, 10);
|
||||
|
||||
$wherePublishedOnDate = $this->createQueryBuilder('n')
|
||||
->andWhere('n.publishedDate = :date')
|
||||
->setParameter('date', $dateStr)
|
||||
->getQuery()
|
||||
->execute();
|
||||
|
||||
return count($wherePublishedOnDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Note[] Returns an array of Note objects
|
||||
*/
|
||||
public function findAllOrderedBySlugDesc(): array
|
||||
{
|
||||
return $this->createQueryBuilder('n')
|
||||
->orderBy('n.slug', 'DESC')
|
||||
->getQuery()
|
||||
->getResult();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user