ZF2: Switching Layouts Based on Idenitity - which event to use - layout

To date I have had a single layout in my application, configured in module.config.php as follows:
'view_manager' => array(
'template_map' => array(
'layout/layout' => __DIR__ . '/../view/layout/layout.phtml',
),
I am now trying to switch the layout based on whether a user is logged in or not. This information is available in my Core\Auth service, and I am using it in my module.php as follows:
public function onBootstrap(MvcEvent $e)
{
...
$eventManager->attach('dispatch', function ($e) {
$application = $e->getApplication();
$auth = $application->getServiceManager()->get('Core\Auth');
if (!$auth->isLoggedIn()) {
$viewModel = $e->getViewModel();
$viewModel->setTemplate('layout/public');
}
}, -1000000);
}
The problem is that I don't know which event to attach it to, and which priority to use. If I attach it to:...
...the bootstrap or route event, it gets overridden
...the dispatch event with a high priority it gets overridden
...the dispatch event with a low priority, it overrides any layout changes that controller actions might make, e.g. $view->setTerminal(true)
...the render event, (again) it overrides changes made by actions
Where should I be attaching it? And with what priority?

Opps, my mistake.
I was doing the same isLoggedIn() test in my index/index action, which was muddying the results. I have removed it and attached the layout switch to the route event, default priority, and so far it works fine!

Related

How to Conditionally remove Block or Container from Layout programatically?

If any one want to remove container (block) like product.info.main from Product Detail Page based on certain conditions or product has attribute with value assigned.
Then what is the best approach for achieving This?
Thanks
We can use Event Observer approach...
In YOUR_VENDOR\YOUR_MODULE\etc\frontend\events.xml file, need to add below code:
<event name="layout_generate_blocks_after">
<observer name="personalize-theme-pdp-customize" instance="YOUR_VENDOR\YOUR_MODULE\Observer\ApplyThemeCustomizationObserver" />
</event>
And in YOUR_VENDOR\YOUR_MODULE\Observer\ApplyThemeCustomizationObserver.php file, need to add below code:
public function execute(Observer $observer)
{
$action = $observer->getData('full_action_name');
if ($action !== 'catalog_product_view') {
return;
}
$product = $this->_registry->registry('product');
if ($product) {
$attribute = $product->getCustomAttribute('g3d_app_url_default');
if ($attribute && $attribute->getValue()) {
/** #var \Magento\Framework\View\Layout $layout */
$layout = $observer->getData('layout');
$layout->unsetElement('product.info.main');
}
}
}
Using a site wide event to remove a container/block from a specific page is overkill and not the best approach because your condition will be evaluated with every page load, adding a slight overhead to all the pages.
Removing a container/block from a specific page is best achieved by creating an after plugin for the execute method of the controller of the page where you want to remove the container/block. With this approach your condition is only executed when the intended page is loaded.
public function afterExecute(\Magento\[Module]\Controller\[ControllerName] $subject, $result)
{
if ([your condition]) {
$result->getLayout()->unsetElement('name_of_container_or_block');
}
return $result;
}

Child-view gets rendered twice

I've noticed that my child-view gets rendered twice for some reasons.
Here is a simple test-case:
Let's assume I've two controllers, ControllerA and ControllerB. I'm forwarding the action forwardTestAction from ControllerA to ControllerB and display the view returned from ControllerB as a child-view.
ControllerA::forwardTestAction()
public function forwardTestAction()
{
$childView = $this->forward()->dispatch(ControllerB::class, [
'action' => 'forward-test',
]);
$mainView = new ViewModel();
$mainView->addChild($childView, 'content');
return $mainView;
}
ControllerB::forwardTestAction()
public function forwardTestAction()
{
$number = new \stdClass();
$number->a = 10.4;
$view = new ViewModel([
'number' => $number,
]);
return $view;
}
Template forward-test.phtml of ControllerA
<?= $content; ?>
Template forward-test.phtml of ControllerB
<?php
$number->a = $this->currencyFormat($number->a);
echo $number->a;
The currencyFormat plugin throws an exception, because number gets formatted twice. The reason is because the child-view itself gets rendered twice.
That's a really weird behaviour and causes not only performance issues but also many other problems e.g. numberFormatting etc.
I'm doing something wrong?
Also I should mention that the html only gets outputted once, but the template gets rendered twice. Maybe it's just a weird constellation of my current setup. I also posted an issue on Github and they were not able to reproduce it.

validation on unbound form field

I have a form with an extra unbound field added using 'property_path' => false.
I would like to have a simple validation on this field and I've found many answers that suggest to use something like
$builder->addValidator(...);
but I've seen that in symfony 2.1 $builder->addValidator is deprecated. Does anyone know what is the correct way to put a validation on an unbound field in Symfony 2.1?
Edit :
I've just made a more complete answer on topic Symfony validate form with mapped false form fields
Original answer
Validating unbound (non mapped) field in a form is not well documented and the fast evolving form and validator components make the few examples obsoletes (for Symfony 2.1.2).
For now I succeed in validated non mapped fields with event listener.
Here is my simplified code :
namespace Dj\TestBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Symfony\Component\Form\FormEvents;
use Dj\TestBundle\Form\EventListener\NewPostListener;
class PostType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('lineNumber', 'choice', array(
'label' => 'How many lines :',
'choices' => array(
3 => '3 lines',
6 => '6 lines'
),
// 'data' => 3, // default value
'expanded' => true,
'mapped' => false
))
->add('body', 'textarea', array(
'label' => 'Your text',
'max_length' => 120));
// this listener will validate form datas
$listener = new NewPostListener;
$builder->addEventListener(FormEvents::POST_BIND, array($listener, 'postBind'));
}
// ... other methods
}
And the event listener :
namespace Dj\TestBundle\Form\EventListener;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormError;
/**
* listener used at Post creation time to validate non mapped fields
*/
class NewPostListener
{
/**
* Validates lineNumber and body length
* #param \Symfony\Component\Form\FormEvent $event
*/
public function postBind(FormEvent $event)
{
$form = $event->getForm();
$data = $event->getData();
if (!isset($data->lineNumber)) {
$msg = 'Please give a correct line number';
$form->get('lineNumber')->addError(new FormError($msg));
}
// ... other validations
}
}
This is how I validate my non mapped fields until I find out how to do this with validators.
I have the same problem from the docs we get this:
The interface FormValidatorInterface was deprecated and will be
removed in Symfony 2.3. If you implemented custom validators using
this interface, you can substitute them by event listeners listening
to the FormEvents::POST_BIND (or any other of the *BIND events). In
case you used the CallbackValidator class, you should now pass the
callback directly to addEventListener.
This suggest to use event listeners but I have not found and example.
https://github.com/symfony/symfony/blob/master/UPGRADE-2.1.md

How to validate a POST of a non standard action in Cake PHP 1.3

Using CakePHP 1.3
I have a controller with a non standard action name - say:
class WidgetsController extends AppController {
function modifyColor($id = null) {
// Some code that modifies the background color of a widget
}
}
and a companion view views/widgets/modifyColor.ctp
The modifyColor template POSTS to the action:
echo $this->Form->create('User',array('url' => array('controller' => 'widgets', 'action' => 'modifyColor')));
I get a 404 on the POST since the CakePHP Security component is trying to validate the form
and I would like to be able to validate the POST.
The only way I can get this to work seems to be to turn off POST validation
if ($this->action == 'modifyColor') {
$this->Security->validatePost = false;
}
This seems a bad solution.
How do I use the Security component on a non standard action?
allowedActions doesn't seem to work
Thanks
Danny
Answering my own question.
A. There is no problem with using any-named actions in CakePHP
B. A conceptual bug with CakePHP related to using the same function for the Form GET and Form POST
On the Form GET I had this:
if (empty($this->data)) {
$this->data = $this->Widget->read(null, $id);
}
The Form itself had some code like this:
echo $this->Form->input('id');
echo $this->formView('Current color', 'CurrentColor');
echo $this->formView('New color', 'NewColor');
echo $this->formView('New background color', 'BackgrdColor');
Which was fine, except that none of these fields appear in the Widget model - and the CakePHP Security component interprets this as a sort of XSRF attack - since it is finding fields in the form that don't belong to the model. That's why:
$this->Security->validatePost = false;
solved the "problem".
The correct solution is simply not to populate $this->data with the model in the controller action and handle the field assignments on the POST:
function modcolor () {
$this->layout = 'app_ui_listview';
if (!empty($this->data)) {
$id = $this->Session->read('Auth.User.id');
$u = $this->Widget->read(null, $id);
// Assign fields from the form to the model....
}
}
the problem is you are trying to pass url and you are trying to pass the controller stuff too.
echo $this->Form->create('Widget', array('controller' => 'widgets', 'action' => 'modifyColor'));
or
echo $this->Form->create('Widget', array('url' => '/widgets/modifyColor'));

Kohana 3: How can I pass full control to another action within my controller?

In my controller, I have a before() function that calls parent::before() and then does some additional processing once the parent returns. based on a specific condition, I want to "save" the original request and pass execution to a specific action. Here is my before() function.
public function before() {
parent::before();
$this->uri = Request::Instance()->uri;
$match = ORM::factory('survey_tester')
->where('eid','=',$this->template->user->samaccountname)
->find();
if (!$match->loaded()) {
self::action_tester("add",$this->template->user);
}
}
And the action that is being called..
public function action_tester($op=null,$user=null) {
$testers = ORM::factory('survey_tester')->find_all();
$tester = array();
$this->template->title = 'Some new title';
$this->template->styles = array('assets/css/survey/survey.css' => 'screen');
$this->template->scripts = array('assets/js/survey/tester.js');
$tester['title'] = $this->template->title;
$tester['user'] = $this->template->user;
switch ($op) {
case "add":
$tester = ORM::factory('survey_tester');
$tester->name = $user->displayname;
$tester->email = $user->mail;
$tester->division = $user->division;
$tester->eid = $user->samaccountname;
if ($tester->save()) {
$this->template->content = new View('pages/survey/tester_add', $admin);
} else {
$this->template->content = new View('pages/survey/tester_error', $admin);
}
break;
default:
break;
}
}
This all seems to work fine. This is designed to prompt the user for a specific piece of information that is not provided by $user (populated by LDAP) if this is the first time they are hitting the controller for any reason.
The problem is the views are not rendering. Instead control passes back to whatever action was originally requested. This controller is called survey. If i browse to http://my.site.com/survey and login with new user info, the record gets written and i get the action_index views instead of my action_tester views.
I cannot figure out what I am doing wrong here. Any ideas will be appreciated. Thank you.
EDIT: I managed to get this working (sort-of) by using $this->request->action = 'tester'; but I'm not sure how to add/set new params for the request yet.
The issue is that you are calling your method (action_tester), but then Kohana is still going to call the original action after the before method is called, which is going to change the response content overwriting the changed made in action_tester().
You can change the action being called (after before is called) inside your before() method:
$this->request->action('action_tester');
After the before method is called, it should then call the new Action (action_tester) rather than the old one, but then you need to do something about the way you are passing your parameters then.
Or you could just redirect the request upon some condition:
if($something) {
$this->request->redirect('controller/tester');
}
This doesn't seem like a nice way to do it anyway.

Resources