How to use Yii::$app->language - internationalization

I'm hard to know what is the ideal place to use the **\Yii::$app->language = 'pt';**
I tried in main.php view, but only the menu got the translation. In the tutorial-i18N says:
You may set the application language at runtime to the language that
the user has chosen. This has to be done at a point before any output
is generated so that it affects all the output correctly. Therefor
just change the application property to the desired value
My intention is to store the desired language in a LANGUAGE field in the user profile (along with FULL_NAME, etc.).
In the code, I need to know the correct location and how to use the same.
EDIT
#Timothée Planchais, this way works:
class SiteController extends Controller
{
public function init()
{
parent::init();
if(!Yii::$app->user->isGuest) {
Yii::$app->language = Yii::$app->user->identity->profile->language;
}
}
But work only in SiteController

To set the application language, edit the file config/web.php :
$config = [
'id' => 'myapp',
'name' => My App',
'basePath' => dirname(__DIR__),
'bootstrap' => ['log'],
'language' => 'pt',//HERE
...
]
You can do all in a custom Controller class which should be extended by all your controllers. In the init() function :
namespace app\components;
class Controller extends yii\web\Controller
{
public function init()
{
parent::init();
if(!Yii::$app->user->isGuest) {
Yii::$app->user->getIdentity()->language = Yii::$app->language;
}
}
}
SiteController for example :
class SiteController extends app\components\Controller
{
...
}

Related

How to mock internal calls with mockery

I try to mock a method of my service with Mockery lib. It works if I call that method from the test's context. But if I call it from another method (for example, it calls from another tested method) - it returns original data from implementation, but not from mock. What I'm doing wrong?
The example is below.
I added contract's because of my real implementation uses it. I don't think the problem is related to interfaces.
app/Contracts/TransactionsServiceContract.php
namespace App\Contracts;
interface TransactionsServiceContract
{
public function getAllRequests(): array;
public function getRequests(array $necessaryFields): array;
}
app/Services/TransactionsService.php
namespace App\Services;
use App\Contracts\TransactionsServiceContract;
class TransactionsService implements TransactionsServiceContract
{
public function getAllRequests(): array
{
return [
'foo' => [
'metric' => 'foo',
],
'bar' => [
'metric' => 'bar',
],
'another' => [
'metric' => [
// Some fields
],
],
];
}
public function getRequests(array $necessaryFields): array
{
// dd($this->getAllRequests()); // -> for the test context it returns original value (above's one)
return collect($this->getAllRequests())->only($necessaryFields)
->map(function (array $metric) {
return $metric['formula'];
})
->toArray();
}
}
tests/Feature/TransactionsServiceTest.php
namespace Tests\Feature;
use App\Contracts\TransactionsServiceContract;
use Tests\TestCase;
class TransactionsServiceTest extends TestCase
{
/** #var TransactionsServiceContract */
private $_transactionsService;
public function setUp()
{
parent::setUp();
$requests = [
'test1' => [
'metric' => 'test 1',
],
'test2' => [
'metric' => 'test 2',
],
];
$this->_transactionsService = \Mockery::mock(app()->make(TransactionsServiceContract::class))->makePartial();
$this->_transactionsService->shouldReceive('getAllRequests')->andReturn($requests);
}
public function testInternalCall()
{
$directCall = $this->_transactionsService->getAllRequests(); // returns array "requests" from the setUp method
dump($directCall);
$internalCall = $this->_transactionsService->getRequests(['test1']);
dd($internalCall); // if we call getAllRequests into getRequests, but not from test's context, we get original array from real implementation, but not test's mock
}
}
Versions of libs/frameworks:
Laravel: v5.7.19
PHPUnit: 7.5.1
Mockery: 1.2.0
Thanks for attention. Happy new year! :)
When you call \Mockery::mock(app()->make(TransactionsServiceContract::class))->makePartial(); in your setUp method, you're not really replacing the implementation existing in the app container. Laravel's container provides you with the bind method, to do that (the documentation for that). Besides you wouldn't replace an interface with a mock, as interfaces don't do anything per definition.
So in fact you would do something like:
app()->bind('\App\TransactionsService', $mockedTransactionService);
Note this will only work if your code gets an instance of the TransactionService by injection or resolving, not by calling new TransactionService.

How do I configure a custom form element with zend expressive

In my ZF2 Application, I had several custom form Elements with an injected database Adapter. I put the configuration in the module.php file with a method, like this:
public function getFormElementConfig()
{
return array(
'factories' => [
'dksCodeElementSelect' => function($container)
{
$db = $container->get(AdapterInterface::class);
$elemnt = new Form\Element\Select\DksCodeElementSelect();
$elemnt->setDb($db);
return $elemnt;
},
)
}
How can I configure custom form elements within a zend-expressive application?
Form class calls elements via 'FormElementManger'. and FormElementManager reads 'form_elements' key from config. It's basicly a container (service manager) so you have to configure it same as container (factories, invokables, aliases etc). Also, you have to register Zend/Form module to container. I didn't try it but has to be ths way;
(ps: it's too late here, if it doesn't work let me know so i can put a working example.)
class MyModule\ConfigProvider {
public function __invoke()
{
return [
'dependencies' => $this->getDependencies(),
'templates' => $this->getTemplates(),
'form_elements => [
/* this is where you will put configuration */
'factories' => [
MyElement::class => MyElementFactory::class,
]
]
];
}
}

ZF2 get global session container

I found how to get a session container like this:
$session = new \Zend\Session\Container('base');
But what if I need to access the session in many places during processing a HTTP request.
Let's say in the Application module's indexAction in the IndexController, then I redirect it to the User\Controller\IndexController and need to access the session again, and then in a view helper or two, and who knows how often more.
When constructing the session container every time anew, that is a waste of processing time. Yes, I debugged it to see what's going on in the constructor, and yes, there is some code executed behind the scenes. It is not as if the constructor would just return a global variable or something else which would be immutable and doesn't need a construction process.
So what to do?
Should I create a service for it?
a controller plugin?
a view helper?
a service and a controller plugin and a view helper, with the latter calling the service?
I'm sure it is something that many people must have come across and have dealt with, but I can't find any information on this.
Any hint is dearly appreciated.
Many thanks in advance! :-)
Here's a more refined and improved version.
It consists of the service "SessionService", a ViewHelper (which calls the SessionService), a ControllerPlugin (which also calls the SessionService), and shows how to set them up in the configuration file "module.config.php".
Make sure you set "use" paths or use absolute class paths in config.
SessionService.php:
class SessionService
{
protected $sessionContainer;
public function setSessionContainer(
$sessionContainer
) {
$this->sessionContainer = $sessionContainer;
}
public function __invoke() {
return $this->sessionContainer;
}
}
SessionHelper.php:
class SessionHelper extends \Zend\View\Helper\AbstractHelper
{
protected $sessionService;
public function setSessionService(
$sessionService
) {
$this->sessionService = $sessionService;
}
public function __invoke() {
return $this->sessionService;
}
}
SessionPlugin.php:
class SessionPlugin extends AbstractPlugin
{
protected $sessionService;
public function setSessionService(
$sessionService
) {
$this->sessionService = $sessionService;
}
public function __invoke() {
return $this->sessionService;
}
}
module.config.php:
'service_manager' => array(
'factories' => array(
'sessionService' => function(
ServiceLocatorInterface $serviceLocator
) {
$sessionContainer = new \Zend\Session\Container('base');
$sessionService = new SessionService();
$sessionService->setSessionContainer($sessionContainer);
return $sessionService;
},
),
),
'controller_plugins' => array(
'factories' => array(
'sessionPlugin' => function(
AbstractPluginManager $pluginManager
) {
$sessionService = $pluginManager->getServiceLocator()->get('sessionService');
$sessionPlugin = new SessionPlugin();
$sessionPlugin->setSessionService($sessionService);
return $sessionPlugin;
},
),
),
'view_helpers' => array(
'factories' => array(
'sessionHelper' => function (
AbstractPluginManager $helperPluginManager
) {
$sessionService = $helperPluginManager->getServiceLocator()->get('sessionService');
$sessionHelper = new SessionHelper();
$sessionHelper->setSessionService($sessionService);
return $sessionHelper;
},
),
),
In your Controller write:-
use Zend\Session\Container;
Make Session variable
$user_session = new Container('user');
'user' is Your Session Name To put Value in Your Session write:
$user_session->username = 'xyz';
After Storing You can Access Your Session By:
$user_session-> username
To destroy Session Variable Use:
$session = new Container('user');
$session->getManager()->getStorage()->clear('user');
it is Just Like : -
unset($_SESSION['user']);
http://wownewcode.blogspot.in/2013/12/set-session-in-zend-framework-2.html
Once you've initialized a session Container you can just re-use it with $_SESSION['container_name'];
Basically $session = new \Zend\Session\Container('base'); will create an ArrayObject named base inside $_SESSION. One advantage of initializing by creating a Container is that you can specify a TTL or expiration after x-hops.
$_SESSION['base']['key'] = "store this value";
I think you need to use session Manager and service with the container storage to achieve your goal.
you can set it in your application Module
Application\Module.php
use Zend\Session\Config\SessionConfig;
public function onBootstrap(EventInterface $e)
{
//Your other code here
//configure session
$sessionConfig = new SessionConfig();
$sessionConfig->setOptions($config['session']);
}
and in module.config.php
'session' => array(
'save_path' => realpath(ZF2PROJECT_ROOT . '/data/session'),
'name' => 'ZF2PROJECT_SESSION',
),
and in your services you can use like this. Forexample in Authentication service.
class AuthenticationService
{
protected $storage = null;
public function getStorage()
{
if (null === $this->storage) {
$this->setStorage(new Storage\Session());
}
return $this->storage;
}
}
Here is my current provisional solution or workaround, consisting of:
- a service for storing the session container object.
- a controller plugin for easy access to the session container, without having to inject a dependency for it in every controller.
The session service:
class Session
{
private static $container;
public function getContainer() {
if (!isset(self::$container)) {
self::$container = new \Zend\Session\Container('base');
}
return self::$container;
}
public function __invoke() {
return $this->getContainer();
}
}
The controller plugin:
class Session extends AbstractPlugin
{
protected $sessionService;
public function __construct(
SessionService $sessionService
) {
$this->sessionService = $sessionService;
}
public function getContainer() {
return $this->sessionService->getContainer();
}
public function __invoke() {
return $this->getContainer();
}
}
Configuration in module.config.php:
'service_manager' => array(
'factories' => array(
'sessionService' => function($sm) {
return new Application\Service\Session\Session();
},
),
),
'controller_plugins' => array(
'factories' => array(
'session' => function($serviceLocator) {
$sessionService = $serviceLocator->get('sessionService');
return new Application\Service\Mvc\Controller\Plugin\Session($sessionService);
},
),
),
Usage example in any controller or controller plugin method:
$sessionContainer = $this->session->getContainer();
or short form (because session service and controller plugin both implement __invoke):
$sessionContainer = $this->session();
and then can use the session container to store any variables in it, like this:
$sessionContainer->foo = 'bar';
Because the session service is created by a factory function through module.config.php, it is only created once.
The actual session container is a static variable in the session service and only created once, i.e. if it doesn't exist.
In subsequent calls to the getSessionContainer function, this only once created static session container is returned.
This is just a provisional workaround solution, works for me for now, but for making it re-usable for other applications also, it should provide functions to customize the session container name and the storage place and strategy, those parts are missing in this simple workaround solution.
Note: A view helper should not be necessary for it. Session variables should not be set in a view, and if a view needs read access to them, the data should be passed via a view model from controller to view.

How to validate a file upload in Symfony2 (ImageValidator)?

I am currently developing an application which uses the newest version of Symfony2. I have some problems validating a form which includes a file upload field.
My form code looks like that (shortened):
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Validator\Constraints\MinLength;
use Symfony\Component\Validator\Constraints\Collection;
class EventEditForm extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add("name", "text", array("required" => true));
//....
$builder->add("image", "file", array("property_path" => false, "required" => false));
}
public function getDefaultOptions(array $options)
{
$collectionConstraint = new \Symfony\Component\Validator\Constraints\Collection(array(
'image' => new \Symfony\Component\Validator\Constraints\Image()
));
$options["validation_constraint"] = $collectionConstraint;
return $options;
}
public function getName()
{
return 'editeventform';
}
}
As you can see here I want to ensure that the uploaded file is an image. I use the form like this:
$form = $this->createForm(new \Trancefans\AdminBundle\Form\EventEditForm(), $event);
But when running this code I get the following error:
Expected argument of type array or Traversable and ArrayAccess, object
given
I really don't know where my fault is. I did it exactly as described in the documentation. Can somebody help me? :-)
BTW: The image is not represented by the event entitiy, but every other field of the form is. I use doctrine.
Symfony\Component\Validator\Constraints\CollectionValidator requires that the data it's validating is an array or an object that implements ArrayAccess and Traversable.
Ensure that your class definition for the $event instance implements ArrayAccess and Traversable so when CollectionValidator::validate() is invoked it can access your values to validate them accordingly.

Include a component in CakePHP 2

I'm trying to working with Security component in a Controller of CakePHP 2.0 but I'm doing something wrong with it.
I've read in the documentation, but when I include the Security component in my Controller and I call the controller action register I get a blank page. If I comment the include public $components = array('Security'); it works again, where I'm wrong?
<?php
App::uses('CakeEmail', 'Network/Email');
class UsersController extends AppController {
public $components = array('Security');
public function register () {
if (!empty($this->data)) {
if ($this->data['User']['password'] == $this->data['User']['confirm_password']) {
$this->User->create();
$this->User->save($this->data);
$this->registrationEmail ($this->data['User']['email'], $this->data['User']['username']);
$this->redirect(array('controller'=>'users', 'action'=>'registration', 'success'));
}
}
}
private function registrationEmail ($account_email, $username) {
$email = new CakeEmail('myconfig');
$email->from(array('mailer#email.com' => 'MySite.com'));
$email->to($account_email);
$email->subject('Account activation / MySite.com');
$email->template('activation');
// $this->set('activation_code', Security->hash($account_email));
$email->viewVars(
array(
'activation_code' => $this->Security->hash($account_email),
'username' => $username
)
);
$email->send();
}
?>
How is the registration form rendered in your view file?
If you're using the Security Component, all form fields need to be generated using the FormHelper. In addition you can't fiddle with hidden values on the front end once they've been given a value by the FormHelper.

Resources