can write and read notes
This commit is contained in:
4
.env
4
.env
@@ -23,10 +23,10 @@ APP_SECRET=
|
||||
# Format described at https://www.doctrine-project.org/projects/doctrine-dbal/en/latest/reference/configuration.html#connecting-using-a-url
|
||||
# IMPORTANT: You MUST configure your server version, either here or in config/packages/doctrine.yaml
|
||||
#
|
||||
# DATABASE_URL="sqlite:///%kernel.project_dir%/var/data_%kernel.environment%.db"
|
||||
DATABASE_URL="sqlite:///%kernel.project_dir%/var/data_%kernel.environment%.db"
|
||||
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=8.0.32&charset=utf8mb4"
|
||||
# DATABASE_URL="mysql://app:!ChangeMe!@127.0.0.1:3306/app?serverVersion=10.11.2-MariaDB&charset=utf8mb4"
|
||||
DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16&charset=utf8"
|
||||
# DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16&charset=utf8"
|
||||
###< doctrine/doctrine-bundle ###
|
||||
|
||||
###> symfony/messenger ###
|
||||
|
||||
@@ -101,7 +101,7 @@
|
||||
"symfony/browser-kit": "7.2.*",
|
||||
"symfony/css-selector": "7.2.*",
|
||||
"symfony/debug-bundle": "7.2.*",
|
||||
"symfony/maker-bundle": "^1.0",
|
||||
"symfony/maker-bundle": "^1.63",
|
||||
"symfony/phpunit-bridge": "^7.2",
|
||||
"symfony/stopwatch": "7.2.*",
|
||||
"symfony/web-profiler-bundle": "7.2.*"
|
||||
|
||||
2
composer.lock
generated
2
composer.lock
generated
@@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "f7d3da4051931b357a1b2691d865cbd3",
|
||||
"content-hash": "be4d1d2b4515d0af292b8887b9724e0b",
|
||||
"packages": [
|
||||
{
|
||||
"name": "composer/semver",
|
||||
|
||||
53
migrations/Version20250517200832.php
Normal file
53
migrations/Version20250517200832.php
Normal file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20250517200832 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE TABLE note (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, content VARCHAR(255) NOT NULL, published_date DATE NOT NULL)
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE TABLE messenger_messages (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, body CLOB NOT NULL, headers CLOB NOT NULL, queue_name VARCHAR(190) NOT NULL, created_at DATETIME NOT NULL --(DC2Type:datetime_immutable)
|
||||
, available_at DATETIME NOT NULL --(DC2Type:datetime_immutable)
|
||||
, delivered_at DATETIME DEFAULT NULL --(DC2Type:datetime_immutable)
|
||||
)
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE INDEX IDX_75EA56E0FB7336F0 ON messenger_messages (queue_name)
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE INDEX IDX_75EA56E0E3BD61CE ON messenger_messages (available_at)
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE INDEX IDX_75EA56E016BA31DB ON messenger_messages (delivered_at)
|
||||
SQL);
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql(<<<'SQL'
|
||||
DROP TABLE note
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
DROP TABLE messenger_messages
|
||||
SQL);
|
||||
}
|
||||
}
|
||||
47
migrations/Version20250517212750.php
Normal file
47
migrations/Version20250517212750.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace DoctrineMigrations;
|
||||
|
||||
use Doctrine\DBAL\Schema\Schema;
|
||||
use Doctrine\Migrations\AbstractMigration;
|
||||
|
||||
/**
|
||||
* Auto-generated Migration: Please modify to your needs!
|
||||
*/
|
||||
final class Version20250517212750 extends AbstractMigration
|
||||
{
|
||||
public function getDescription(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function up(Schema $schema): void
|
||||
{
|
||||
// this up() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql(<<<'SQL'
|
||||
ALTER TABLE note ADD COLUMN slug VARCHAR(255) NOT NULL
|
||||
SQL);
|
||||
}
|
||||
|
||||
public function down(Schema $schema): void
|
||||
{
|
||||
// this down() migration is auto-generated, please modify it to your needs
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE TEMPORARY TABLE __temp__note AS SELECT id, content, published_date FROM note
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
DROP TABLE note
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
CREATE TABLE note (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, content VARCHAR(255) NOT NULL, published_date DATE NOT NULL)
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
INSERT INTO note (id, content, published_date) SELECT id, content, published_date FROM __temp__note
|
||||
SQL);
|
||||
$this->addSql(<<<'SQL'
|
||||
DROP TABLE __temp__note
|
||||
SQL);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,15 @@
|
||||
<?php
|
||||
namespace App\Controller;
|
||||
|
||||
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;
|
||||
|
||||
class GuiController extends AbstractController {
|
||||
@@ -14,4 +21,66 @@ class GuiController extends AbstractController {
|
||||
'description' => 'Joe Carstairs\' personal website',
|
||||
];
|
||||
}
|
||||
|
||||
#[Route('/notes', name: 'notes')]
|
||||
#[Template('/notes.html.twig')]
|
||||
public function notes(): Array {
|
||||
return [
|
||||
'title' => 'Joe Carstairs\' notes',
|
||||
'description' => 'Joe Carstairs\' notes',
|
||||
];
|
||||
}
|
||||
|
||||
#[Route('/note/{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',
|
||||
'note' => $note,
|
||||
];
|
||||
}
|
||||
|
||||
#[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' => $note->getSlug()]);
|
||||
}
|
||||
|
||||
return $this->render('/write_note.html.twig', [
|
||||
'title' => 'Write for Joe Carstairs',
|
||||
'description' => 'The authoring page for Joe Carstairs\' personal website',
|
||||
'form' => $form,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
66
src/Entity/Note.php
Normal file
66
src/Entity/Note.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Entity;
|
||||
|
||||
use App\Repository\NoteRepository;
|
||||
use Doctrine\DBAL\Types\Types;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
|
||||
#[ORM\Entity(repositoryClass: NoteRepository::class)]
|
||||
class Note
|
||||
{
|
||||
#[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 $published_date = 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->published_date;
|
||||
}
|
||||
|
||||
public function setPublishedDate(\DateTime $published_date): static
|
||||
{
|
||||
$this->published_date = $published_date;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSlug(): ?string
|
||||
{
|
||||
return $this->slug;
|
||||
}
|
||||
|
||||
public function setSlug(string $slug): static
|
||||
{
|
||||
$this->slug = $slug;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
27
src/Form/Type/NoteType.php
Normal file
27
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,
|
||||
]);
|
||||
}
|
||||
}
|
||||
62
src/Repository/NoteRepository.php
Normal file
62
src/Repository/NoteRepository.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?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 {
|
||||
$min = new DateTime($date->format('c'));
|
||||
$min->setTime(0, 0, 0, 0);
|
||||
$max = new DateTime($min->format('c'));
|
||||
$max->add(DateInterval::createFromDateString('1 day'));
|
||||
|
||||
$wherePublishedOnDate = $this->createQueryBuilder('n')
|
||||
->andWhere('n.published_date >= :min')
|
||||
->andWhere('n.published_date < :max')
|
||||
->setParameter('min', $min)
|
||||
->setParameter('max', $max)
|
||||
->getQuery()
|
||||
->execute();
|
||||
|
||||
return count($wherePublishedOnDate);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @return Note[] Returns an array of Note objects
|
||||
// */
|
||||
// public function findByExampleField($value): array
|
||||
// {
|
||||
// return $this->createQueryBuilder('n')
|
||||
// ->andWhere('n.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->orderBy('n.id', 'ASC')
|
||||
// ->setMaxResults(10)
|
||||
// ->getQuery()
|
||||
// ->getResult()
|
||||
// ;
|
||||
// }
|
||||
|
||||
// public function findOneBySomeField($value): ?Note
|
||||
// {
|
||||
// return $this->createQueryBuilder('n')
|
||||
// ->andWhere('n.exampleField = :val')
|
||||
// ->setParameter('val', $value)
|
||||
// ->getQuery()
|
||||
// ->getOneOrNullResult()
|
||||
// ;
|
||||
// }
|
||||
}
|
||||
9
templates/note.html.twig
Normal file
9
templates/note.html.twig
Normal file
@@ -0,0 +1,9 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block content %}
|
||||
<section>
|
||||
<h1>Note</h1>
|
||||
<time>{{ note.getPublishedDate().format('c') }}
|
||||
<p>{{ note.content }}</p>
|
||||
</section>
|
||||
{% endblock %}
|
||||
7
templates/notes.html.twig
Normal file
7
templates/notes.html.twig
Normal file
@@ -0,0 +1,7 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block content %}
|
||||
<section>
|
||||
<h1>Notes</h1>
|
||||
</section>
|
||||
{% endblock %}
|
||||
8
templates/write_note.html.twig
Normal file
8
templates/write_note.html.twig
Normal file
@@ -0,0 +1,8 @@
|
||||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block content %}
|
||||
<section>
|
||||
<h1>Write a note</h1>
|
||||
{{ form(form) }}
|
||||
</section>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user