Access RouteMatch-Object in ViewHelper - model-view-controller

In my Project, i created a ViewHelper, to display a form on every Page. If the user submit the form the Post information always posted to the same controller. After the Controller handles the post, the user should redirected to the page, where he submitted the form.
To redirect the dynamically i want to submit the controller and action via hidden elements. But i have no clue, how i can set them in the ViewHelper. I tried it with the InjectApplicationEventInterface, but my member variable was empty, so i guess its only work for controllers and not for helper.
So how can i access the RouteMatch Object?

The Zend\View\HelperPluginManager is the service locator that keeps your view helpers, and it contains no instance of the application event initializer, but it has a reference to the main service locator, since it is a plugin manager. Consider implementing following in your helper:
public function __construct(\Zend\Mvc\MvcEvent $mvcEvent)
{
// injecting the mvc event, since $mvcEvent->getRouteMatch() may be null
$this->mvcEvent = $mvcEvent;
}
Then, in your module's getViewHelperConfig (implementation of ViewHelperProviderInterface) you define following:
public function getViewHelperConfig()
{
return array(
'factories' => array(
'myHelper' => function (\Zend\ServiceManager\AbstractPluginManager $pm) {
$application = $pm->getServiceLocator()->get('Application');
return new MyViewHelper($application->getMvcEvent());
},
),
);
}

Related

Cakephp 3 View variables set in App controller not available when rendering specific template / AJAX requests

I am setting few view variables within my App controller such as company name, address, contact information which changes based on sub domains so that they are available throughout all view templates. However I am struggling to identify why the are not available when making ajax request.
//App Controller beforeFilter
$this->set('company', 'Test Company');
$this->set('address', '14 Test Street, Test, TE5 3ST');
$this->set('email', 'test#test.com');
Above variable are available for all none ajax i.e when I am not rendering specific template request however for below example request I am not able to access those variables in test_data template.
function _ajaxGetTestData()
{
$view = new View();
$content = $view->render('Home/Ajax/test_data');
$response['content'] = $content;
$response['success'] = TRUE;
$this->set(compact('response'));
$this->set('_serialize', ['response']);
}
That is because you did not serialize the variables from the app controller.
You can try in your method:
$response['company'] = $company;
$response['address'] = $address;
$response['email'] = $email;
Or https://book.cakephp.org/3/en/views/json-and-xml-views.html#using-a-data-view-with-template-files
You have to set view variables before render is called.
$this->set('data');
$this->render('custom_view');
When you call $this->set it sets the view variables on the Controller class. These variables are eventually passed to the View Builder, which creates a new View class and returns a Result containing the HTML for this new View.
When you want to render your own View manually you need to pass it the view variables manually too - $this->set isn't setting view variables in this new View class you created here:
$view = new View();
$content = $view->render('Home/Ajax/test_data'); // Has nothing to do with $this->set, you'd have to pass the variables in manually
This isn't generally the simplest approach to take to render an AJAX view.
While you can generally continue to use $this->set in beforeFilter as you already are:
public function beforeFilter(Event $event)
{
$this->set('company', 'Test Company');
$this->set('address', '14 Test Street, Test, TE5 3ST');
$this->set('email', 'test#test.com');
}
.. the easiest method to make an AJAX-compatible is to enable the JSON/XML handler let the built-in JSON/XML renderers do their magic.
In the action function (index/view/edit/whatever) just include the company/address/email in the _serialize variable.
For example, a "view" function might look like:
public function view($id = null)
{
// Do regular view stuff:
$entity = $this->MyTable->get($id);
$this->set('entity', $entity);
// Include ALL the variables you want in the response in _serialize:
$this->set('_serialize', ['entity', 'company','address', 'email']);
}
If you are sure you need a custom template (which isn't required), don't render it manually, just set the template when AJAX is detected:
if($this->request->is('ajax')){
$this->viewBuilder()->setTemplate('Home/Ajax/test_data');
}
This will automatically be rendered for you, using the variables you set with $this->set.
If you want to make a global custom template (for example, wrapping all your data with a "response" node), for all AJAX requests, use a new Layout instead of a custom template:
if($this->request->is('ajax')){
$this->viewBuilder()->setLayout('custom_json');
}
Create this layout in src/Template/Layout/custom_json.ctp and format it as you wish, for example:
<?php
/**
* #var \App\View\AppView $this
*/
?>
{"response": <?= $this->fetch('content') ?> }
See from docs:
Enabling Data Views https://book.cakephp.org/3/en/views/json-and-xml-views.html#enabling-data-views-in-your-application
Using the _serialize Key https://book.cakephp.org/3/en/views/json-and-xml-views.html#using-data-views-with-the-serialize-key
Custom Layouts https://book.cakephp.org/3/en/views.html#layouts

View from custom controller overriding default view

I'm using orchardcms 1.9 (no tag created jet). I am writing a custom module that implements its own controller that calles a service wich check some information and based on the service response I either redirect or let the user stay on the page.
The module is on the default layer in other words it is on everypage. So when user tries to log in or register this module checks information normally.
This is my route:
new RouteDescriptor {
Priority = -1,
Route = new Route(
"{*path}", // this is the name of the page url
new RouteValueDictionary {
{"area", "modulename"}, // this is the name of your module
{"controller", "controllername"},
{"action", "Redirect"}
},
new RouteValueDictionary(),
new RouteValueDictionary {
{"area", "modulename"} // this is the name of your module
},
new MvcRouteHandler())
and this is my controller:
public ActionResult Redirect()
{
String response = _authService.VerifyRegistration(_orchardServices.WorkContext.CurrentUser);
if (response.Equals("2"))
{
Response.Redirect("~/Registration");
}
else if (response.Equals("3"))
{
Response.Redirect("~/Users/Account/LogOn");
}
return View();
}
What happens is that when I go to registration or login controller triggers, checks the infromation, says no redirect needed then returns view. But because the view is empty my page is blank instead of its default login/registration form.
How can I solve this? Am I making a mistake in routing that I somehow override the default view (I tried different priorities but same response).
Your route overrides all routes ({*path}. So when you redirect, you redirect to....your redirector I guess. Therefore the view you are rendering is the one for your controller, not the page you were after.
Whatever the logic flaw - this is not a good way to globally control authorization type scenarios on your site. If you meant to have a single page that people might go to (e.g. http://www.mysite/welcome) then your problem is that your route is too global. However, if, as your code suggests that you want to create a "all pages" check to see if you should go to login or register, then you should implement an authorization filter. An example of an authorization filter (for a slightly different purpose) can be found at https://stackoverflow.com/a/30377097/1638254 . You are looking to fill in the OnAuthorization method with suitable code to redirect the user (or let them through

Where to put filter form code in zf2 MVC pattern

I’ve added a small form to an index view to allow users to filter the data. I have placed the following code for the form inside the controller, but I question whether this is the right place to put it.
// ...
public function indexAction()
// ...
// build group list
$groupList = array(
0 => 'all',
1 => 'short people',
2 => 'tall people',
3 => 'fun people',
4 => 'boring people',
);
// create group selection box
$groupSelect = new Element\Select('group');
$groupSelect->setValueOptions($groupList);
$groupSelect->setAttributes(array(
'onChange' => 'this.form.submit()',
));
// create filter form
$form = new Form('group-filter');
$form->add($groupSelect);
$form->setData(array(
'group' => $group,
));
// process the form
$request = $this->getRequest();
if ($request->isPost()) {
$groupSelection = $request->getPost('group', 0);
return $this->redirect()->toRoute('admin-members', array('group'=>$groupSelection,));
}
// ...
Following the MVC pattern, does all of this code belong in the controller?
Nope it does not belong in the controller. Create a new form class (that extends Zend\Form\Form) and inject into the controller class. You can do that through the controllers factory, either through a factory class or the anonymous function "factory".
Other way to do it would be to get it (the form you created) in the controller from the service manager, but as far I know that's not the recommended method anymore, even though it still in the ZF2 docs.
That way your form code will be separated from the controller code, not mixing with the actual controller logic and, in the former case, also more easily testable.
You can learn more from this ZF2 forum thread. It's lengthy, but there are code samples and lead devs from ZF2 team are weighing in.

How to serialize ZF2 form object to JSON?

I'm using zf2 form object on the server and ajax code on the client to implement my registration form.
I post the form values in the ajax request, no problem, and the form gets them fine with
$form->setData($request->getPost());
After I validate the form and perform the registration on the server, I want to send the form back to the client, especially if there are errors, so I can show them to the user.
I'm looking for a standard way using zend or any plugin to serialise the form object into JSON format, so I can send it in the response to the AJAX call.
Any idea?
Well what you can do is run the validation on your form and after that you will return your form within a new JsonModel.
Here is a little example of how to handle your controller:
class RegistrationController extends AbstractActionController
{
public function RegisterAction()
{
$form = new RegisterForm();
$form->setInputFilter(new RegisterInputFilter());
if ($this->getRequest()->isPost()) {
$form->setData($this->getRequest()->getPost());
if($form->isValid()) {
// Handle your registration as the form is valid!
// return to some path after registration is complete.
// Show user he registered succesfully, etc. ;)
}
// Checks if the request is from JavaScript
if($this->getRequest()->isXmlHttpRequest()) {
return new JsonModel(array('registerForm' => $form));
}
}
return new ViewModel(array('registerForm' => $form));
}
}
Notice that the form object is holding all the invalid inputs including its message after validation.
I would take another approach just to completely render the ViewModel again so you can display the validation message much easier. On the side you could add Client Side (Javascript) validation as it's much more user-friendly, but that is just some fancy shizzle I would do ;) In case of rendering the ViewModel:
use Zend\View\Renderer\PhpRenderer;
if($this->getRequest()->isXmlHttpRequest()) {
$renderer = new PhpRenderer;
$registerViewModel = new ViewMOdel();
$registerViewModel->setTemplate('view/register.phtml');
return new JsonModel(array('registerViewModel' => $renderer->render($registerViewModel));
}
Note that not setting a template to your viewModel will result in ZF2 getting the default of the action (view/moduleName/registration/register.phtml) you are in! So in your case you don't need to use PhpRenderrer::setTemplate(). But I just hand it to you so you can change it if you are using any other file.
So now you will receive Json from our controller, in your javascript. Retrieve the new ViewModel from Json and remove the old ViewModel and replace it with the new. By removing the old, you also remove any Javascript that is bound to any element within the viewModel, so you might set the events on your body within your javascript or have it on your attributes in Form/RegistrationForm.
Hope this pushes you in the right direction.

Pass in id using the index action - CakePHP 2.1

I have a controller in my CakePHP app called 'Profiles', and this is what my action called index is:
function index($id = NULL) {
$this->set('profile', $this->Profile->findById($id));
}
So using this method to pass in the id, I can then return the correct profile depending on the id that is in the link.
This works fine when I do this on an action called view, however when I try to do it on the index action it treats my id as if it were an action, and returns an error saying that the action doesn't exist. Is there a way to pass in the id on the index action in CakePHP, or does it have to be on an action besides the index such as a view action?
Cheers,
Adam.
If you want to pass an id to your method you have to visit a url with an id in it.
/profiles/index/<id_here>
Cake will load /profiles as /profiles/index magically. You can not use /profiles/<id> out the box, that would require a new Route before it would work.
If you used the Bake function to create your controller, the Index method would be used to display several records from the Profile model.
Is the three lines of code you displayed in your question the actual code from your controller?
This can be done with a custom route. Pass a parameter to the action...
Router::connect('/mycontroller/:id', array('controller' => 'mycontroller', 'action' => 'index'), array('pass' => array('id') ) );
and in your controller...
public function index( $id )
http://book.cakephp.org/3.0/en/development/routing.html#passing-parameters-to-action

Resources