Conference URLs now use slugs instead of integer id
This commit is contained in:
34
migrations/Version20240728050727.php
Normal file
34
migrations/Version20240728050727.php
Normal 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');
|
||||
}
|
||||
}
|
||||
32
migrations/Version20240728051153.php
Normal file
32
migrations/Version20240728051153.php
Normal 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');
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
$offset = max(0, $request->query->getInt('offset', 0));
|
||||
|
||||
@@ -6,8 +6,11 @@ use App\Repository\ConferenceRepository;
|
||||
use Doctrine\Common\Collections\ArrayCollection;
|
||||
use Doctrine\Common\Collections\Collection;
|
||||
use Doctrine\ORM\Mapping as ORM;
|
||||
use Doctrine\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
|
||||
use Symfony\Component\String\Slugger\SluggerInterface;
|
||||
|
||||
#[ORM\Entity(repositoryClass: ConferenceRepository::class)]
|
||||
#[UniqueEntity('slug')]
|
||||
class Conference
|
||||
{
|
||||
#[ORM\Id]
|
||||
@@ -30,6 +33,9 @@ class Conference
|
||||
#[ORM\OneToMany(targetEntity: Comment::class, mappedBy: 'conference', orphanRemoval: true)]
|
||||
private Collection $comments;
|
||||
|
||||
#[ORM\Column(length: 255, unique: true)]
|
||||
private ?string $slug = null;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->comments = new ArrayCollection();
|
||||
@@ -44,6 +50,13 @@ class Conference
|
||||
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
|
||||
{
|
||||
return $this->city;
|
||||
@@ -109,4 +122,16 @@ class Conference
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSlug(): ?string
|
||||
{
|
||||
return $this->slug;
|
||||
}
|
||||
|
||||
public function setSlug(string $slug): static
|
||||
{
|
||||
$this->slug = $slug;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
29
src/EntityListener/ConferenceEntityListener.php
Normal file
29
src/EntityListener/ConferenceEntityListener.php
Normal 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);
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
<h1><a href="{{ path('homepage') }}">Guestbook</a></h1>
|
||||
<ul>
|
||||
{%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 %}
|
||||
</ul>
|
||||
<hr/>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
{% for conference in conferences %}
|
||||
<h4>{{ conference }}</h4>
|
||||
<p>
|
||||
<a href="{{ path('conference', { id: conference.id }) }}">View</a>
|
||||
<a href="{{ path('conference', { slug: conference.slug }) }}">View</a>
|
||||
</p>
|
||||
{% endfor %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -22,10 +22,10 @@
|
||||
{% endfor %}
|
||||
|
||||
{% 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 %}
|
||||
{% 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 %}
|
||||
{% else %}
|
||||
<div>No comments have been posted yet for this conference.</div>
|
||||
|
||||
Reference in New Issue
Block a user