I have a questiom regarding the Zend Framework 2:
I have
library/System and library/Zend. the system is my custom library, which I want to configure de aplication (routes, modules, etc., and redirect user to correct module, controller and/or action).
I don't want to do this inside each application/modules/ModuleName/Module.php file. So, my library/System can do everything related to application configuration.
As said in the comments above: register to the bootstrap-event and add new routes there:
<?php
namespace Application;
use Zend\Module\Manager,
Zend\EventManager\StaticEventManager;
class Module
{
public function init(Manager $moduleManager)
{
$events = StaticEventManager::getInstance();
$events->attach('bootstrap', 'bootstrap', array($this, 'initCustom'), 100);
}
public function initCustom($e)
{
$app = $e->getParam('application');
$r = \Zend\Mvc\Router\Http\Segment::factory(array(
'route' => '/test',
'defaults' => array(
'controller' => 'test'
)
)
);
$app->getRouter()->addRoute('test',$r);
}
}
$app = $e->getParam('application'); does return an instance of Zend\Mvc\Application. Have a look there to see which additional parts you can get there. The bootstrap event is fired before the actual dispatching does happen.
Note that the ZendFramework 1 routes are not always compatible to the ZendFramework 2 ones.
Update to comments
public function initCustom($e)
{
$app = $e->getParam('application');
// Init a new router object and add your own routes only
$app->setRouter($newRouter);
}
Update to new question
<?php
namespace Application;
use Zend\Module\Manager,
Zend\EventManager\StaticEventManager;
class Module
{
public function init(Manager $moduleManager)
{
$events = StaticEventManager::getInstance();
$events->attach('bootstrap', 'bootstrap', array($this, 'initCustom'), 100);
}
public function initCustom($e)
{
$zendApplication = $e->getParam('application');
$customApplication = new System\Application();
$customApplication->initRoutes($zendApplication->getRouter());
// ... other init stuff of your custom application
}
}
This only happens in one zf2 module (named Application which can be the only one as well). This doesn't fit your needs? You could:
extend a custom module autoloader
extend Zend\Mvc\Application for your own logic
make your code zf2-compatible
Related
I need to optionally include some modules in my ZF2 app. The modules are entirely independent with any loaded modules.
In application.config.php, in the config array I can just include the main modules, and then, at the end, based on some conditions, to add the optional module. Like this:
$config = array(
'modules' => array(
'Application',
),
...
);
if (condition) {
$config['modules'][] = 'OptionalModule';
}
return $config;
Though this works and fixes the problem, I was wondering if there is another way of doing this.
Is it a good approach for this use case? Would there be a nicer way to accomplish this?
Thanks!
I usually do this by using any of the below two methods:
Conditionally load module with local application config
application.config.php:
<?php
use Zend\Stdlib\ArrayUtils;
$config = [
// You config
];
$local = __DIR__ . '/application.config.local.php';
if (is_readable($local)) {
$config = ArrayUtils::merge($config, require($local));
}
return $config;
application.config.local.php:
<?php
return [
// Your config
];
This enables you to have a base application config and load an additional config per deployment. So no if $condition, this is determined by your deployment process, which is most times easier to manage.
Note this also works for deployment configs: application.config.development.php vs application.config.production.php. This is just whatever you like, to suit your needs.
Conditionally execute code in module config
In your Module.php
<?php
namespace MyModule;
use Zend\Mvc\MvcEvent;
class Module
{
public function onBootstrap(MvcEvent $e)
{
$app = $e->getApplication();
$sm = $app->getServiceManager();
$config = $sm->get('Config');
if ($config['mymodule']['enabled'] === true) {
// condition
}
}
}
Then you can have your module.config.php in your own module folder:
<?php
return [
'mymodule' => [
'enabled' => true,
],
];
But if youn need to disable this in a certain environment, you add this to your config/autoload/local.php:
<?php
return [
'mymodule' => [
'enabled' => false,
],
];
I am new to yii and i have to create a yii component for Enom api .I have followed this url Enom application for refrence . It is in core php and i want to implement this in yii as component or module .I have done in this way
put the files interface and class in the yii component folder.
modify the class as mentioned here yii custom component . Now my class name is EnomService and interface name is EnomInterface
i have added these lines also in my class
use Yii;
use yii\base\Component;
use yii\base\InvalidConfigException;
modified the main.php file in config folder:
'import'=>array(
'application.models.*',
'application.components.*',
),
'defaultController'=>'post',
// application components
'components'=>array(
'user'=>array(
// enable cookie-based authentication
'allowAutoLogin'=>true,
),
'mycomponent' => [
'class' => 'app\components\EnomService',
],
calling in the controller in this way .
public function actionEnom ()
{
echo "asdgsgsag";
$enom = new EnomService('manoj_rudra', 'manoj#41#', false, true);
$enom->debug = true;
$result= Yii::$app->EnomService->checkDomain('systurn', 'com', true);
//$result = $enom->checkDomain('systurn', 'com', true); // This enables domain spinner
echo '<pre>';
var_dump($result);
echo '</pre>';
}
But it is not working . I am not so much familiar with yii custom component . Please help me to create this .
Are you using Yii or Yii2?
If it is Yii, then you could use plenty of other existing extensions to inspire yourself, for example this one: https://github.com/HeavyDots/yii-sms
As for Yii2 you could do something similar, look into already existing extensions for Yii2 on YiiFramework website and you can see how component classes are defined.
I would recommend:
1) Create a new directory inside "components" named "enom"
2) Place inside that directory all your enom files from https://github.com/comdexxsolutionsllc/MoondayFramework/tree/master/engine/enom
3) Create the component class called "Enom.php" inside the directory, something like this:
<?php
// include enom service class
require(dirname(__FILE__).'/class.EnomService.php');
namespace components\enom;
use Yii;
class Enom extends \yii\base\Component
{
// define private property to store service
private $service;
public function init()
{
parent::init();
// init the service
$this->service=new EnomService('manoj_rudra', 'manoj#41#', false, true);
}
/**
* #return EnomService
*/
public function getService() {
return $this->service;
}
}
?>
4) Then in the configuration properly define the component
'enom' => [
'class' => 'app\components\enom\Enom',
],
5) And finally use it like this
Yii::$app->enom->getService()->checkDomain
As I said before, haven't used Yii2 yet so this might need tweaking but could point you on the right path.
I'm using ZFCUser and need to develop a German project.
Unfortunately the login form is in English and I couldn't find a way to translate the form fields or especially the error messages to English.
Is there maybe a global way for the module to either overwrite the messages or switch the language?
Thanks!
Edit:
This is my translator call in my bootstrap:
$translator = new Translator();
$translator->addTranslationFile(
'phpArray',
'vendor/zendframework/zendframework/resources/languages/de/Zend_Validate.php',
'default',
'de_DE'
);
AbstractValidator::setDefaultTranslator($translator);
Edit II:
My custom translation file:
<?php
return array(
// ZFCUser
"Authentication failed. Please try again." => "test"
);
My factory:
<?php
class CustomTranslatorFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $sl)
{
$translator = new Translator();
$translator->addTranslationFile(
'phpArray',
'vendor/.../TranslationTable.php',
'default',
'de_DE'
);
return $translator;
}
}
Module.php from my User module:
public function getServiceConfig()
{
return array(
'factories' => array(
'translator' => 'Path\To\Translator\CustomTranslatorFactory',
),
);
}
Edit III:
My Translator looks like this. Even when using a PHP file instead of the array just nothing happens. No error, no translation. Any ideas what I'm doing wrong?
$custom_translations = array(
"Authentication failed. Please try again." => "test",
);
$translator = new Translator();
$translator->addTranslationFile(
'phpArray',
'vendor/zendframework/zendframework/resources/languages/de/Zend_Validate.php',
'default',
'de_DE'
);
$translator->addTranslationFile(
'phpArray',
$custom_translations
);
return $translator;
Just for Your knowledge - since yesterday there's a modular de_DE translation available (pl_PL and ja_JP are available too). It covers only ZfcUser messages, so not all validators, but form labels etc. https://github.com/websafe/zf-mod-zfc-user-i18n-de-de Easy installable and available via Packagist too.
I'm creating an answer for that because it's becoming too complex for the comment section.
The validator library does translation of messages on its own. As you've assigned the translator to it, your validation messages are fine.
However, form label translation belongs to another piece of library. Accordingly, they also need a translator assigned to them. As stated above in the comments, you can either do that manually (by invoking $viewHelper->setTranslator($translator)) or let the ViewHelperManager do that for you.
You can easily refactor your code to support the second case.
Create a factory class for your translator.
Register that factory to SM. Use the "translator" key.
If you need it, retrieve your $translation var through SM in future.
Example (uses skipped):
/** One of your modules, should be a base module that's always loaded */
class Module
{
// ...
public function getServiceConfig()
{
return array(
'factories' => array(
'translator' => 'Your\Translator\Factory', // could also be a closure (anonymous function)
)
);
}
}
-
class YourTranslatorFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $sl)
{
$translator = new Translator();
$translator->addTranslationFile(
'phpArray',
'vendor/zendframework/zendframework/resources/languages/de/Zend_Validate.php',
'default',
'de_DE'
);
return $translator;
}
}
If you need to access it, simpyl retrieve it from SM as you're used to ($translator = $sm->get('translator');).
Note: Validators implement the TranslatorAwareInterface. This means that, if you've registered the translator key to SM, they also should be injected into the validators automatically. Thus, you can even skip the static method call.
Note also: This is just an example of how you can do it without changing much. You could also reach this goal just by configuration.
How I can get access to my module config from the controller?
I am really surprised at how obscure this is, because I had exactly the same problem and could not find a definitive answer. One would think the ZF2 documentation would say something about this. Anyhow, using trial and error, I came across this extremely simple answer:
Inside controller functions:
$config = $this->getServiceLocator()->get('Config');
Inside Module class functions (the Module.php file):
$config = $e->getApplication()->getServiceManager()->get('Config');
whereas $e is an instance of Zend\Mvc\MvcEvent
In general, the config is accessible from anywhere you have access to the global service manager since the config array is registered as a service named Config. (Note the uppercase C.)
This returns an array of the union of application.config.php (global and local) and your module.config.php. You can then access the array elements as you need to.
Even though the OP is quite old now, I hope this saves someone the hour or more it took me to get to this answer.
What exactly do you want to do in your controller with the module configuration? Is it something that can't be done by having the DI container inject a fully configured object into your controller instead?
For example, Rob Allen's Getting Started with Zend Framework 2 gives this example of injecting a configured Zend\Db\Table instance into a controller:
return array(
'di' => array(
'instance' => array(
'alias' => array(
'album' => 'Album\Controller\AlbumController',
),
'Album\Controller\AlbumController' => array(
'parameters' => array(
'albumTable' => 'Album\Model\AlbumTable',
),
),
'Album\Model\AlbumTable' => array(
'parameters' => array(
'config' => 'Zend\Db\Adapter\Mysqli',
)),
'Zend\Db\Adapter\Mysqli' => array(
'parameters' => array(
'config' => array(
'host' => 'localhost',
'username' => 'rob',
'password' => '123456',
'dbname' => 'zf2tutorial',
),
),
),
...
If you need to do additional initialization after the application has been fully bootstrapped, you could attach an init method to the bootstrap event, in your Module class. A blog post by Matthew Weier O'Phinney gives this example:
use Zend\EventManager\StaticEventManager,
Zend\Module\Manager as ModuleManager
class Module
{
public function init(ModuleManager $manager)
{
$events = StaticEventManager::getInstance();
$events->attach('bootstrap', 'bootstrap', array($this, 'doMoarInit'));
}
public function doMoarInit($e)
{
$application = $e->getParam('application');
$modules = $e->getParam('modules');
$locator = $application->getLocator();
$router = $application->getRouter();
$config = $modules->getMergedConfig();
// do something with the above!
}
}
Would either of these approaches do the trick?
for Beta5, you can add function like this in Module.php
public function init(ModuleManager $moduleManager)
{
$sharedEvents = $moduleManager->getEventManager()->getSharedManager();
$sharedEvents->attach(__NAMESPACE__, 'dispatch', function($e) {
$config = $e->getApplication()->getConfiguration();
$controller = $e->getTarget();
$controller->config = $config;
});
}
in controller, you can get config :
print_r($this->config);
To read module-only config your module should just implement LocatorRegisteredInterface
Before:
namespace Application;
class Module
{
// ...
}
After:
namespace Application;
use Zend\ModuleManager\Feature\LocatorRegisteredInterface;
class Module implements LocatorRegisteredInterface
{
// ...
}
That implementation says LocatorRegistrationListener to save module intance in service locator as namespace\Module
Then anywhere you can get access to your module:
class IndexController extends AbstractActionController
{
public function indexAction()
{
/** #var \Application\Module $module */
$module = $this->getServiceLocator()->get('Application\Module');
$moduleOnlyConfig = $module->getConfig();
// ...
}
}
There is a pull request ready now which pulls the module class (so the modules/foo/Module.php Foo\Module class) from the DI container. This gives several advantages, but you are also able to grab that module instance another time if you have access to the Zend\Di\Locator.
If your action controller extends the Zend\Mvc\Controller\ActionController, then your controller is LocatorAware. Meaning, upon instantiation your controller is injected with the locator knowing about modules. So, you can pull the module class from the DIC in your controller. Now, when your module consumes a config file and stores this inside the module class instance, you can create a getter to access that config data from any class with a locator. You probably have already an accessor with your module Foo\Module::getConfig()
While ZF2 is heavily under development and perhaps this code will change later on, this feature is currently covered by this test, with this the most relevant part:
$sharedInstance = $locator->instanceManager()->getSharedInstance('ListenerTestModule\Module');
$this->assertInstanceOf('ListenerTestModule\Module', $sharedInstance);
So with $sharedInstance your module class, you can access the config from there. I expect a shorthand for this feature soon, but this can only be done after PR #786 has been merged in ZF2 master.
You need to implements ServiceLocatorAwareInterface from your model. And then you can set setServiceLocator() and getServiceLocator() which give you direct access to the service manager. Take a look at this code sample https://gist.github.com/ppeiris/7308289
I created the module with controller plugin and view helper for reading a config in controllers and views. GitHub link __ Composer link
Install it via composer
composer require tasmaniski/zf2-config-helper
Register new module "ConfigHelper" in your config/application.config.php file
'modules' => array(
'...',
'ConfigHelper'
),
Use it in controller and view files
echo $this->configHelp('key_from_config'); // read specific key from config
$config = $this->configHelp(); // return config object Zend\Config\Config
echo $config->key_from_config;
you can also access any config value anywhere by this hack/tricks
$configReader = new ConfigReader();
$configData = $configReader->fromFile('./config.ini');
$config = new Config($configData, true);
We're using ZendFramework at my workplace for our webapps. It's ok, but it lacks some of the best modern practices (like dependency injection and inversion of control, aop, etc).
For a couple of months, I've been (on my own) using Ding framework as a container for DI and AOP as a test drive. I really like it, so I'd like to bring it into our projects.
But how? So there's the question: how to properly integrate Ding in Zend Framework applications? considering ZF controllers cant be beans (as they are instantiated right from the dispatcher), how to propertly inject all dependencies in them?
P.s: Not using Zend Framework is not an option (at least in the middle term).
P.P.S: Anyone care to add "ding" as a new tag?
I'm glad Ding is helping you.
I contributed on this project and also needed to integrate with a Zend Framework application. I used Zend's application resources and plugin system to achieve this.
An application resource (you can reuse among projects)
<?php
class Application_Resource_Ding extends Zend_Application_Resource_ResourceAbstract
{
protected $_options = array(
'factory' => array(
'bdef' => array(
'xml' => array(
'filename' => array('beans.xml')
),
),
),
'cache' => array(
'proxy' => array('impl' => 'dummy'),
'bdef' => array('impl' => 'dummy'),
'beans' => array('impl' => 'dummy')
)
);
public function init()
{
// set default config dir before mergin options (cant be set statically)
$this->_options['factory']['bdef']['xml']['directories'] = array(APPLICATION_PATH .'/configs');
$options = $this->getOptions();
// parse factory properties (if set)
if (isset($options['factory']['properties'])) {
$options['factory']['properties'] = parse_ini_file(
$options['factory']['properties']
);
}
// change log4php_properties for log4php.properties (if exists)
if (isset($options['log4php_properties'])) {
$options['log4php.properties'] = $options['log4php_properties'];
unset($options['log4php_properties']);
}
$properties = array(
'ding' => $options
);
return Ding\Container\Impl\ContainerImpl::getInstance($properties);
}
}
An action helper to use inside the controllers:
<?php
class Application_Action_Helper_Ding extends Zend_Controller_Action_Helper_Abstract
{
protected $ding = null;
public function init()
{
// just once...
if (null !== $this->ding) {
return;
}
// get ding bootstrapped resource
$bootstrap = $this->getActionController()->getInvokeArg('bootstrap');
$ding = $bootstrap->getResource('ding');
if (!$ding) {
throw new Zend_Controller_Action_Exception(
'Ding resource not bootstrapped'
);
}
$this->ding = $ding;
}
public function getBean($bean)
{
return $this->ding->getBean($bean);
}
public function direct($bean)
{
return $this->getBean($bean);
}
}
In your application.ini you should add something like this (plus any extra configuration you need)
resources.frontController.actionHelperPaths.Application_Action_Helper = "Application/Action/Helper"
resources.ding.factory.properties = APPLICATION_PATH "/configs/ding.properties"
resources.ding.log4php_properties = APPLICATION_PATH "/configs/log4php.properties"
And then in your controllers, to request a bean:
$service = $this->_helper->ding('someService');
Hope this helps!