Overriding ZF2 global/local config: unsetting - zend-framework

I've run into a problem where my local config overrides global but I need local to remove not just override.
E.g.
// global.php
'mail_transport' => [
'type' => 'Zend\Mail\Transport\Smtp',
'options' => [
'host' => 'smtp.gmail.com',
'port' => 587,
'connectionClass' => 'login',
'connectionConfig' => [
// ...
],
],
], // ...
// local.php
'mail_transport' => [
'type' => 'Zend\Mail\Transport\File',
'options' => [
'path' => 'data/mail/',
]
],
// ...
So, mail_transport is being overridden, yet its options host, port, connectionClass remain and muck up the mail transport factory. Is there any way to override as I'd like? Or is the only way to edit global.php directly?

You can add a listener on the event Zend\ModuleManager\ModuleEvent::EVENT_MERGE_CONFIG to remove the required options.
Zend\ModuleManager\Listener\ConfigListener triggers a special event, Zend\ModuleManager\ModuleEvent::EVENT_MERGE_CONFIG, after merging all configuration, but prior to it being passed to the ServiceManager. By listening to this event, you can inspect the merged configuration and manipulate it.
Such a listener could look like this.
use Zend\ModuleManager\ModuleEvent;
use Zend\ModuleManager\ModuleManager;
use Zend\ModuleManager\Feature\InitProviderInterface;
class Module implements InitProviderInterface
{
public function init(ModuleManager $moduleManager)
{
$events = $moduleManager->getEventManager();
$events->attach(ModuleEvent::EVENT_MERGE_CONFIG, [$this, 'removeMailOptions']);
}
public function removeMailOptions(ModuleEvent $event)
{
$listener = $event->getConfigListener();
$config = $listener->getMergedConfig(false);
if (isset($config['mail_transport']['type'])) {
switch($config['mail_transport']['type']) {
case \Zend\Mail\Transport\File::class :
$config['mail_transport']['options'] = [
'path' => $config['mail_transport']['options']['path']
];
break;
}
}
$listener->setMergedConfig($config);
}
}

Related

OPTION verb is not working for a list of all records in Yii 2 app

I have created a blank Yii 2 project that have created a REST UserController for already existing User model:
namespace app\controllers;
use yii\rest\ActiveController;
class UserController extends ActiveController
{
public $modelClass = 'app\models\User';
}
I have modified the model to have all fields safe:
public function rules()
{
return [
['status', 'default', 'value' => self::STATUS_INACTIVE],
['status', 'in', 'range' => [self::STATUS_ACTIVE, self::STATUS_INACTIVE, self::STATUS_DELETED]],
[['username', 'email'], 'required'],
[['username', 'email'], 'unique'],
['email', 'email'],
[['password_hash', 'password_reset_token', 'verification_token', 'auth_key', 'status,created_at', 'updated_at', 'password'], 'safe'],
];
}
I have configured URL rules to have both pluralized and non-pluralized paths:
'urlManager' => [
'enablePrettyUrl' => true,
'enableStrictParsing' => true,
'showScriptName' => false,
'rules' => [
[
'class' => 'yii\rest\UrlRule',
'controller' => 'user',
'pluralize' => false,
'except' => ['index'],
],
[
'class' => 'yii\rest\UrlRule',
'controller' => 'user',
'patterns' => [
'GET,HEAD,OPTIONS' => 'index',
],
],
],
I have enabled JSON input, if that matters:
'request' => [
'parsers' => [
'application/json' => 'yii\web\JsonParser',
]
]
All the verbs are processed correctly except for OPTIONS /users:
When I execute OPTIONS /user/20 then I am getting:
200 OK
Empty content
List of allowed methods
But, when I execute OPTIONS users then I am getting 405 Method not Allowed.
What can be wrong or what am I missing?
You are getting 405 Method Not Allowed not because of routing but because of yii\filters\VerbFilter.
The yii\rest\Controller uses verbs() method to set up VerbFilter.
The yii\rest\ActiveController overrides verbs() method and sets VerbFilter to only allow GET and HEAD requests for index action.
It uses options action for OPTIONS method.
If you really want to use index action for OPTIONS method. You have to override verbs() method yourself and add OPTIONS as allowed method for that action. For example like this:
protected function verbs()
{
$verbs = parent::verbs();
$verbs['index'][] = 'OPTIONS';
return $verbs;
}
Or if you want to use options action you have to modify patterns settings as suggested by #Bizley in comments.

Zend_Cache understanding issue

I try to use Zend_Cache (first try) to save information about user grants. The idea and most of the source code comes from Oleg Krivtsovs tutorial.
I get an error, if I try to retrieve my cache.
Call to a member function getItem() on array
Here the implementation of FilesystemCache, in my global.php
'caches' => [
'FilesystemCache' => [
'adapter' => [
'name' => Filesystem::class,
'options' => [
// Store cached data in this directory.
'cache_dir' => './data/cache',
// Store cached data for 1 hour.
'ttl' => 60*60*1
],
],
'plugins' => [
[
'name' => 'serializer',
'options' => [
],
],
],
],
],
Here my factory class:
<?php
namespace User\Service;
use User\Controller\Plugin\AuthPlugin;
use User\Model\GrantsTable;
use User\Model\UserTable;
use Zend\Authentication\AuthenticationService;
use Zend\ServiceManager\Factory\FactoryInterface;
use Interop\Container\ContainerInterface;
class AccessControlFactory implements FactoryInterface {
public function __invoke(ContainerInterface $container, $requestedName, array $options = null) {
$config = $container->get('config');
$userTable = $container->get(UserTable::class);
$grantsTable = $container->get(GrantsTable::class);
$cache = $config['caches']['FilesystemCache'];
$userplugin = $container->get(AuthPlugin::class);
// $authentication = $container->get( \Zend\Authentication\AuthenticationService::class);
return new AccessControl($userTable, $grantsTable, $cache, $userplugin);//, $authentication
}
}
Now in the init function within my AccessControl Service, I try to retrieve from the cache:
$this->cache->getItem('rbac_container', $result);
There I get the above error.
Any help with a bit of explanation would be appreciated.
What you're injecting to the AccessControl constructor is an array, not a cache implementation, because $config['caches']['FilesystemCache'] returns an array of FilesystemCache options (adapter, plugins, etc.). What you're supposed to do is fetch the cache implementation via the ContainerInterface, like this:
$cache = $container->get('FilesystemCache');
Then the ContainerInterface will depend on StorageCacheAbstractServiceFactory to find your requested cache configs and return the class for you.

Yii2 advanced change views default path (theming)

I would like for my application to automatically change template
so i created this structure frontend/web/themes/myTheme
following http://www.yiiframework.com/doc-2.0/guide-output-theming.html i added this code in frontend/config/main.php
'components' => [
'view' => [
'theme' => [
'basePath' => '#app/themes/myTheme',
'baseUrl' => '#web/themes/myTheme',
'pathMap' => [
'#app/views' => '#app/themes/myTheme',
],
],
],
],
however i kept getting the error that " /var/www/html/myProject/app/frontend/views/site/index.php" The view file does not exist???
i also tried to put this function inside the controller based on How to change default view path in Yii2?
public function getViewPath()
{
return Yii::getAlias('#web/themes/myTheme/site');
}
so my question is:
1. how can I change the views default path?
2. how can i do it automatically since i can not change the common/config/main.php settings during a session?
site controller
class SiteController extends Controller
{
public function behaviors()
{
return [
'access' => [
'class' => AccessControl::className(),
'rules' => [
[
'actions' => ['index'],
'allow' => true,
'roles' => ['?'],
],
[
'actions' => ['index'],
'allow' => true,
'roles' => ['#'],
],
],
],
];
}
public function actions()
{
return [
'error' => [
'class' => 'yii\web\ErrorAction',
],
'captcha' => [
'class' => 'yii\captcha\CaptchaAction',
'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
],
];
}
/**
* Displays homepage.
*
* #return mixed
*/
public function actionIndex()
{
$searchModel = new ProductSearch();
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('index', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
}
I think you are configuring the wrong file.Don't configure themes in the common/config
Try this:
in frontend/config/main.php
'components' => [
'view' => [
'theme' => [
'pathMap' => [
'#frontend/views'=>'#frontend/themes/myTheme',
],
],
],
],
if you need to configure the backend then in the backend/config/main.php
'components' => [
'view' => [
'theme' => [
'pathMap' => [
'#backend/views'=>'#backend/themes/myTheme',
],
],
],
],
The common folder is has to contain the files that are required by both
frontend and backend.
Hope this helps.
First question:
I think than you have a common mistake in yii when used advanced app: the alias #app references root directory of frontend, backend, or common depending on where you access it from View documentation here.
You would used the solution proposed by ovicko.
Second question:
You can change the theme configuration dynamically in controller through view object:
$this->view->theme->pathMap =['#app/views' => '#app/themes/myTheme/',];
EDIT
According to Documentation:
Theming is a way to replace a set of views with another without the need of touching the original view rendering code.
What means that the original view file must exist and theming simply replace it in during rendering. So you must create a file in /var/www/html/myProject/app/frontend/views/site/index.php (a empty file is valid) in order to theming works.
This sounds quite ridiculous, I Know, but it works.
But I think that is much better and easier the use of differents layouts, again, to change dinamically the layout in your controller:
$this->layout = 'route/yourlayout';

Yii2 Rest URL Routing for Regular Controllers

How is it possible to extend yii\rest\UrlRule in a way I can rewrite rules for actions of a controller? For example, I want to generate the following URIs:
/user/[username]
/user/keywords
/user/keyword/[key1]/[key2]/[...]
...
Every above actions are rendering their own view too.
You don't need to extend yii\rest\UrlRule. Just add your rules to routes of UrlManager by putting them on extraPatterns property of yii\rest\UrlRule.
For example suppose You defined a list action in your controller:
class BarController extends Controller
{
public $modelClass = 'app\models\Foo';
public function actionList()
{
return ['id' => 1];
}
}
Then in configuration file add extra route:
<?php
// some configs are here
'urlManager' => [
'class' => 'yii\web\UrlManager',
'enablePrettyUrl' => true,
'enableStrictParsing' => true,
'showScriptName' => false,
'rules' => [
[
'class' => 'yii\rest\UrlRule',
'controller' => [
'v1/bar',
],
'extraPatterns' => [
'GET list' => 'list',
],
],
],
],
// and some other configs are here
Now you can browse the api with /v1/bars/list. Read Yii2 Documentations for more examples.
you must change the extends of Controller in ActiveController
class ArticleController extends ActiveController

ZF2 access to config in router configuration

i route by hostname and i want to get my domain name by local.php config file in config/autoload in zf2
i know how should i get configs in controller,
but i dont know how can i get it in my router configuration file
i comment what i want in my code
'router' => array(
'routes' => array(
'advertise' => array(
'type' => 'Hostname',
'options' => array(
'route' => 'www.myhome.com', // here i want to get my domain by config
'defaults' => array(
'controller' => 'Advertise\Controller\Advertise',
'action' => 'index',
),
),
.............
You can use the API of the 'router' service (an instance of Zend\Mvc\Router\Http\TreeRouteStack) and add a route dynamically.
How you attach the route to the route stack is up to you, you could extend the default router factory Zend\Mvc\Service\RouterFactory with your own routes from config.
use MyModule\Mvc\Service;
use Zend\Mvc\Service\RouterFactory;
use Zend\ServiceManager\ServiceLocatorInterface;
class MyRouterFactory extends RouterFactory
{
public function createService(ServiceLocatorInterface $serviceLocator, $cName = null, $rName = null)
{
$serviceManager = $serviceLocator->getServiceLocator();
$router = parent::createService($serviceLocator, $cName, $rName);
$config = $serviceManager->get('config');
$router->addRoute('advertise', [
'type' => 'hostname',
'options' => [
'route' => $config['some_other_config_key'],
'defaults' => [
'controller' => 'Advertise\Controller\Advertise',
'action' => 'index'
]
],
'priority' => 123
]);
return $router;
}
}
Remember to register it with the name Router in module.config.php to replace the default implementation.
'service_manager' => [
'factories' => [
'Router' => 'MyModule\Mvc\Service\MyCustomRouterFactory',
],
],
The nice thing with this approach is that the routers construction is all kept in one place; also as you are adding it with a factory class
you have access to other services should you need them.
Alternatively, you could add it via an event, although being triggered via the event manager, this method would likely be more resource intensive.
namespace MyModule;
use Zend\ModuleManager\InitProviderInterface;
use Zend\ModuleManager\ModuleManagerInterface;
use Zend\Mvc\Application;
use Zend\Mvc\MvcEvent;
class Module implements InitProviderInterface
{
// init is called when the module is initilised, we can use this to add a listener to the
// 'bootstrap' event
public function init(ModuleManagerInterface $manager)
{
$eventManager = $manager->getEventManager()->getSharedManager();
$eventManager->attach(Application::class, MvcEvent::EVENT_BOOTSTRAP, [$this, 'addRoutes']);
}
public function addRoutes(MvcEvent $event)
{
$serviceManager = $event->getApplication()->getServiceManager();
$router = $serviceManager->get('Router');
$config = $serviceManager->get('Config');
$router->addRoute('advertise', [
'type' => 'hostname',
'options' => [
'route' => $config['some_other_config_key'],
'defaults' => [
'controller' => 'Advertise\Controller\Advertise',
'action' => 'index'
]
],
'priority' => 123
]);
}
}