Skip to content

Commit

Permalink
bug symfony#1062 Make blog post titles unique (javiereguiluz, Javier …
Browse files Browse the repository at this point in the history
…Eguiluz)

This PR was merged into the master branch.

Discussion
----------

Make blog post titles unique

I'm trying to fix symfony#1045.

We should make the `slug` unique because it's what we use to look for blog posts in the database. However, I can't make this work. If I define `UniqueEntity` on `title`, it works ... but if I define it on `slug`, it doesn't work (I can create any number of posts with the same slug). Anybody sees the error? Thanks!

Commits
-------

178e2b1 Added a functional test
7486dc8 Added a form event to set the slug
a4a316a Make blog post titles unique
  • Loading branch information
javiereguiluz committed Jan 15, 2020
2 parents e01aebb + 178e2b1 commit 17bf17b
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 6 deletions.
8 changes: 2 additions & 6 deletions src/Controller/Admin/BlogController.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\String\Slugger\SluggerInterface;

/**
* Controller used to manage blog contents in the backend.
Expand Down Expand Up @@ -70,7 +69,7 @@ public function index(PostRepository $posts): Response
* to constraint the HTTP methods each controller responds to (by default
* it responds to all methods).
*/
public function new(Request $request, SluggerInterface $slugger): Response
public function new(Request $request): Response
{
$post = new Post();
$post->setAuthor($this->getUser());
Expand All @@ -86,8 +85,6 @@ public function new(Request $request, SluggerInterface $slugger): Response
// However, we explicitly add it to improve code readability.
// See https://symfony.com/doc/current/forms.html#processing-forms
if ($form->isSubmitted() && $form->isValid()) {
$post->setSlug($slugger->slug($post->getTitle())->lower());

$em = $this->getDoctrine()->getManager();
$em->persist($post);
$em->flush();
Expand Down Expand Up @@ -133,13 +130,12 @@ public function show(Post $post): Response
* @Route("/{id<\d+>}/edit", methods="GET|POST", name="admin_post_edit")
* @IsGranted("edit", subject="post", message="Posts can only be edited by their authors.")
*/
public function edit(Request $request, Post $post, SluggerInterface $slugger): Response
public function edit(Request $request, Post $post): Response
{
$form = $this->createForm(PostType::class, $post);
$form->handleRequest($request);

if ($form->isSubmitted() && $form->isValid()) {
$post->setSlug($slugger->slug($post->getTitle())->lower());
$this->getDoctrine()->getManager()->flush();

$this->addFlash('success', 'post.updated_successfully');
Expand Down
2 changes: 2 additions & 0 deletions src/Entity/Post.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Validator\Constraints as Assert;

/**
* @ORM\Entity(repositoryClass="App\Repository\PostRepository")
* @ORM\Table(name="symfony_demo_post")
* @UniqueEntity(fields={"slug"}, errorPath="title", message="This title was already used in another blog post, but they must be unique.")
*
* Defines the properties of the Post entity to represent the blog posts.
*
Expand Down
21 changes: 21 additions & 0 deletions src/Form/PostType.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\String\Slugger\SluggerInterface;

/**
* Defines the form used to create and manipulate blog posts.
Expand All @@ -28,6 +31,14 @@
*/
class PostType extends AbstractType
{
private $slugger;

// Form types are services, so you can inject other services in them if needed
public function __construct(SluggerInterface $slugger)
{
$this->slugger = $slugger;
}

/**
* {@inheritdoc}
*/
Expand Down Expand Up @@ -64,6 +75,16 @@ public function buildForm(FormBuilderInterface $builder, array $options): void
'label' => 'label.tags',
'required' => false,
])
// form events let you modify information or fields at different steps
// of the form handling process.
// See https://symfony.com/doc/current/form/events.html
->addEventListener(FormEvents::SUBMIT, function (FormEvent $event) {
/** @var Post */
$post = $event->getData();
if (null !== $postTitle = $post->getTitle()) {
$post->setSlug($this->slugger->slug($postTitle)->lower());
}
})
;
}

Expand Down
25 changes: 25 additions & 0 deletions tests/Controller/Admin/BlogControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,31 @@ public function testAdminNewPost()
$this->assertSame($postContent, $post->getContent());
}

public function testAdminNewDuplicatedPost()
{
$postTitle = 'Blog Post Title '.mt_rand();
$postSummary = $this->generateRandomString(255);
$postContent = $this->generateRandomString(1024);

$client = static::createClient([], [
'PHP_AUTH_USER' => 'jane_admin',
'PHP_AUTH_PW' => 'kitten',
]);
$crawler = $client->request('GET', '/en/admin/post/new');
$form = $crawler->selectButton('Create post')->form([
'post[title]' => $postTitle,
'post[summary]' => $postSummary,
'post[content]' => $postContent,
]);
$client->submit($form);

// post titles must be unique, so trying to create the same post twice should result in an error
$client->submit($form);

$this->assertSelectorTextSame('form .form-group.has-error label', 'Title');
$this->assertSelectorTextContains('form .form-group.has-error .help-block', 'This title was already used in another blog post, but they must be unique.');
}

public function testAdminShowPost()
{
$client = static::createClient([], [
Expand Down

0 comments on commit 17bf17b

Please sign in to comment.