Zend Framework 2 - Translation and text domain in views - php

I'm currently working on translating an existing Zend Framework 2 project that is spread around multiple modules.
My understanding of the translate functionality of ZF2, is that you can have as many translation files, providing each are 'namespaced' to a different text_domain. This works fine in practice, with each module having the following in their module.config.php file:
...
'translator' => array (
'locale' => 'en_US',
'translation_file_patterns' => array (
array (
'type' => 'phparray',
'base_dir' => __DIR__ . '/../language',
'pattern' => '%s_default.php',
'text_domain' => 'ExampleModule'
),
),
),
...
Which adds a ../language/*_default.php file to the translation list with a text_domain of ExampleModule. All good so far.
Now, the translator itself needs to know which text_domain to pick a translation from and will use default if one isn't provided.
So, inside and at the top of all of my view *.phtml files, I have:
$this->plugin('translate')->setTranslatorTextDomain('ExampleModule');
$this->formLabel()->setTranslatorTextDomain('ExampleModule');
$this->formText()->setTranslatorTextDomain('ExampleModule');
Which tells all proceeding $this->translate() blocks and form elements which text_domain to use.
This is great, and works fine, but it doesn't sit well with the DRY principle in that I have similar code at the top of every view. I attempted to extend the ViewModel class so I can pick a different ViewModel class in the controller and have the above code already baked in, but the plugins aren't available at that stage.
How would I include the above code on every/most views without having to type it each time?

After searching endlessly, I found that the default renderer - PhpRenderer - can be accessed via the onBootstrap method of Module.php (reference).
As the view scripts are rendered by PhpRenderer the $this variable points to PhpRenderer (reference). This means that you can attach the code I needed to Module.php as below:
// Get the default ViewRenderer (PhpRenderer) and setup the correct text domain for derivative plugins
$viewRenderer = $e->getApplication()->getServiceManager()->get('ViewRenderer');
$viewRenderer->plugin('translate')->setTranslatorTextDomain('ExampleModule');
$viewRenderer->formLabel()->setTranslatorTextDomain('ExampleModule');
$viewRenderer->formText()->setTranslatorTextDomain('ExampleModule');
As the current namespace matches the text_domain I need, the above can be simplified by swapping 'ExampleModule' with __NAMESPACE__.
EDIT: If you're looking for a different text_domain per module; you'll need in just one Module.php:
$viewRenderer = $e->getApplication()->getServiceManager()->get('ViewRenderer');
$eventManager->getSharedManager()->attach('Zend\Mvc\Controller\AbstractActionController', 'dispatch', function($e) use ($viewRenderer) {
$controller = $e->getTarget();
$controllerClass = get_class($controller);
$moduleNamespace = substr($controllerClass, 0, strpos($controllerClass, '\\'));
$viewRenderer->plugin('translate')->setTranslatorTextDomain($moduleNamespace);
$viewRenderer->formLabel()->setTranslatorTextDomain($moduleNamespace);
$viewRenderer->formText()->setTranslatorTextDomain($moduleNamespace);
}, 100);

Related

what i need for invoke various actions and view in my layour main

well i trying build a website on ZendFrameWork 2 i want execute various actions in my layout main like a fild of search, or a form for loguin etc. each one of this i believe should are in views diferents with your actions, how i do for references this actions with your views that will return this element for i put where i want in my layout. like the helper $this->content; where it return the context view and action no just the view with no function, which are the best way for i do this or a way alternative
I think what you need are view partials. You can read on view partials in the ZF2 official documentation.
You have to create your partials and then you register them under your template_map inside the view_manager config.
'view_manager' => array(
//...
'template_map' => array(
'my/form' => __DIR__ . '/../view/partials/form.phtml',
'my/partial' => __DIR__ . '/../view/partials/partial.phtml',
)
),
Then in every layout you can simply embed them like this:
echo $this->partial('my/form');
echo $this->partial('my/partial');
If you need to pass variables to your partials you can pass a second argument:
echo $this->partial('my/form', array('foo' => 'bar'));

Phalcon Router and Loader for subfolder structure getting bigger. How to set up?

I have a quite big project that I need to program. I used to code with CodeIgniter but since they stopped maintaining the framework, I decided to switch to another. I chose Phalcon framework. The folder structure of the application that I want to implement is next:
app/
controllers/
admin/
users/
UsersController.php
UserGroupsController.php
dashboard/
system/
another_subfolder/
AnotherSubFolderController.php
production/
settings/
SettingsController.php
dashboard/
flow/
another_subfolder/
AnotherSubFolderController.php
website1/
customers/
CustomersController.php
CompaniesController.php
another_subfolder .... /
models/
sub_folder_structure/ // The same as controllers
This is just example of the folder structure of the application. There will be quite a lot of folders and sub-folders in this project to make it manageable.
From my understanding I need to register all namespaces at the loader for each sub-folder, so that Phalcon knows where to search and load the class.
//Register an autoloader
$loader = new \Phalcon\Loader();
$loader->registerNamespaces(array(
// Main
'Controllers' =>'app/controllers/',
'Models' =>'app/models/',
// Admin Routing
'Controllers\Admin'=>'app/controllers/admin',
'Models\Admin'=>'app/models/admin',
'Controllers\Admin'=>'app/controllers/admin',
'Models\Admin'=>'app/models/admin',
'Controllers\Admin\Users'=>'app/controllers/admin/users',
'Models\Admin\Users'=>'app/models/admin/users'
))->register();
The loader will look quite big.
Then I have to set-up the router so that requests redirected to the right controller. Right now I have next:
// Setup Router
$di->set('router', function(){
$router = new \Phalcon\Mvc\Router(false);
$router->removeExtraSlashes(true);
$router->add('/', array(
'namespace' => 'Controllers',
'controller' => "login"
));
$router->add('/:controller/:action/', array(
'namespace' => 'Controllers',
'controller' => 1,
'action' =>2
));
$router->add('/admin/:controller/:action/', array(
'namespace' => 'Controllers\Admin',
'controller' => 1,
'action' =>2
));
$router->add('/admin/users/:controller/:action/', array(
'namespace' => 'Controllers\Admin\Users',
'controller' => 1,
'action' =>2
));
return $router;
});
That will be also very big if I need to manually setup router for each sub-folder and namespace.
So the questions that I have are this:
Is there any way to make a loader prettier and smaller? As it will grow bigger.
How can I setup router so that I don't need to enter all of the namespaces and combinations of subfolders? Is there any way to make it smaller? May be modify dispatcher or any other class? Or is there a way I can set a path variable in the router to the location of the controller?
I have researched Phalcon documentation but could not find the way to do that.
Any help and suggestions are very much appreciated.
Thanks
Ad. 1. You can change your namespaces to be more PSR-0 (in my opinion), so I would make:
app
controllers
Admin
Users
UsersController.php
The you can register one namespace Admin or any other. Then you need to register only the top most namespace to work (keep in mind that your UsersController must have namespace Admin\Users\UsersController; to work). Thena autoloader should have only:
$loader
->registerDirs(
array(
// It's taken from my config so path may be different
__DIR__ . '/../../app/controllers/'
// other namespaces here (like for models)
)
);
I'm using registerDirs so I only point loader to the folder in which some namespace exists.
Ad. 2.
For this you can use groups of routes so you can pass a namespace as a parameter for config array of constructor and then do the repeative task in one place. Then just create new instances with different parameters;
$router->addGroup(new MahGroup(array('namespace' => 'Mah\\Controller'));
So inside MahGroup class could be:
class MahGroup extends Phalcon\Mvc\Router\Group {
public function _construct($config = array()) {
$this->setPrefix('/' . $config['perfix']);
$router->add('/:controller/:action/', array(
'namespace' => $config['namespace'],
'controller' => 1,
'action' => 2
));
// etc...
}
}
And then configuring routes:
$router->addGroup( new MahGroup(array('prefix' => 'mah-namespace', 'namespace' => 'Mah\\Namespace' )) );
$router->addGroup( new MahGroup(array('prefix' => 'mah-other-namespace', 'namespace' => 'Mah\\Other\\Namespace' )) );
But given examples for second question are just what could be done. I usually create Group class for each namespace and then declare some routes that my app uses since I'm not using english names for routes and I need rewriting polish urls to controllers which have also some namespaces.

Phalcon router doesn't react to subfolders and namespace declaration

So I've been reading a ton of stackoverflow and phalcon forum threads.. (I'm starting to hate this framework), but nothing seem to work and it doesn't explain why like Laravel does, for example.
I'm just trying to be able to operate with this application structure:
As you can see, all I want is to use namespaced controllers in subfolders to make more order for my code.
According to all explanations, here's my loader.php:
<?php
$loader = new \Phalcon\Loader();
/**
* We're a registering a set of directories taken from the configuration file
*/
$loader->registerDirs(
array(
$config->application->controllersDir,
$config->application->modelsDir,
$config->application->pluginsDir
)
)->register();
AFAIK, Phalcon should traverse all subfolders for not found classes when used via registerDirs.
Then I define my routes to specific controller after the main route to index controllers in base directory:
<?php
$router = new Phalcon\Mvc\Router(false);
$router->add('/:controller/:action/:params', array(
'namespace' => 'App\Controllers',
'controller' => 1,
'action' => 2,
'params' => 3,
));
$router->add('/:controller', array(
'namespace' => 'App\Controllers',
'controller' => 1
));
$router->add('/soccer/soccer/:controller', array(
'namespace' => 'App\Controllers\Soccer',
'controller' => 1
));
$router->add('/soccer/:controller/:action/:params', array(
'namespace' => 'App\Controllers\Soccer',
'controller' => 1,
'action' => 2,
'params' => 3
));
return $router;
And one of my controllers look like:
<?php namespace App\Controllers\Soccer;
use App\Controllers\ControllerBase as ControllerBase;
class IndexController extends ControllerBase
{
public function indexAction()
{
}
}
What's wrong here? Default top namespace is not registered? Am I missing something?
This just doesn't work. When I try to open myserver.com/soccer which I expect to go to app/controllers/soccer/IndexController.php, but instead it tells me:
SoccerController handler class cannot be loaded
Which basically means it's looking for SoccerController.php in /controllers directory and totally ignores my subfolder definition and routes.
Phalcon 1.3.0
Stuck on this for a week. Any help - Much appreciated.
I was having a problem with loading the ControllerBase and the rest of the controllers in the controllers folder using namespaces. I was having a hard time since other example projects worked fine and I realized that I was missing a small detail in the despatcher declaration where I was supposed to setDefaultNamespace
(ref: https://github.com/phalcon/vokuro/blob/master/app/config/services.php)
$di->set('dispatcher', function () {
$dispatcher = new Dispatcher();
$dispatcher->setDefaultNamespace('App\Controllers');
return $dispatcher;
});
or you can specify it directly on the routing declaration file like this
$router->add("/some-controler", array(
'namespace' => 'App\Controllers'
'controller' => 'some',
'action' => 'index'
));
that should work as well, it might be a bit confusing at first with namespaces but once you get a hang of it you will love this extremely fast framework
It looks like your namespaces have capitals
App\Controllers\Soccer
and your folder structure does not
app\controllers\soccer
In my app I have controllers with a namespace and I have just tried changing the case of the folders so they don't match and I get the missing class error.
Of course it does raise a question, how many controllers are you planning on having that namespacing them is worthwhile? Are you grouping controllers and action by function or content? I used to have loads of controllers in Zend, but now I have grouped them by function I only have 16 and I am much happier. e.g. I suspect soccer, rugby, hockey etc will probably be able to share a sport controller, articles, league positions etc will have a lot of data in common.
ps Don't give up on Phalcon! In my experience it is sooo much faster than any other PHP framework I have used it is worth putting up with a lot :)
Are you sure Phalcon traverses subfolders when registering directories to the autoloader? Have you tried adding a line to your autoloader which explicitly loads the controllers\soccer directory?
Alternatively, if your soccer controller is namespaced, you can also register the namespace: "App\Controllers\Soccer" => "controllers/soccer/" with the autoloader.

ZF translation forms

I'm working with ZF2 and I'm using the translator.
In the config of my application I've added the translator to the service_manager factories, so it will be used by the ZF2 helpers.
This is how my translator config looks like:
'translator' => array(
'locale' => 'nl_NL',
'translation_file_patterns' => array(
array(
'type' => 'gettext',
'base_dir' => __DIR__ . '/../language',
'pattern' => '%s.mo',
),
),
),
In the module.php file of my application, I have the following piece of code in my onBootstrap method:
/**
* Magic to determine the right locale to be used.
*
* Check cookie
* Check GET parameter
* Check HOST (application can be used from several domain names)
*/
$translator = $serviceManager->get('translator');
// We have to change the current locale if $locale is not null.
if (!is_null($locale)) {
$translator->setLocale($locale);
// The translate helper of the view has some problems with what we're doing.
// So we'll manually add the translator with the correct locale to the view helper.
$serviceManager->get('viewhelpermanager')->get('translate')->setTranslator($translator);
}
As you can see, I've already had some problems because of the locale modification in the onBootstrap method.
Now there are two things that can help me:
- Help me to find a way to re-inject the correct translator into the form helper;
- Help me to find a way to do this the way ZF2 likes or it should be done (My searches did not lead to a solution).
Hope you guys can help me out!
It should work the same way for the form helper.
$serviceManager->get('ViewHelperManager')->get('form')->setTranslator($translator);
EDIT
And use the MvcTranslator service instead of translator.
if (!is_null($locale)) {
$translator = $serviceManager->get('MvcTranslator');
$translator->setLocale($locale);
// ...
}
If you do that, you should not even need the setTranslator() calls.
use MvcTranslator instead of translator:
$translator = $serviceManager->get('MvcTranslator');
$translator->setLocale($locale);
$serviceManager->get('ViewHelperManager')
->get('translate')->setTranslator($translator);

Zend Framework 2 ViewModel returns an empty output in some modules

There are different modules and all of them returns ViewModel in the actions. But somehow, ViewModel acting weird a bit in one of the modules.
I am saying;
$view = new ViewModel(array('data' => $someContent));
$view->setTemplate('a valid path to template');
return $view;
and getting an empty page.
If I put an exit() statement at the end of related template like
<!DOCTYPE html>
<html>
...
</html>
<?php exit(); ?>
I can get the expected output because script ends there but I lost the output otherwise.
If I say *var_dump($view)*, I can see that the $view is an instance of Zend\View\Model\ViewModel.
There is no error, just an empty output and even the notice warnings are visible. So, it doesn't throw any exception, error, warning, notice etc.
To remind that again, it just happens in a specific module but that module are not different the others actually.
I am not a ZF guru and I am working on someone else's codes, so please give me a start point to able to find that problem.
Thanks in advance.
edit : I have an extra info;
It works if I use JsonModel instead of ViewModel and as you may know, JsonModel extends the ViewModel.
Since you have not posted your controller action properly , this is the guess what I could do on your problem .
In Zend framework 2 there are various types of controllers from which you will be extending your controllers with in your modules .
for example in case if you extend your controller from AbstractActionController your view will be returned properly .
So the problem here is your other modules have controllers extending AbstractActionController . This module which is not returning your view properly might not be extending it . Instead it might be extending other controllers such as restfulcontrollers
You should also check in module.php file of your module to check whether you have any strategies eg json strategy applied on bootstrap for this module from module.config.php .
eg.
return array(
'view_manager' => array(
'strategies' => array(
'ViewJsonStrategy',
),
),
)
Also you have check your module.config.php file whether you have proper specification for your viewmanager to your template .
eg .
'view_manager' => array(
'template_path_stack' => array(
'album' => __DIR__ . '/../view',
),
),
Hope this helps .

Categories