Zend\Mvc\Controller\PluginManager::get was unable to fetch or create an instance for translate - zend-framework2

I have written a controller plugin to get the MVC translator using ZF 2.5.
Here is my translate controller plugin
namespace Freedom\Controller\Plugin;
use Zend\Mvc\Controller\Plugin\AbstractPlugin;
use Zend\I18n\Translator\Translator;
/**
* Translate
*
*/
class Translate extends AbstractPlugin
{
/**
*
* #var Translator
*/
private $translator;
public function __construct(Translator $translator)
{
$this->translator = $translator;
}
/**
* Translate message
* #param string $message
* #param string $textDomain
* #param string $locale
* #return string
*/
public function __invoke($message, $textDomain = 'default', $locale = null)
{
return $this->translator->translate($message, $textDomain, $locale);
}
/**
*
* #return Translator
*/
function getTranslator()
{
return $this->translator;
}
}
and is factory
namespace Freedom\Controller\Plugin\Service;
use Zend\ServiceManager\FactoryInterface;
use Interop\Container\ContainerInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Freedom\Controller\Plugin\Translate;
/**
* TranslateFactory
*
*/
class TranslateFactory implements FactoryInterface
{
public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
{
return new Translate($container->get('translator'));
}
public function createService(ServiceLocatorInterface $container)
{
return $this($container->getServiceLocator(), Translate::class);
}
}
and finally in my module.config
'controller_plugins' => [
'factories' => [
'checkRedirect' => 'Freedom\Controller\Plugin\Service\CheckRedirectFactory',
'translate' => 'Freedom\Controller\Plugin\Service\TranslateFactory',
],
],
The problem I have is that I am getting this error and I can't understand why.
Zend\Mvc\Controller\PluginManager::get was unable to fetch or create an instance for translate
As you can see I have registered the plugin in my module.config but the plugin manager can't find it. I have checked that the controller_plugins key exists in the config and that my namespacing is correct. I also have another plugin called checkRedirect that produces the same error.
I simply can't figure out what is going on, please can someone tell me what I have missed, many thanks.

I finally found the problem, I was calling the plugin from the controllers constructor which does not work. Calling from an action, everything ok.

please try this, may be it help you.
I think all is ok, but you need to update this,
'controller_plugins' => [
'factories' => [
'checkRedirectPlugin' => 'Freedom\Controller\Plugin\Service\CheckRedirectFactory',
'translatePlugin' => 'Freedom\Controller\Plugin\Service\TranslateFactory',
],
'aliases' =>[
'checkRedirect' => 'checkRedirectPlugin',
'translate' => 'translatePlugin'
]
],
now use aliases, you can now access from your controller as,
$this->translate()

Related

How to handle different reference directions in database and ZF2 application?

Zend\Form\Fieldsets and Zend\Form\Collectionss can be nested and provide a very comfortable way to map complex object structures to them, in order to get a comlete object (ready to be saved) from the form input more or less automatically. The Form Collections tutorial provides a very good example.
The case I'm currently having is a bit more complex, since it contains a reference inversion. That means:
I have two entities -- MyA and MyB and while in the database the relationship between them is implemented as FOREIGN KEY from myb.mya_id to mya.id, the application is using an inverted referencing:
MyA has MyB
Or with some code:
namespace My\DataObject;
class MyA {
/**
* #var integer
*/
private $id;
/*
* #var text
*/
private $foo;
/**
* #var MyB
*/
private $myB;
}
namespace My\DataObject;
class MyB {
/**
* #var integer
*/
private $id;
/*
* #var text
*/
private $bar;
/*
Actually it's even bidirectional, but it's not crucial for this issue.
For this problem it's not important,
wheter the class MyB has a property of type MyA.
We get the issue already,
when we define a property of type MyB in the class MyA.
Since the direction of the reference MyA.myB->MyB differes
from the direction of the reference my_b.my_a.id->my_a.id.
*/
/**
* #var MyA
*/
// private $myA;
}
My Mapper objects get DataObjects passed as argument: MyAMapper#save(MyA $object) and MyBMapper#save(MyB $object).
namespace My\Mapper;
use ...
class MyAMapper
{
...
public fuction save(MyA $object)
{
// save the plain MyA propertis a new entry in the my_a table
...
$myMapperB->save($myA->getMyB());
}
}
namespace My\Mapper;
use ...
class MyBMapper
{
...
public fuction save(MyB $object)
{
// save the plain MyB propertis a new entry in the my_b table
...
}
}
That means, the MyAMapper#save(...) has evrything needed to save the MyA object to the my_a table. But in the MyBMapper the data for my_b.my_a_id will be missing.
And I also cannot create a fieldset MyAFieldset with a nested fieldset MyBFieldset and then nest the fieldset MyBFieldset into MyAFieldset in order to fill MyA#MyB#MyA (in order to pass the data for my_b.my_a_id to MyBMapper#save(...)):
class MyAFieldset {
$this->add([
'name' => 'my_b',
'type' => 'My\Form\Fieldset\MyBFieldset',
'options' => []
]);
}
class MyBFieldset {
$this->add([
'name' => 'my_a',
'type' => 'My\Form\Fieldset\MyAFieldset',
'options' => []
]);
}
This would cause a recursive dependency and cannot work.
How to handle a case, when the reference direction on the application level differs from it's direction in the database? How to create though a fieldsets structure, that provides a complete ("ready to be saved") object?
Workaround 1
When the form is processed, a further MyA object can be created and added to the MyB object got from the form:
class MyConrtoller {
...
public function myAction() {
$this->myForm->bind($this->myA);
$request = $this->getRequest();
$this->myForm->setData($request->getPost());
// here the hack #start#
$this->myB->setMyA($this->myA);
// here the hack #stop#
$this->myAService->saveMyA($this->myA);
}
}
Well, maybe not in the controller, the mapper might be a better place for that:
class MyAMapper
{
...
public function save(MyA $myA)
{
$data = [];
$data['foo'] = [$myA->getFoo()];
// common saving stuff #start#
$action = new Insert('my_a');
$action->values($data);
$sql = new Sql($this->dbAdapter);
$statement = $sql->prepareStatementForSqlObject($action);
$result = $statement->execute();
$newId = $result->getGeneratedValue()
// common saving stuff #stop#
...
// hack #start#
if(! $myA->getB->getA()) {
$myA->getB->setA(new MyA());
$myA->getB->getA()->setId($newId);
}
// hack #stop#
// and only after all that we can save the MyB
$myB = $this->myBMapper->save($myB);
$myA->setMyB($myB);
...
}
}
But anyway it's just a hack.
Workaround 2
The MyB class gets a property $myAId. But it's also not a clean way.
Workaround 3
The MyBFieldset gets a MyAFieldsetFake as sub-fieldset. This fieldset class is then just a "shallow" copy of the MyAFieldset, that contains only the ID for the MyA data object:
class MyAFieldset {
...
public function init()
{
$this->add([
'type' => 'text',
'name' => 'id',
'options' => [...],
]);
$this->add([
'type' => 'text',
'name' => 'foo',
'options' => [...],
]);
}
}
class MyAFieldset {
...
public function init()
{
$this->add([
'type' => 'text',
'name' => 'id',
'options' => [...],
]);
$this->add([
'type' => 'text',
'name' => 'bar',
'options' => [...],
]);
$this->add([
'type' => 'text',
'name' => 'foo',
'type' => 'My\Form\Fieldset\MyAFakeFieldset',
'options' => [...],
]);
}
}
class MyAFieldset {
...
public function init()
{
$this->add([
'type' => 'text',
'name' => 'id',
'options' => [...],
]);
}
}
But fake objects are a bit dirty as well.
How about creating a new table to handle the mappings on their own. Then you can isolate that complexity away from the objects that take advantage of them.
So, you could have a new object AtoBMappings
namespace My\DataObject;
class MyA {
/**
* #var integer
*/
private $id;
/*
* #var text
*/
private $foo;
/**
* #var MyAtoB
*/
private $myAtoB;
}
namespace My\DataObject;
class MyB {
/**
* #var integer
*/
private $id;
/**
* #var AtoBMapperID
*/
private $myAtoB;
}
class MyAtoBMapper {
/**
* #var myB
*/
private $myB
/**
* #var myA
**
private $myA
}
Then, instead of hacking your Mapper method, you can simply make an assignment in MyA to MyB creation.
class MyAMapper
{
...
public function save(MyA $myA)
{
$myAtoB = new MyAtoBMapper();
//.... instert new myAtoB into DB
$data = [];
$data['foo'] = [$myA->getFoo()];
$data['myAtoB'] = $myAtoB->getId();
// common saving stuff #start#
$action = new Insert('my_a');
$action->values($data);
$sql = new Sql($this->dbAdapter);
$statement = $sql->prepareStatementForSqlObject($action);
$result = $statement->execute();
$newId = $result->getGeneratedValue();
$myA->setMyAtoB($newAtoB);
$myAtoBMapper->myA = $newId;
// common saving stuff #stop#
// and only after all that we can save the MyB
$myB = $this->myBMapper->save($myB);
$myB->setMyAtoB($newAtoB);
$myAtoBMapper->myB = $myB;
...
}
}
Do you think this would work, or do you think this is too much of a hack?

how to inject class dependency in Yii2 configuration?

I am learning Yii2. Here is one situation I have not googled the answer.
I register a component called scraper in config/console.php's $config['components'] array,
this scraper class has a public property $_client which is a Goutte\Client class.
I tried to use the following way to set up scraper component, but it is not working, Yii2 did not instantiate $_client as a Goutte\Client object.
$config = [
'scraper' => [
'class' => 'app\models\Scraper',
'_pageSize' => 10,
'_client' => [ //not working. can not instantiate this property as an object
'class' => 'Goutte\Client'
],
],
//...
]
Question: What would be working way to inject the dependency in the configuration?
Yii2 will not instantiate objects beyond the first level in your config array. In other words, scraper will get instantiated as an object, but it's property _client will be instantiated as an array ['class' => 'Goutte\Client'].
You should implement this logic yourself:
class Service extends Component
{
private $_client = null;
public $clientClass;
public function getClient()
{
if (null !== $this->_client) {
return $this->_client;
}
$this->_client = new $clientClass;
return $this->_client;
}
}
Alternatively, you can register Goutte\Client as a separate component, then Yii will properly instantiate it.
UPDATE:
To clarify, instantiating objects from config is done with yii\base\Configurable interface which is implemented in yii\base\Object class. Eventually, this implementation executes Yii::configure:
public static function configure($object, $properties)
{
foreach ($properties as $name => $value) {
$object->$name = $value;
}
return $object;
}
As you see, all properties will be assigned their respective values, so _client will become an array, not an object.
Found another approach in the guide itself: The property targets of the class yii\log\Dispatcher can be initialized with a class names or an objects. To make it working as one expects the init method is overwritten:
/**
* {#inheritdoc}
*/
public function init()
{
parent::init();
foreach ($this->targets as $name => $target) {
if (!$target instanceof Target) {
$this->targets[$name] = Yii::createObject($target);
}
}
}
This allows configuration/initialization of the log component like this:
'log' => [
'class' => 'yii\log\Dispatcher',
'targets' => [
[
'class' => 'yii\log\FileTarget',
],
],
],
Note: targets is an array here. But it can be done with a single class/object as well.
So in your case this should be a solution:
namespace app\models;
class Scraper extends ActiveRecord // or extends from anything that actually implements yii\base\Configurable
{
public $_client;
/**
* {#inheritdoc}
*/
public function init()
{
parent::init();
if (!$this->_client instanceof Goutte\Client) {
$this->_client = Yii::createObject($this->_client);
}
}
}
btw: usually underscore prefix in variable names is used for private properties.

ZF2 service manager use from a custom class

It looks like it has been touched several times already, but i still can't get it work. I set up an JSON-RPC server in a separate module, it works fine. Its functionality is in a new class Rpcapi. Now I want reuse DB related functions that already implemented in another module from that class. According to ZF2 docs my Rpcapi class has to be ServiceLocator-aware and it looks like I made it that way. Unfortunatelly still can't get it working. Please help keeping in mind that I'm new with ZF2 :)
Rpccontroller.php
namespace Rpc\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\Json\Server\Server;
use Zend\Json\Server\Smd;
use Rpc\Model\Rpcapi;
class RpcController extends AbstractActionController
{
public function indexAction()
{
header('Content-Type: application/json');
$jsonrpc = new Server();
$jsonrpc->setClass(new Rpcapi);
$jsonrpc->getRequest()->setVersion(Server::VERSION_2);
if ($this->getRequest()->getMethod() == "GET") {
$smd = $jsonrpc->getServiceMap()->setEnvelope(Smd::ENV_JSONRPC_2);
echo $smd;
} else {
$jsonrpc->handle();
}
}
}
module.config.php for Rpc module
'service_manager' => array(
'invokables' => array(
'rpcapi' => 'Search\Model\SiteTable',
),
),
Rpcapi.php
namespace Rpc\Model;
use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class Rpcapi implements ServiceLocatorAwareInterface
{
protected $services;
protected $siteTable;
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->services = $serviceLocator;
}
public function getServiceLocator()
{
return $this->services;
}
public function getSiteTable()
{
if (!$this->siteTable) {
$sm = $this->getServiceLocator();
$this->siteTable = $sm->get('rpcapi');
}
return $this->siteTable;
}
/**
* Returns list of all sites
*
*
* #return array
*/
public function getAllSites()
{
$results = $this->getSiteTable()->fetchAll();
$r = array ('1' => '1', '2' => 2); //Just to return something for now
return $r;
}
}
All I could get out is: Fatal error: Call to a member function get() on a non-object in /var/www/html/AmeriFluxZF2/module/Rpc/src/Rpc/Model/Rpcapi.php on line 28. Line 28 is:
$this->siteTable = $sm->get('rpcapi');
Any help is much appreciated!
Making the class service locator aware tells the ZF2 that the service locator should be injected into your class upon instantiation. However, you still need to use the service locator to instantiate this class, rather than creating an instance of it yourself, or this will never happen.
Your probably want to add a new entry to invokables for your Rpcapi class, and then grab this from the service locator instead of doing new Rpcapi in your controller.
PS: The naming of your classes is very confusing - you have an Rpcapi class, and an invokable called rpcapi, yet this invokable creates an instance of a completely different class?
If you want serviceLocator to be injected by the service manager in your Rpcapi, you must get it via the service manager itself :
'service_manager' => array(
'invokables' => array(
'rpcapi' => 'Search\Model\SiteTable',
'Rpc\Model\Rpcapi' => 'Rpc\Model\Rpcapi',
),
),
the action :
public function indexAction()
{
header('Content-Type: application/json');
$jsonrpc = new Server();
$jsonrpc->setClass($this->getServiceLocator()->get('Rpc\Model\Rpcapi'));
$jsonrpc->getRequest()->setVersion(Server::VERSION_2);
if ($this->getRequest()->getMethod() == "GET") {
$smd = $jsonrpc->getServiceMap()->setEnvelope(Smd::ENV_JSONRPC_2);
echo $smd;
} else {
$jsonrpc->handle();
}
}
And this is where you can see that your 'rcpai' name for SiteTable is not a good choice... ;)

ZF2 SM, Do I really need to

I currently have a ZF2 Module which needs access to three database tables. No other module needs access to these tables.
So my question is (performance in mind too, please) should I really add the factories to the Module.php like I have been doing:
/**
* Service Configuration
*
* #return array
*/
public function getServiceConfig()
{
return array(
'invokables' => array(
'Login\Service\Authenticate' => 'Login\Service\Authenticate',
'Login\Service\Oauth' => 'Login\Service\Oauth'
),
'factories' => array(
'Login\Form\Login' => function () {
$form = new Form\Login();
$form->setInputFilter(new FormFilter\Login());
return $form;
},
'Login\Model\GaclEmployeePermission' => function ($sm) {
return new Model\GaclEmployeePermission($sm->get('OTWebsoft\Db\Adapter\Master'), $sm->get('OTWebsoft\Db\Adapter\Slave'));
},
'Login\Model\Passport' => function ($sm) {
return new Model\Passport($sm->get('OTWebsoft\Db\Adapter\Master'), $sm->get('OTWebsoft\Db\Adapter\Slave'));
},
'Login\Model\PassportLog' => function ($sm) {
return new Model\PassportLog($sm->get('OTWebsoft\Db\Adapter\Master'), $sm->get('OTWebsoft\Db\Adapter\Slave'));
}
)
);
}
Then I have an abstract class which does:
/**
* Table GaclEmployeePermission
*
* #return \Login\Model\GaclEmployeePermission
*/
protected function getTableGaclEmployeePermission()
{
return $this->getServiceManager()->get('Login\Model\GaclEmployeePermission');
}
OR should I just remove the configuration from the Module.php and in my abstract class just do this:
/**
* Table GaclEmployeePermission
*
* #return \Login\Model\GaclEmployeePermission
*/
protected function getTableGaclEmployeePermission()
{
return new GaclEmployeePermission($this->getMasterAdapter(), $this->getSlaveAdapter());
}
Both seems to work exactly the same. But when it comes to performance, which would you recommend? Keeping in mind, these three tables do not need to be accessed from any other module but this one.
The main reason to use a service manager or dependency injection container is not that other services could access it. The main reason is to apply dependency injection and thereby you apply inversion of control. This ease the usage of your objects, allow you to swap implementations easily, enhance the ability to test the class and to keep the class responsible about the contained logic and not bothering its dependencies.
In both examples, you actually use the service manager but it's still the same pattern: the class (inside getTableGaclEmployeePermission()) decides what to get. Either via the service manager or via direct instantiation, that does not actually matter much.
By your module configuration I cannot grasp the layers you have in the application and what they do (esp. what's about that abstract class), but in general, you should inject it's dependencies:
abstract class SomeAbstract
{
protected $permission;
public function __construct(Permission $permission)
{
$this->permission = $permission;
}
}
class Permission
{
protected $master;
protected $slave;
public function __construct(TableAdapter $master, TableAdapter $slave = null)
{
$this->master = $master;
if (null !== $slave) {
$this->slave = $slave;
}
}
}
Then you create the service manager configuration to inject those dependencies for the services you define:
'factories' => array(
'Login\Model\Permission' => function ($sl) {
$master = $sl->get('My\MasterAdapter');
$slave = null;
if ($some_flag) {
$slave = $sl->get('My\SlaveAdapter');
}
return new Login\Model\Permission($master, $slave);
},
'Login\Some\ConcreteImplementation' => function ($sl) {
$permission = $sl->get('Login\Model\Permission');
return new Login\Some\ConcreteImplementation($permission);
}
),
Then you request Login\Some\ConcreteImplementation and all the injection ($master, $slave and $permission) is done for you, enabling all the benefits described above.

Symfony cyrillic routing slug

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/" (/).
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);
}
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'
)));

Resources