mongodb with zf2 gives an error The class was not found in the chain configured namespaces - mongodb

I know I miss something in the configuration.
Can anyone provide the steps for this configuration
Mongo is running in my terminal properly.
when connect with zend in sequential way it works . but in procedural way it gives error.
this is global config file
'driver' => array(
'odm_default' => array(
'class' => 'Doctrine\Common\Persistence\Mapping\Driver\MappingDriverChain',
'drivers' => array()
)
),
when I write
$dm = $this->getServiceLocator()->get('doctrine.documentmanager.odm_default');
$user = new User();
$user->setName("Gembul");
$dm->persist($user);
persist then it gives class not found error

When I found this error then I modify my database class means that change the comments in my model class in document section .
use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;
/**
* Description of User
*
* #author kiwitech
*/
/** #ODM\Document(collection="user") */
class Users
{
protected $dm;
/** #ODM\Id */
private $_id;
/** #ODM\Field(type="string") */
private $_first_name;
/** #ODM\Field(type="string") */
private $_last_name;
/** #ODM\Field(type="string") */
private $_login_name;
/** #ODM\Field(type="string") */
private $_email;
/** #ODM\Field(type="string") */
private $tp_id;
/** #ODM\Field(type="string") */
private $_is_active;
/** #ODM\Field(type="string") */
private $_role_id;
function get_id() {
return $this->_id;
}
function get_first_name() {
return $this->_first_name;
}
function get_last_name() {
return $this->_last_name;
}
function get_login_name() {
return $this->_login_name;
}
function get_email() {
return $this->_email;
}
function getTp_id() {
return $this->tp_id;
}
function get_is_active() {
return $this->_is_active;
}
function get_role_id() {
return $this->_role_id;
}
function set_id($_id) {
$this->_id = $_id;
}
function set_first_name($_first_name) {
$this->_first_name = $_first_name;
}
function set_last_name($_last_name) {
$this->_last_name = $_last_name;
}
function set_login_name($_login_name) {
$this->_login_name = $_login_name;
}
function set_email($_email) {
$this->_email = $_email;
}
function setTp_id($tp_id) {
$this->tp_id = $tp_id;
}
function set_is_active($_is_active) {
$this->_is_active = $_is_active;
}
function set_role_id($_role_id) {
$this->_role_id = $_role_id;
}
}
then I easily solve this bug :)

Related

get settings in validator - typo3

I have an extension with backend configuration options.I need to validate a phone number in AddAction and UpdateAction.I can configure the phone number format in backend (say us phone number/indian phone number etc).How can i get the settings in validator?
I have a custom validator to validate phone numbers.Here is my code
<?php
namespace vendor\Validation\Validator;
class UsphonenumberValidator extends \TYPO3\CMS\Extbase\Validation\Validator\AbstractValidator
{
protected $supportedOptions = array(
'pattern' => '/^([\(]{1}[0-9]{3}[\)]{1}[ ]{1}[0-9]{3}[\-]{1}[0-9]{4})$/'
);
public function isValid($property) {
$settings = $this->settings['phone'];
$pattern = $this->supportedOptions['pattern'];
$match = preg_match($pattern, $property);
if ($match >= 1) {
return TRUE;
} else {
$this->addError('Phone number you are entered is not valid.', 1451318887);
return FALSE;
}
}
}
$settings returns null
In cases where the extbase configuration of your extension isn't implemented by default you should retrieve it yourself by using the \TYPO3\CMS\Extbase\Configuration\ConfigurationManager.
Here is an example how you can obtain the settings of your extension:
<?php
namespace MyVendor\MyExtName\Something;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManager;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
use TYPO3\CMS\Extbase\Object\ObjectManager;
class Something {
/**
* #var string
*/
static protected $extensionName = 'MyExtName';
/**
* #var null|array
*/
protected $settings = NULL;
/**
* Gets the Settings
*
* #return array
*/
public function getSettings() {
if (is_null($this->settings)) {
$this->settings = [];
/* #var $objectManager \TYPO3\CMS\Extbase\Object\ObjectManager */
$objectManager = GeneralUtility::makeInstance(ObjectManager::class);
/* #var $configurationManager \TYPO3\CMS\Extbase\Configuration\ConfigurationManager */
$configurationManager = $objectManager->get(ConfigurationManager::class);
$this->settings = $configurationManager->getConfiguration(
ConfigurationManagerInterface::CONFIGURATION_TYPE_SETTINGS,
self::$extensionName
);
}
return $this->settings;
}
}
I recommend that you implement such functionality in general. So you could retrieve any configuration of any extension as a Service inside your extension or something similar to this.
Good luck!

Custom Form Editing Setting Conent in Symfony

I created the following Custom Form Type
<?php
namespace UserBundle\Form\Type;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\HttpFoundation\Request;
use UserBundle\Entity\Users;
class UserType extends AbstractType
{
const FORM = 'user';
/**
* #var
*/
protected $UserContext;
/**
* Construct
* #param $uc
*/
public function __construct($uc)
{
$this->UserContext = $uc;
}
/**
* Build Form
* #param \Symfony\Component\Form\FormBuilderInterface $builder
* #param array $options
*/
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('username','text',array('label'=>'username'))
->add('password','text',array('label'=>'password'))
->add('firstname','text',array('label'=>'firstname','required'=>false))
->add('lastname','text',array('label'=>'lastname','required'=>false))
->add('email','email',array('label'=>'email','required'=>true));
$roles = array(
'Guest'=>array(
'ROLE_GUEST' =>'ROLE_GUEST',
),
'Users'=>array(
'ROLE_USER' =>'ROLE_USER',
),
'Customers'=>array(
'ROLE_CUSTOMER' =>'ROLE_CUSTOMER',
),
);
if($this->UserContext->isGranted('Role_SUPERVISOR'))
{
$roles['Staff']['ROLE_STAFF'] = 'ROLE_STAFF';
$roles['Staff']['ROLE_SUPPORT'] = 'ROLE_SUPPORT';
}
if($this->UserContext->isGranted('ROLE_ADMIN'))
{
$roles['Staff']['ROLE_SUPERVISOR'] = 'ROLE_SUPERVISOR';
}
if($this->UserContext->isGranted('ROLE_SUPER_ADMIN'))
{
$roles['Staff']['ROLE_ADMIN'] = 'ROLE_ADMIN';
}
if($this->UserContext->isGranted('ROLE_GOD'))
{
$roles['Staff']['ROLE_SUPER_ADMIN'] = 'ROLE_SUPER_ADMIN';
$roles['Staff']['ROLE_GOD'] = 'ROLE_GOD';
}
$builder->add('role','choice',array(
'choices'=>$roles,
'required'=>true,
'label'=>'userrole'
))
->add('status','choice',array(
'choices'=>array(
'1'=>'account active',
'0'=>'account inactive',
),
'required'=>true,
'label'=>'status'
));
}
/**
* get Form Name
* #return string
*/
public function getName()
{
return self::FORM;
}
/**
* persist Form
* #param \Symfony\Component\HttpFoundation\Request $request
* #param \UserBundle\Entity\Users $user
* #param \Doctrine\ORM\EntityManager $em
*
* #return \UserBundle\Entity\Users
*/
public static function storeFormContent(Request $request,Users $user, EntityManager $em)
{
$formData = $request->request->get(self::FORM);
$isNew = (int)$user->getId()<1;
$user->setFirstname($formData['firstname']);
$user->setLastname($formData['lastname']);
$user->setUsername($formData['username']);
if(!empty($formData['password']) || $isNew)
{
$user->setPassword($formData['password']);
}
$user->setEmail($formData['email']);
$user->setRole($formData['role']);
$user->setActive($formData['status']);
if($isNew)
{
$em->persist($user);
}
$em->flush();
return $user;
}
}
In my Controller I added this method to edit a User:
/**
* #Route("/Administration/Users/Edit", name="admin_users_edit")
* #Template()
*/
public function editAction()
{
$this->denyAccessUnlessGranted('ROLE_ADMIN', null, 'Unable to access this page!');
$request = Request::createFromGlobals();
$userid = (int)$request->query->get('id', null);
if (!$this->get('security.authorization_checker')->isGranted('IS_AUTHENTICATED_FULLY')) {
$session = new Session();
$session->start();
$session->set('currentRoute',array('call'=>'admin_users_edit','parameters'=>array('id'=>$userid)));
throw $this->createAccessDeniedException();
}
$em = $this->getDoctrine()->getManager();
/** #var Users $user */
$user = $this->getDoctrine()
->getRepository('UserBundle\Entity\Users')
->find($userid);
if(empty($user) || !($user instanceof Users) || $user->getId() < 0)
{
return $this->redirectToRoute('admin_users_index', array('status' => 'user_does_not_exist'));
}
if($user->getRole()=='ROLE_ADMIN')
{
$this->denyAccessUnlessGranted('ROLE_SUPER_ADMIN', null, 'Unable to access this page!');
}
if($user->getRole()=='ROLE_SUPER_ADMIN' or $user->getRole()=='ROLE_GOD')
{
$this->denyAccessUnlessGranted('ROLE_GOD', null, 'Unable to access this page!');
}
if(!($user instanceof Users))
{
return $this->redirectToRoute('admin_users_index', array('status' => 'not_found'));
}
$form = $this->createForm(new UserType($this->get('security.context'),$user));
$form->handleRequest($request);
if($form->isValid())
{
UserType::storeFormContent($request,$user,$this->getDoctrine()->getManager());
return $this->redirectToRoute('admin_users_index', array('status' => 'create_success'));
}
return array(
'activeMenu'=>'users',
'error'=>$request->query->get('error'),
'form'=>$form->createView(),
);
}
Everything works fine so far.
Creating Users Work without an issue.
But Editing them leaves me blank fields.
I can't see where the issue is comming from.
I appreceate any help.
Chris
P.S.:
I have to seperate the Form into a Custom Type, because I will need to reuse it on several locations.
I will also reuse the twig part of the form for this.
^^
I just discovered that $form->setData() works for you, because you forgotten to add setDefaultOptions method to your form type:
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
...
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$resolver->setDefaults(array(
'data_class' => 'UserBundle\Entity\Users',
));
}
You can remove $form->setData();

phpunit: How do I pass values between tests?

I'm really running into a brick wall with this. How do you pass class values between tests in phpunit?
Test 1 -> sets value,
Test 2 -> reads value
Here is my code:
class JsonRpcBitcoinTest extends PHPUnit_Framework_TestCase
{
public function setUp(){
global $configRpcUser, $configRpcPass, $configRpcHost, $configRpcPort;
$this->bitcoindConn = new JsonRpcBitcoin($configRpcUser, $configRpcPass, $configRpcHost, $configRpcPort);
$this->blockHash = '';
}
/**
* #depends testCanAuthenticateToBitcoindWithGoodCred
*/
public function testCmdGetBlockHash()
{
$result = (array)json_decode($this->bitcoindConn->getblockhash(20));
$this->blockHash = $result['result'];
$this->assertNotNull($result['result']);
}
/**
* #depends testCmdGetBlockHash
*/
public function testCmdGetBlock()
{
$result = (array)json_decode($this->bitcoindConn->getblock($this->blockHash));
$this->assertEquals($result['error'], $this->blockHash);
}
}
testCmdGetBlock() is not getting the value of $this->blockHash that should be set in testCmdGetBlockHash().
Help in understanding what is wrong would be greatly appreciated.
The setUp() method is always called before tests, so even if you set up a dependency between two tests, any variables set in setUp() will be overwritten. The way PHPUnit data passing works is from the return value of one test to the parameter of the other:
class JsonRpcBitcoinTest extends PHPUnit_Framework_TestCase
{
public function setUp()
{
global $configRpcUser, $configRpcPass, $configRpcHost, $configRpcPort;
$this->bitcoindConn = new JsonRpcBitcoin($configRpcUser, $configRpcPass, $configRpcHost, $configRpcPort);
$this->blockHash = '';
}
public function testCmdGetBlockHash()
{
$result = (array)json_decode($this->bitcoindConn->getblockhash(20));
$this->assertNotNull($result['result']);
return $result['result']; // the block hash
}
/**
* #depends testCmdGetBlockHash
*/
public function testCmdGetBlock($blockHash) // return value from above method
{
$result = (array)json_decode($this->bitcoindConn->getblock($blockHash));
$this->assertEquals($result['error'], $blockHash);
}
}
So if you need to save more state between tests, return more data in that method. I would guess that the reason PHPUnit makes this annoying is to discourage dependent tests.
See the official documentation for details.
You can use a static variable within a function...
PHP annoyingly shares static variables of class methods with all the instances... But in this cas it can help :p
protected function &getSharedVar()
{
static $value = null;
return $value;
}
...
public function testTest1()
{
$value = &$this->getSharedVar();
$value = 'Hello Test 2';
}
public function testTest2()
{
$value = &$this->getSharedVar();
// $value should be ok
}
NB: this is NOT the good way but it helps if you need some data in all your tests...
Another option is to use static variables.
Here is an example (for Symfony 4 functional tests):
namespace App\Tests\Controller\Api;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Hautelook\AliceBundle\PhpUnit\RefreshDatabaseTrait;
use Symfony\Component\HttpFoundation\AcceptHeader;
class BasicApiTest extends WebTestCase
{
// This trait provided by HautelookAliceBundle will take care of refreshing the database content to a known state before each test
use RefreshDatabaseTrait;
private $client = null;
/**
* #var string
*/
private const APP_TOKEN = 'token-for-tests';
/**
* #var string
*/
private static $app_user__email = 'tester+api+01#localhost';
/**
* #var string
*/
private static $app_user__pass = 'tester+app+01+password';
/**
* #var null|string
*/
private static $app_user__access_token = null;
public function test__Authentication__Login()
{
$this->client->request(
Request::METHOD_POST,
'/api/login',
[],
[],
[
'CONTENT_TYPE' => 'application/json',
'HTTP_App-Token' => self::APP_TOKEN
],
'{"user":"'.static::$app_user__email.'","pass":"'.static::$app_user__pass.'"}'
);
$response = $this->client->getResponse();
$this->assertEquals(Response::HTTP_OK, $response->getStatusCode());
$content_type = AcceptHeader::fromString($response->headers->get('Content-Type'));
$this->assertTrue($content_type->has('application/json'));
$responseData = json_decode($response->getContent(), true);
$this->assertArrayHasKey('token', $responseData);
$this->static = static::$app_user__access_token = $responseData['token'];
}
/**
* #depends test__Authentication__Login
*/
public function test__SomeOtherTest()
{
$this->client->request(
Request::METHOD_GET,
'/api/some_endpoint',
[],
[],
[
'CONTENT_TYPE' => 'application/json',
'HTTP_App-Token' => self::APP_TOKEN,
'HTTP_Authorization' => 'Bearer ' . static::$app_user__access_token
],
'{"user":"'.static::$app_user__email.'","pass":"'.static::$app_user__pass.'"}'
);
$response = $this->client->getResponse();
$this->assertEquals(Response::HTTP_OK, $response->getStatusCode());
$content_type = AcceptHeader::fromString($response->headers->get('Content-Type'));
$this->assertTrue($content_type->has('application/json'));
//...
}
}
Another (simpler) example using static variables as pointed out by BoĊĦtjan Biber:
class ClientTest extends \PHPUnit\Framework\TestCase
{
protected $client;
protected static $testClient;
// Before a test method is run, a template method called setUp() is invoked.
public function setUp() :void
{
$this->client = new \Application\models\Clients;
}
public function testInsertCustomer()
{
$testclient = array(
'name' => 'Test Client',
'responsible' => 'Responsible Test',
'payment' => 'Test payment',
'email' => 'Test Email',
'phone' => '123-456-789'
);
$this->assertTrue($this->customer->insertCustomer($CustomerTest));
}
public function testGetCustomers()
{
$this->assertIsArray($this->customer->getCustomers());
$this->assertNotEmpty($this->customer->getCustomers());
// Save test client for other methods
$clients = $this->client->getClients();
static::$testCustomer = end($customers);
}
public function testGetClient()
{
$this->assertIsArray($this->customer->getCustomer(static::$customerTest['customer_id']));
$this->assertNotEmpty($this->customer->getCustomer(static::$customerTest['customer_id']));
}
}
You can use a static variable using the method setUpBeforeClass:
protected static $sharedId;
public static function setUpBeforeClass(): void
{
self::$sharedId = random_int(100,10000);
}
And access it in you tests like this:
public function testCreateLocation() {
echo 'Shared variable = ' . self::$sharedId;
// Use the variable in your test code and asserts...
}
/The Schwartz
This worked for me perfectly fine across all tests: $this->varablename
class SignupTest extends TestCase
{
private $testemail = "registerunittest#company.com";
private $testpassword = "Mypassword";
public $testcustomerid = 123;
private $testcountrycode = "+1";
private $testphone = "5005550000";
public function setUp(): void
{
parent::setUp();
}
public function tearDown(): void
{
parent::tearDown();
}
public function testSignup()
{
$this->assertEquals("5005550000", $this->testphone;
}
}

ACL Ressource (Controller)

i just implemented ACL in my Zend Framework which already uses Zend Auth.
I want to give access to some controllers and tried it this way:
$roleGuest = new Zend_Acl_Role('guest');
$this->addRole($roleGuest);
$this->addRole(new Zend_Acl_Role('supplier'));
$this->addRole(new Zend_Acl_Role('admin'));
$this->add(new Zend_Acl_Resource('Articles'));
$this->add(new Zend_Acl_Resource('Index'));
$this->deny();
$this->allow('supplier', 'Articles');
$this->allow('admin', null);
But a user, who is supplier (he is really :)) is not able to see the Controller Articles.
What am I doing wrong?
Thanks for help.
BR frgtv10
I think the best solution is to create a plugin and write something like this
class Application_Controller_Plugin_AclManager extends Zend_Controller_Plugin_Abstract
{
public function preDispatch(Zend_Controller_Request_Abstract $Request)
{
$AclManager = new Zend_Acl();
$AclManager->addRole(new Zend_Acl_Role('Guest'));
$AclManager->addRole(new Zend_Acl_Role('Supplier'), 'Guest');
$AclManager->addResource(new Zend_Acl_Resource('controller1'));
$AclManager->addResource(new Zend_Acl_Resource('controller2'));
$AclManager->addResource(new Zend_Acl_Resource('controller3'));
$AclManager->allow('Guest', 'controller1', 'index');
$AclManager->allow('Supplier', 'controller2');
$AclManager->allow('Supplier', 'controller3');
It will work great. In addition you can write
if (! $AclManager->isAllowed(USER_ROLE, $Request->getControllerName(), $Request->getActionName()))
{
$this->getResponse()->setRedirect(SOME_URL_TO_REDIRECT);
}
The approach from user707795 is good. I build up my resources with Pike_Reflection_Resource to automaticly define your resources. It's not perfectly documented yet but usage is very simple:
You download the latest version of the Pike library
http://code.google.com/p/php-pike/
Then you create a ACL class which extends Zend_Acl:
<?php
class Application_Acl extends Zend_Acl
{
/**
* Constructor
*/
public function __construct()
{
$this->_addRoles();
$this->_addResources();
$this->_setAuthorization();
}
/**
* Adds roles to ACL
*/
protected function _addRoles()
{
/**
* Get your roles from the application config here or the database like below (Doctrine2)
*/
// $repository = $this->_em->getRepository('BestBuy\Entity\Usergroup');
$roles = array('guest', 'admin', 'moderator');
foreach($roles as $role) {
$this->addRole(new Zend_Acl_Role(strtolower($role)));
}
}
/**
* Adds resources to ACL
*
* Here are resources added to the ACL. You don't have to do this manually
* because Pike_Reflection_Resource will search automaticly in your controller
* directories to define which actions there are and adds every resource as:
* modulename_controller_actionname all lowercase.
*/
public function _addResources()
{
$resourceReflection = new Pike_Reflection_Resource();
$resources = $resourceReflection->toFlatArray('default');
foreach ($resources as $resource => $humanValue) {
$this->addResource(new Zend_Acl_Resource($resource));
}
}
/**
* Sets authorization
*/
public function _setAuthorization()
{
//$permissions = $this->_em->getRepository('BestBuy\Entity\Permission')->findAll();
/**
* I retrieve my permissions here from the database but you could receive the
* from the roles attribute too:
*/
$resourceReflection = new Pike_Reflection_Resource();
$resources = $resourceReflection->toArray('default');
foreach ($resources as $moduleName => $controllers) {
foreach($controllers as $controllerName=>$actions) {
foreach($actions as $actionName=>$action) {
$resourceName = sprintf('%s_%s_%s',
strtolower($moduleName),
strtolower($controllerName),
strtolower($actionName)
);
if(isset($action['roles'])) {
foreach($action['roles'] as $role) {
if ($this->hasRole($role) && $this->has($resourceName)) {
$this->allow($role, $resourceName);
}
}
}
}
}
}
}
}
?>
Then you set up a frontcontroller plugin something just like above:
<?php
class Application_Controller_Plugin_Authorization extends Zend_Controller_Plugin_Abstract
{
/**
* Request
*
* #var Zend_Controller_Request_Abstract
*/
protected $_request;
/**
* ACL
*
* #var Buza_Acl
*/
protected $_acl;
/**
* Called before Zend_Controller_Front enters its dispatch loop.
*
* #param Zend_Controller_Request_Abstract $request
* #return void
*/
public function dispatchLoopStartup(Zend_Controller_Request_Abstract $request)
{
$this->_request = $request;
$this->_acl = new Application_Acl();
Zend_Registry::set('acl', $this->_acl);
$this->_checkAuthorization();
}
/**
* Checks if the current user is authorized
*/
protected function _checkAuthorization()
{
$allowed = false;
$currentResource = sprintf('%s_%s_%s',
strtolower($this->_request->getModuleName()),
strtolower($this->_request->getControllerName()),
strtolower($this->_request->getActionName())
);
if(Zend_Auth::getInstance()->hasIdentity()) {
$user = Zend_Auth::getInstance()->getIdentity());
$identityRole = strtolower($user->getRole()); //make sure you implement this function on your user class/identity!
} else {
$identityRole = 'guest';
}
if ($this->_acl->hasRole($identityRole) && $this->_acl->has($currentResource)) {
if ($this->_acl->isAllowed($identityRole, $currentResource)) {
$allowed = true;
}
}
if ($allowed !== true) {
throw new Zend_Controller_Exception('No permission', 403);
}
}
}
?>
Finally in your controller/actions you define your permissions as follows:
<?php
class IndexController extends Zend_Controller_Action {
/**
* #human Some description for the permissions of this action
* #roles guest|admin|moderator
*/
public function indexAction() {
}
/**
* #human Only for admins!
* #roles admin
*/
public function secretAction() {
}
}
?>
This approach is the best and set-up for small applications. For applications where you want to define the allowed actions in the interface of the application you should leave the roles tag and get the permissions for the database.
Please be aware that code below is not tested but with some reviewing it will work and you can control your permissions in the code.

Idea discussion: dynamic view script switching in zend MVC implementation [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 11 years ago.
This is basically the "Am i doing it right?" question.
I have an idea how i can transparently switch views for default/mobile version/admin areas at run time. And I would like to know what pros and cons you see in this approach.
Main requirements are:
switch entire application with
little to no coding
integrate into zend MVC workflow,
not overwrite it
fallback to default
preserve standard functionality
controllers shouldn't be aware of
changes
Here is my pseudohelper
class Xrks_Controller_Action_Helper_VrExtension extends Zend_Controller_Action_Helper_Abstract
{
public function postDispatch()
{
if(!$this->_shouldRender()) {
return; //just skip
}
try {
$vr = $this->_getViewRenderer();
$backupView = clone $vr->view;
$this->_setBasePaths(); //set base path(s) through ViewRenderer::initView($path)
$oldSpecArray = $this->_setVrPathSpecs(); //set VR view script path specs
$vr->render();
$vr->setNoRender(true); //disable renderer
} catch(Zend_View_Exception $e) { //fallback to default viewscripts if view script file not found
$vr->setView($backupView); //restore view on error
} catch(Exception $e) {
$vr->setView($backupView); //restore view on error
$this->_setVrPathSpecs($oldSpecArray); //restore script path spec
throw $e;
}
$this->_setVrPathSpecs($oldSpecArray);//restore script path spec
}
/**
* Same functionality as ViewRenderer helper _shouldRender method
* #return boolean
*/
protected function _shouldRender();
/**
* #return Zend_Controller_Action_Helper_ViewRenderer
*/
protected function _getViewRenderer();
/**
* Sets viewRenderer path specifications
*
* #param array $spec if NULL uses $this->_viewRendererPathSpecs
* #return array old path spec (0 => pathSpec, 1 => pathNoControllerSpec)
*/
protected function _setVrPathSpecs(array $spec = NULL);
}
How exactly helper should be configured is not important and that part skipped
Here is example how it supposed to work:
$this->_setBasePaths(); sets view base paths to application/views/default/ and application/views/admin/
$this->_setVrPathSpecs(); set path specification to ':module/:controller/:action.:suffix'
so for foo-baz-bar it will search at
1. application/views/admin/scripts/foo/baz/bar.phtml
2. application/views/default/scripts/foo/baz/bar.phtml
if view script not found fall back to default ViewRenderer:
3. application/modules/foo/views/scripts/baz/bar.phtml
Ask questions if I missed something
Upd: After some research i decided to use action helper to autoregister view scriptPaths based on specification for inflector and specified variables. I also modified partial helpers to register scriptPaths if partial from other module requested.
This is crude but working version of action helper:
class Xrks_Controller_Action_Helper_ViewRendererPathstack extends Zend_Controller_Action_Helper_Abstract
{
const PATH_APPEND = 'append';
const PATH_PREPEND = 'prepend';
protected $_enabled = FALSE;
protected $_viewScriptPaths = array();
/**
* By default following vars available: baseDir, area, theme, module
* #var string
*/
protected $_viewScriptPathSpec = ':baseDir/:area/:module';
protected $_defaults = array(
'area' => 'frontend',
'theme' => 'default',
);
protected $_vars = array();
protected $_inflector;
protected $_viewRenderer;
public function __construct($baseDir = NULL)
{
if($baseDir == NULL) {
$baseDir = APPLICATION_PATH . DS . 'views';
}
$this->setDefaultVar('baseDir', $baseDir);
$this->addPath(array());
}
/**
* Enter description here ...
* #return Zend_Controller_Action_Helper_ViewRenderer
*/
protected function _getViewRenderer()
{
if(!$this->_viewRenderer) {
$this->_viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
}
return $this->_viewRenderer;
}
/**
* Should the ViewRenderer render a view script?
*
* #return boolean
*/
protected function _shouldRender()
{
$vR = $this->_getViewRenderer();
return (!$this->getFrontController()->getParam('noViewRenderer')
&& !$vR->getNeverRender()
&& !$vR->getNoRender()
&& (null !== $vR->getActionController())
&& $vR->getRequest()->isDispatched()
&& !$vR->getResponse()->isRedirect()
);
}
public function generatePaths(array $vars = array())
{
$this->_registerVarsWithInflector();
$vars = array_merge($this->_defaults, $this->_vars, $vars);
$inflector = $this->getInflector();
$generatedPaths = array();
foreach($this->_viewScriptPaths as $path) {
$pathVars = array_merge($vars, $path);
$generatedPaths[] = $inflector->filter($pathVars);
}
return array_reverse(array_unique(array_reverse($generatedPaths)));//last occurence more important than first
// array('test', 'test2', 'test') => array('test2', 'test')
// #todo rethink this code piece later. must be better solution
}
protected function _registerVarsWithInflector()
{
$vars = array_merge($this->_defaults, $this->_vars);
$inflector = $this->getInflector();
$unregistered = array_keys(array_diff_key($vars, $inflector->getRules()));
sort($unregistered, SORT_DESC);//more specific first (moduleDir prior to module key)
foreach($unregistered as $var) {
$inflector->addFilterRule($var, array('Word_CamelCaseToDash', 'StringToLower'));
}
}
protected function _viewAddScriptPaths(Zend_View_Abstract $view, $paths)
{
foreach ($paths as $path) {
$view->addScriptPath($path);
}
}
/**
* Get inflector
*
* #return Zend_Filter_Inflector
*/
public function getInflector()
{
if (null === $this->_inflector) {
$this->_inflector = new Zend_Filter_Inflector();
$this->_inflector->setThrowTargetExceptionsOn(true);
//setup default rules
$this->_inflector->addRules(array(
':baseDir' => array(),
))
->setTargetReference($this->_viewScriptPathSpec);
}
return $this->_inflector;
}
/**
*
* #return array
*/
public function getPaths()
{
return $this->_basePaths;
}
public function getEnabled()
{
return $this->_enabled;
}
public function setEnabled($flag = TRUE)
{
$this->_enabled = (bool)$flag;
return $this;
}
/**
*
* #todo add check for $pathVars keys and values validity
* #param array $pathVars associative array
* #param string $placement either append or prepend
* #return Xrks_Controller_Action_Helper_ViewRendererPathstack
*/
public function addPath(array $pathVars, $placement = self::PATH_APPEND)
{
if($placement == self::PATH_PREPEND) {
array_unshift($this->_viewScriptPaths, $pathVars);
} else {
$this->_viewScriptPaths[] = $pathVars;
}
return $this;
}
/**
*
* #param array|Zend_Config $paths
* #param string $placement either append or prepend
* #return Xrks_Controller_Action_Helper_ViewRendererPathstack
* #throws Xrks_Exception
*/
public function addPaths($paths, $placement = self::PATH_APPEND)
{
if($paths instanceof Zend_Config) {
$paths = $paths->toArray();
} elseif (!is_array($paths)) {
throw new Xrks_Exception('$paths should be either array or instance of Zend_Config');
}
if($placement == self::PATH_PREPEND) {
$paths = array_reverse($paths);
}
foreach($paths as $path) {
$this->addPath((array)$path, $placement);
}
return $this;
}
/**
*
* #param array $pathVars associative array
* #return Xrks_Controller_Action_Helper_ViewRendererPathstack
*/
public function setPath(array $pathVars)
{
$this->_basePaths = array();
$this->addPath($pathVars);
return $this;
}
/**
*
* #param array|Zend_Config $paths
* #return Xrks_Controller_Action_Helper_ViewRendererPathstack
* #throws Xrks_Exception
*/
public function setPaths($paths)
{
$this->_basePaths = array();
$this->addPaths($paths);
return $this;
}
/**
*
* #param string $varName
* #return string |NULL
*/
public function getDefaultVar($varName)
{
if(key_exists($varName, $this->_defaults)) {
return $this->_defaults[$varName];
}
return NULL;
}
/**
* #param string $varName
* #param string $value
* #return Xrks_Controller_Action_Helper_ViewRendererPathstack Provides fluent interface
*/
public function setDefaultVar($varName, $value)
{
$this->_defaults[$varName] = (string)$value;
return $this;
}
/**
*
* #param string $name
* #return string |NULL
*/
public function getVar($name, $defaults = false)
{
if(key_exists($name, $this->_vars)) {
return $this->_vars[$name];
}
return $defaults ? $this->getDefaultVar($name) : NULL;
}
/**
* #param string $varName
* #param string $value
* #return Xrks_Controller_Action_Helper_ViewRendererPathstack Provides fluent interface
*/
public function setVar($varName, $value)
{
$this->_vars[$varName] = $value;
return $this;
}
public function unsetVar($name)
{
if(key_exists($name, $this->_vars)) {
unset($this->_vars[$name]);
}
return $this;
}
public function postDispatch()
{
if(!$this->getEnabled() || !$this->_shouldRender()) {
return; //just skip
}
try {
$vr = $this->_getViewRenderer();
$this->setVar('module', $vr->getModule());
$paths = $this->generatePaths();
$this->_viewAddScriptPaths($vr->view, $paths);
if(Zend_Registry::isRegistered('Zend_Log')) {
Zend_Registry::get('Zend_Log')
->log($paths, Zend_Log::DEBUG);
}
} catch(Exception $e) {
if(Zend_Registry::isRegistered('Zend_Log')) {
Zend_Registry::get('Zend_Log')
->log($e, Zend_Log::WARN);
}
throw $e;
}
}
}
The way I usually handle this:
I register a Layout Plugin, extending Zend_Layout_Controller_Plugin_Layout
I use the preDispatch hook to determine what module, controller, action I am in
I switch between layouts and views depending on the context
For me, that's by far the easiest method.
GJ