Symfony 2 - Generate Slugs with Gedmo\Slug
Asked Answered
B

4

7

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

Boxcar answered 17/10, 2014 at 13:39 Comment(1)
I think slug is only generated for newly persisted objects.Sling
T
7

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.

Taffrail answered 17/10, 2014 at 14:4 Comment(2)
Yeaaaa, thank you $entity = $em->getRepository($entityName)->find($id); $titre = $entity->getTitre(); $entity -> setSlug(null); $entity -> setTitre($titre." "); $em -> flush();Boxcar
It did not work for me. I had to re-generate the slug manually according to https://mcmap.net/q/1477402/-generating-doctrine-slugs-manuallyRepine
M
4
$uow = $em->getUnitOfWork();    
$uow->propertyChanged($entity, 'slug', NULL, NULL);    
$uow->scheduleForUpdate($entity);    
$em->flush();
Maltase answered 5/8, 2016 at 14:14 Comment(1)
This works for me and I wonder why did it have -1 ????. I just added +1. Also keep in mind that you may want to typehint ObjectManager to EntityManager so that you can get code completion for getUnitOfWork() : /** @var ObjectManager|EntityManager $em */Elaineelam
E
3

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.

Elaineelam answered 30/3, 2017 at 17:31 Comment(0)
O
2

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.

Onionskin answered 18/10, 2016 at 13:41 Comment(1)
doesn't work for me. Field annotations are like this @ORM\Column(name="slug", unique=true, nullable=true) @Gedmo\Slug(fields={"name","city"}, updatable=true)Elaineelam

© 2022 - 2024 — McMap. All rights reserved.