We have moved our forum to GitHub Discussions. For questions about Phalcon v3/v4/v5 you can visit here and for Phalcon v6 here.

Overwrite service for terminal script

Hi everybody, I have a service 'translation' like that

$di->setShared(
'translation',
function () use ($config, $di) {
$code = getLanguage();

/*
* Ahora es lo que va a funcionar
*/
$path = APP_DIR . "/lang/{$code}/{$code}.php";
if (!file_exists($path)) {
trigger_error("You must specify a language file for language '$code'");
$path = APP_DIR . '/lang/es/es.php';
}

$messages = [];
include $path;
//codump($messages);
if (!is_array($messages)) {
trigger_error("Translation data [{$path}] for language '$code' must be an array. Got: " . gettype($messages));
}

/*
* Utilizamos nuestra clase hipervitaminada Translate
*/
return new \Colindar\Translate\Translate(['content' => $messages]);
}
);

The problem is that getLanguage use cookies and:

$request = new Phalcon\Http\Request();
$code = $request->getBestLanguage();

to know what is te languajeĀ“s user.

Now in my aplication, the user can select your languaje and I save that in my bbdd.

how can i force this service to use the selection??

I have tried to overwrite the service in Mail.php (library to manage the shipments) like this:

    public function getTemplate($name, $params)
    {
        $parameters = array_merge(array(
            'publicUrl' => $this->config->application->publicUrl
        ), $params);

        $code = getLanguage();

        if (php_sapi_name() === 'cli' //se ejecuta por script
            and isset($params['lang']) //se le ha pasado un idioma de la bbd
            and $params['lang'] !== $code //y ese idioma es distionto al que ya hay cargado... sino para que tanto lio...
        ){
            $code = $params['lang'];
            $this->view->code = $code;

            //sobreescribimos el servicio
            $this->getDI()->remove('translation');

            $this->getDI()->setShared(
                'translation',
                function () use ($code) {
                    /*
                     * Ahora es lo que va a funcionar
                     */
                    $path = APP_DIR . "/lang/{$code}/{$code}.php";
                    if (!file_exists($path)) {
                        trigger_error("You must specify a language file for language '$code'");
                        $path = APP_DIR . '/lang/es/es.php';
                    }

                    $messages = [];
                    include $path;
                    if (!is_array($messages)) {
                        trigger_error("Translation data [{$path}] for language '$code' must be an array. Got: " . gettype($messages));
                    }

                    /*
                     * Utilizamos nuestra clase hipervitaminada Translate
                     */
                    return new \Colindar\Translate\Translate(['content' => $messages]);
                }
            );
        }

        //si ejecutamos por consola, cargamos de nuevo esto
        if (php_sapi_name() === 'cli') {
            $this->view->t    = $this->getDI()->getShared('translation');
            $this->view->code = $code;
        }

        return $this->view->getRender('emailTemplates', $name, $parameters, function ($view) {
            $view->setRenderLevel(View::LEVEL_LAYOUT);
        });

        return $view->getContent();
    }

this library is executed from console with a crontab every minute.

The email always is sent in english, the user want spanish.

Thanks people!!



77.7k
Accepted
answer
edited Oct '18

This is working for me:


$di = new \Phalcon\Di\FactoryDefault;

// Mock service class
class Translate {
    public $code;
    public function __construct($code)
    {
        $this->code = $code;
    }
}

// Mock component class, like a Controller
class Component extends \Phalcon\Mvc\User\Component
{
    public function action()
    {
        unset($this->translate); // components cache services independently of DI,
                                 // this would otherwise always return 'first'

        echo $this->translate->code, PHP_EOL;
    }
}

// define service
$di->set('translate', new Translate('first'));

// create mock component
$component = new Component;
$component->setDI($di);

echo 'Accessing from DI:', PHP_EOL;
echo $di->get('translate')->code, PHP_EOL;
echo 'Accessing from component:', PHP_EOL;
$component->action();

echo PHP_EOL, 'replacing service...', PHP_EOL, PHP_EOL;

$di->remove('translate');
$di->setShared('translate', new Translate('second'));

echo 'Accessing from DI:', PHP_EOL;
echo $di->get('translate')->code, PHP_EOL;
echo 'Accessing from component:', PHP_EOL;
$component->action();

Output:

Accessing from DI:
first
Accessing from component:
first

replacing service...

Accessing from DI:
second
Accessing from component:
second

EDIT: The gotcha that caused me sleepless nights is that if you already accessed a service from a component, it gets cached COMPONENT-WISE. You have to unset it inside the component for it to update properly

The output without the unset($this->translate); line looks like this:

Accessing from DI:
first
Accessing from component:
first

replacing service...

Accessing from DI:
second
Accessing from component:
first


5.6k

Fantastic!!! the headache was right where you said.

You had to do unset () so you would not take the CACHED!

edited Apr '20

This is working for me:


$di = new \Phalcon\Di\FactoryDefault;

// Mock service class
class Translate {
   public $code;
   public function __construct($code)
   {
       $this->code = $code;
   }
}

// Mock component class, like a Controller
class Component extends \Phalcon\Mvc\User\Component
{
   public function action()
   {
       unset($this->translate); // components cache services independently of DI,
                                // this would otherwise always return 'first'

       echo $this->translate->code, PHP_EOL;
   }
}

// define service
$di->set('translate', new Translate('first'));

// create mock component
$component = new Component;
$component->setDI($di);

echo 'Accessing from DI:', PHP_EOL;
echo $di->get('translate')->code, PHP_EOL;
echo 'Accessing from component:', PHP_EOL;
$component->action();

echo PHP_EOL, 'replacing service...', PHP_EOL, PHP_EOL;

$di->remove('translate');
$di->setShared('translate', new Translate('second'));

echo 'Accessing from DI:', PHP_EOL;
echo $di->get('translate')->code, PHP_EOL;
echo 'Accessing from component:', PHP_EOL;
$component->action();

Output:

Accessing from DI:
first
Accessing from component:
first

replacing service...

Accessing from DI:
second
Accessing from component:
second

EDIT: The gotcha that caused me sleepless nights is that epayit if you already accessed a service from a component, it gets cached COMPONENT-WISE. You have to unset it inside the component for it to update properly

The output without the unset($this->translate); line looks like this:

Accessing from DI:
first
Accessing from component:
first

replacing service...

Accessing from DI:
second
Accessing from component:
first

Thanks for the feedback and sharing your experience.