Domanda

I have problem whit slugify routing parameter. I want to replace all intervals and symbols with "-". When parameter is with latin letters all work, but if I try to slugify parameter whit cyrillic letters I get error.

routing:

 catTests:
      url:    /cat/:id/:name_slug
      class:   sfDoctrineRoute
      options: { model: categories, type: object }
      param: { module: categories, action: testsByCat }
      requirements:
        id: \d+

slug functions:

static public function slugify($text)
{
  // replace all non letters or digits by -
  $text = preg_replace('/\W+/', '-', $text);

  // trim and lowercase
  $text = strtolower(trim($text, '-'));

  return $text;
}

public function getNameSlug()
{
  $text= Category::slugify($this->getName());
  return $text;
}

Example: i have two names in databace:

  • english language
  • Български език

Normally whitin function url is :

  • english+language
  • Български+език

When i put function result is :

  • english-language
  • and on cyrillic version parameter is empty.

    Empty module and/or action after parsing the URL "/cat/1/" (/).

È stato utile?

Soluzione

I recommend you to use Doctrine::urlize instead of your own slugify function (since your are using Doctrine).

And then, replace your function like:

public function getNameSlug()
{
  return Doctrine_Inflector::urlize($this->getName());
}

edit:

In fact, it seems that Doctrine doesn't well handle Cyrillic (even in the 2.0). You will have to handle it on your own. I found this function:

  public static function replaceCyrillic ($text)
  {
    $chars = array(
      'ґ'=>'g','ё'=>'e','є'=>'e','ї'=>'i','і'=>'i',
      'а'=>'a', 'б'=>'b', 'в'=>'v',
      'г'=>'g', 'д'=>'d', 'е'=>'e', 'ё'=>'e',
      'ж'=>'zh', 'з'=>'z', 'и'=>'i', 'й'=>'i',
      'к'=>'k', 'л'=>'l', 'м'=>'m', 'н'=>'n',
      'о'=>'o', 'п'=>'p', 'р'=>'r', 'с'=>'s',
      'т'=>'t', 'у'=>'u', 'ф'=>'f', 'х'=>'h',
      'ц'=>'c', 'ч'=>'ch', 'ш'=>'sh', 'щ'=>'sch',
      'ы'=>'y', 'э'=>'e', 'ю'=>'u', 'я'=>'ya', 'é'=>'e', '&'=>'and',
      'ь'=>'', 'ъ' => '',
    );

    return strtr($text, $chars);
  }

And then :

public function getNameSlug()
{
  $slug = Category::replaceCyrillic($this->getName());
  return Doctrine_Inflector::urlize($slug);
}

Altri suggerimenti

You can fix that behaviour globaly all around your project too. Thanks to symfony autoloader which prepends your global directories before the plugin's you can do it this way:

mkdir lib/doctrine
touch lib/doctrine/Inflector_Cyrilic.php
touch lib/doctrine/Sluggable.php
touch test/unit/TransliterationTest.php

lib/doctrine/Inflector_Cyrilic.php:

<?php
class Doctrine_Inflector_Cyrilic extends Doctrine_Inflector
{

    /**
     * @param string $text
     * @param Doctrine_Record $record
     *
     * @return string
     */
    public static function urlizeExtended($text, $record) {
        // we need to use other method name because of PHP strict standards (one more attribute unlike parent so its not compatible)
        // $record attribute is given here standardly, it was only not used before

        //XXX this sollution expect youll have all slugs in Translation records (in I18n in schema.yml)
        // You can alter this conditions how do you need for your project
        // this is important because this should not be used on other writing systems

        if (preg_match('/Translation$/', get_class($record))) {
            if ($record->lang === 'ru') {
                $text = self::cyrilicToLatin($text);
            }
        }
        return parent::urlize($text);
    }

    /**
     * @param string $text
     *
     * @return string
     */
    public static function cyrilicToLatin ($text)
    {
        $chars = array(
            'ґ'=>'g','ё'=>'e','є'=>'e','ї'=>'i','і'=>'i',
            'а'=>'a', 'б'=>'b', 'в'=>'v',
            'г'=>'g', 'д'=>'d', 'е'=>'e', 'ё'=>'e',
            'ж'=>'zh', 'з'=>'z', 'и'=>'i', 'й'=>'i',
            'к'=>'k', 'л'=>'l', 'м'=>'m', 'н'=>'n',
            'о'=>'o', 'п'=>'p', 'р'=>'r', 'с'=>'s',
            'т'=>'t', 'у'=>'u', 'ф'=>'f', 'х'=>'h',
            'ц'=>'c', 'ч'=>'ch', 'ш'=>'sh', 'щ'=>'sch',
            'ы'=>'y', 'э'=>'e', 'ю'=>'u', 'я'=>'ya', 'é'=>'e',
            'ь'=>'', 'ъ' => '',
        );
        return strtr($text, $chars);
    }

}

lib/doctrine/Sluggable.php:

<?php
/**
* we cannot use inheritance here because we are replacing class by otherone with the same name
*/
class Doctrine_Template_Sluggable extends Doctrine_Template
{

    /**
     * Array of Sluggable options
     *
     * @var string
     */
    protected $_options = array(
        'name'          =>  'slug',
        'alias'         =>  NULL,
        'type'          =>  'string',
        'length'        =>  255,
        'unique'        =>  TRUE,
        'options'       =>  array(),
        'fields'        =>  array(),
        'uniqueBy'      =>  array(),
        'uniqueIndex'   =>  TRUE,
        'canUpdate'     =>  FALSE,
        'builder'       =>  array('Doctrine_Inflector_Cyrilic', 'urlizeExtended'),
        'provider'      =>  NULL,
        'indexName'     =>  NULL
    );

    /**
     * Set table definition for Sluggable behavior
     *
     * @return void
     */
    public function setTableDefinition()
    {
        $name = $this->_options['name'];
        if ($this->_options['alias']) {
            $name .= ' as ' . $this->_options['alias'];
        }
        if ($this->_options['indexName'] === NULL) {
            $this->_options['indexName'] = $this->getTable()->getTableName().'_sluggable';
        }
        $this->hasColumn($name, $this->_options['type'], $this->_options['length'], $this->_options['options']);

        if ($this->_options['unique'] == TRUE && $this->_options['uniqueIndex'] == TRUE) {
            $indexFields = array($this->_options['name']);
            $indexFields = array_merge($indexFields, $this->_options['uniqueBy']);
            $this->index($this->_options['indexName'], array('fields' => $indexFields,
                    'type' => 'unique'));
        }

        $this->addListener(new Doctrine_Template_Listener_Sluggable($this->_options));
    }

}

test/unit/TransliterationTest.php:

<?php

// some bootstrapping of your tests

$record = Doctrine_Core::getTable('YourTable')->create(array(
        'record params....'
    ));
$record->Translation['ru']->name = 'холодильник';
$record->save();
$t->ok(preg_match('/^holodilnik/', $record->Translation['ru']->slug), '        RU slug transliterated cyrilic to latin');

Take care if You want to use it in cli tasks, you'll have to preload it manualy there, because of its running environment. sf1.4 cli tasks has its own specific running env and in my projects it does not preloads this classes before Doctrine original ones..

//i have this in my abstract class which is parent of each my cli tasks
require_once(implode(DIRECTORY_SEPARATOR, array(
        __DIR__, '..',
        'Doctrine',
        'Sluggable.php'
    )));
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top