Johann
Johann

Reputation: 137

Symfony 2 - Generate Slugs with Gedmo\Slug

I have just installed the doctrine extensions to use Sluggable.

I make this :

composer.json

"stof/doctrine-extensions-bundle": "1.2.*@dev"

AppKernel.php

new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(),

app/config/config.yml

stof_doctrine_extensions:
    orm:
        default:
            sluggable: true

Djoo\AppliBundle\Entity\Nomenclature.php

namespace Djoo\AppliBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\DBAL\Types\SmallIntType;
use Gedmo\Mapping\Annotation as Gedmo;

/**
 * Nomenclature
 *
 * 
 * @ORM\Table(name="app_nomenclature")
 * @ORM\Entity
 */
class Nomenclature
{
    .....
    /**
     * @var string
     *
     * @ORM\Column(name="titre", type="string", length=200, nullable=false)
     */
    private $titre;

    /**
     * @var string
     *
     * @ORM\Column(name="finess", type="string", length=10, nullable=true)
     */
    private $finess;

    /**
     * @Gedmo\Slug(fields={"titre","finess"},suffix=".html")
     * @ORM\Column(length=128, unique=true,nullable=true)
     */
    private $slug;

    public function getSlug() {
        return $this->slug;
    }

    public function setSlug($slug){
        $this->slug = $slug;
        return $this;
    }

}

In my controller i make this to generate slug for old values in my datatable :

$filterBuilder = $this->get('doctrine.orm.entity_manager')>getRepository('DjooAppliBundle:Nomenclature')->createQueryBuilder('e')->orderBy('e.titre', 'asc');
$query = $filterBuilder->getQuery();
$nomenclatures = $query->getResult();

foreach($nomenclatures as $nomenclaturee){
    $nomenclature->setSlug(null);
    $this->get('doctrine.orm.entity_manager')->persist($nomenclature);
    $this->get('doctrine.orm.entity_manager')->flush();
 }

I have no error, but my old values are a null slug. I try to create a new element and i have a good slug. Have you and idea ?

Thanks

Upvotes: 7

Views: 10419

Answers (4)

Dimitry K
Dimitry K

Reputation: 2356

Why it didn't work for OP, but worked for others (eg. @gregor):

When creating slug, your first instinct is to create slug property with this column configuration:

 ..    

 @ORM\Column(unique=true, nullable=false)
 private $slug;
 ..

When running app/console doctrine:schema:update and this will result in 2 sql statements:

ALTER TABLE ... ADD slug ... NOT NULL
CREATE UNIQUE INDEX...`. 

By default column slug will be filled with value '' (empty string) which would make 2nd statement to fail with (Duplicate entry '') error. Now you have two choices:

Choice A: Ignore failure of the 2nd statement

If you ignore the error, and later try generating slugs manually using the documented method $entity->setSlug(null) everything would work. It would work because by using $entity->setSlug(null) you would let Doctrine know that propertyslug was changed (from '' to null) and this in turn would trigger internally $uow->propertyChanged() and $uow->scheduleForUpdate() (Thanks to @Sebastian Radu for his example). The Sluggable extension will notice this change as well and will regenerate the slug. Now as all the slugs are unique, next time you run app/console doc:schema:update it will succeed in creating index on slug and your schema will be fully in sync.

Choice B: Modify slug field to be nullable After noticing error your instinct would be to mark slug field as nullable, so that index creation succeeds:

 ..    

 @ORM\Column(unique=true, nullable=true)
 private $slug;
 ..

This would result in slug column having NULL as it's default value. Now as you try using documented $entity->setSlug(null) approach it won't work (just as OP has posted). This happens because when $entity->slug property is already NULL. Thus when you use $entity->setSlug(null) no changes are detected by Doctrine, and thus Sluggable regeneration behaviour is never triggered. In order to trigger the changes there were two answers:

  • hack with adding space to the slug source properties $entity -> setTitre($titre." "); (but this would result in extra space you would have to trim after)

  • approach by @Sebastian Radu, where he shows how to tell Doctrine directly that the field was changed (I personally prefer this one and wonder why it was unfairly downvoted)

Hope this helps you understand a bit better the inner workings of Doctrine and extensions.

Upvotes: 3

gregor
gregor

Reputation: 558

The sluggable documentation states the following:

In case if you want the slug to regenerate itself based on sluggable fields, set the slug to null.

<?php
$entity = $em->find('Entity\Something', $id);
$entity->setSlug(null);

$em->persist($entity);
$em->flush();

It does work for me.

Upvotes: 2

Sebastian Radu
Sebastian Radu

Reputation: 76

$uow = $em->getUnitOfWork();    
$uow->propertyChanged($entity, 'slug', NULL, NULL);    
$uow->scheduleForUpdate($entity);    
$em->flush();

Upvotes: 4

lsouza
lsouza

Reputation: 2488

To change the slug you must change the related property. You can add a space at the end of $titre, save it, change it back and save it again. That will flush the slugs.

Upvotes: 7

Related Questions