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
|
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));
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
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>
|
<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/>
|
||||||
|
|||||||
@@ -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 %}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user