Compare commits

..

2 Commits

10 changed files with 132 additions and 7 deletions

View File

@@ -0,0 +1,34 @@
<?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 Version20240728050727 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('ALTER TABLE conference ADD slug VARCHAR(255)');
$this->addSql("UPDATE conference SET slug=CONCAT(LOWER(city), '-', year)");
$this->addSql('ALTER TABLE conference ALTER COLUMN slug SET NOT NULL');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE SCHEMA public');
$this->addSql('ALTER TABLE conference DROP slug');
}
}

View File

@@ -0,0 +1,32 @@
<?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 Version20240728051153 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('CREATE UNIQUE INDEX UNIQ_911533C8989D9B62 ON conference (slug)');
}
public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE SCHEMA public');
$this->addSql('DROP INDEX UNIQ_911533C8989D9B62');
}
}

View File

@@ -56,8 +56,6 @@ class CommentCrudController extends AbstractCrudController
]); ]);
if(Crud::PAGE_EDIT === $pageName) { if(Crud::PAGE_EDIT === $pageName) {
yield $createdAt->setFormTypeOption('disabled', true); yield $createdAt->setFormTypeOption('disabled', true);
} else {
yield $createdAt;
} }
} }
} }

View File

@@ -21,7 +21,7 @@ class ConferenceController extends AbstractController
]); ]);
} }
#[Route('/conference/{id}', name: 'conference')] #[Route('/conference/{slug}', name: 'conference')]
public function show(Request $request, Conference $conference, CommentRepository $commentRepository): Response public function show(Request $request, Conference $conference, CommentRepository $commentRepository): Response
{ {
$offset = max(0, $request->query->getInt('offset', 0)); $offset = max(0, $request->query->getInt('offset', 0));

View File

@@ -7,6 +7,7 @@ use Doctrine\DBAL\Types\Types;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity(repositoryClass: CommentRepository::class)] #[ORM\Entity(repositoryClass: CommentRepository::class)]
#[ORM\HasLifecycleCallbacks]
class Comment class Comment
{ {
#[ORM\Id] #[ORM\Id]
@@ -86,6 +87,12 @@ class Comment
return $this; return $this;
} }
#[ORM\PrePersist]
public function setCreatedAtValue()
{
$this->createdAt = new \DateTimeImmutable();
}
public function getConference(): ?Conference public function getConference(): ?Conference
{ {
return $this->conference; return $this->conference;

View File

@@ -6,8 +6,11 @@ use App\Repository\ConferenceRepository;
use Doctrine\Common\Collections\ArrayCollection; use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection; use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\Mapping as ORM;
use Doctrine\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\String\Slugger\SluggerInterface;
#[ORM\Entity(repositoryClass: ConferenceRepository::class)] #[ORM\Entity(repositoryClass: ConferenceRepository::class)]
#[UniqueEntity('slug')]
class Conference class Conference
{ {
#[ORM\Id] #[ORM\Id]
@@ -30,6 +33,9 @@ class Conference
#[ORM\OneToMany(targetEntity: Comment::class, mappedBy: 'conference', orphanRemoval: true)] #[ORM\OneToMany(targetEntity: Comment::class, mappedBy: 'conference', orphanRemoval: true)]
private Collection $comments; private Collection $comments;
#[ORM\Column(length: 255, unique: true)]
private ?string $slug = null;
public function __construct() public function __construct()
{ {
$this->comments = new ArrayCollection(); $this->comments = new ArrayCollection();
@@ -44,6 +50,13 @@ class Conference
return $this->id; return $this->id;
} }
public function computeSlug(SluggerInterface $slugger)
{
if(!$this->slug || '-' === $this->slug) {
$this->slug = (string)$slugger->slug((string)$this)->lower();
}
}
public function getCity(): ?string public function getCity(): ?string
{ {
return $this->city; return $this->city;
@@ -109,4 +122,16 @@ class Conference
return $this; return $this;
} }
public function getSlug(): ?string
{
return $this->slug;
}
public function setSlug(string $slug): static
{
$this->slug = $slug;
return $this;
}
} }

View File

@@ -0,0 +1,29 @@
<?php
namespace App\EntityListener;
use App\Entity\Conference;
use Doctrine\Bundle\DoctrineBundle\Attribute\AsEntityListener;
use Doctrine\ORM\Events;
use Doctrine\Persistence\Event\LifecycleEventArgs;
use Symfony\Component\String\Slugger\SluggerInterface;
#[AsEntityListener(event: Events::prePersist, entity: Conference::class)]
#[AsEntityListener(event: Events::preUpdate, entity: Conference::class)]
class ConferenceEntityListener
{
public function __construct(
private SluggerInterface $slugger,
) {
}
public function prePersist(Conference $conference, LifecycleEventArgs $event)
{
$conference->computeSlug($this->slugger);
}
public function preUpdate(Conference $conference, LifecycleEventArgs $event)
{
$conference->computeSlug($this->slugger);
}
}

View File

@@ -16,7 +16,7 @@
<h1><a href="{{ path('homepage') }}">Guestbook</a></h1> <h1><a href="{{ path('homepage') }}">Guestbook</a></h1>
<ul> <ul>
{%for conference in conferences %} {%for conference in conferences %}
<li><a href="{{ path('conference', { id: conference.id }) }}">{{ conference }}</a></li> <li><a href="{{ path('conference', { slug: conference.slug }) }}">{{ conference }}</a></li>
{% endfor %} {% endfor %}
</ul> </ul>
<hr/> <hr/>

View File

@@ -8,7 +8,7 @@
{% for conference in conferences %} {% for conference in conferences %}
<h4>{{ conference }}</h4> <h4>{{ conference }}</h4>
<p> <p>
<a href="{{ path('conference', { id: conference.id }) }}">View</a> <a href="{{ path('conference', { slug: conference.slug }) }}">View</a>
</p> </p>
{% endfor %} {% endfor %}
{% endblock %} {% endblock %}

View File

@@ -22,10 +22,10 @@
{% endfor %} {% endfor %}
{% if previous >= 0 %} {% if previous >= 0 %}
<a href="{{ path('conference', { id: conference.id, offset: previous }) }}">Previous</a> <a href="{{ path('conference', { slug: conference.slug, offset: previous }) }}">Previous</a>
{% endif %} {% endif %}
{% if next < comments|length %} {% if next < comments|length %}
<a href="{{ path('conference', { id: conference.id, offset: next }) }}">Next</a> <a href="{{ path('conference', { slug: conference.slug, offset: next }) }}">Next</a>
{% endif %} {% endif %}
{% else %} {% else %}
<div>No comments have been posted yet for this conference.</div> <div>No comments have been posted yet for this conference.</div>