Zend Framework 2 - handling form actions - php

In Zend Framework 2, I've created a class that extends Zend\Form\Form called MyForm.
In the indexAction of one Controller class, I'll initialize MyForm like this:
public function indexAction()
{
$form = new MyForm('my-name');
$viewModel = new ViewModel(array('form' => $form));
return $viewModel;
}
Then in the corresponding view, I basically just do
$form = $this->form;
$form->prepare();
echo $this->form()->openTag($this->form);
echo $this->formCollection($form);
echo $this->form()->closeTag();
This all works, but you may have noticed that the action for the form is missing.
I have tried to add the action like this in the view:
$form->setAttribute('action', $this->url(NULL, array('controller'=>'Index', 'action' => 'go')));
Then in the go action inside my IndexController I just have this for testing:
public function goAction()
{
die('huh');
}
This did not work at all, I always land at the form view (== index action) again. Why is the go action never executed?
I also know that I could either hardcode the action attribute and let a segment route handle the processing, or I could even define an own route for that.
In what cases form actions should get their own route?
In what cases form actions should be handled using a segment route?
In what cases form actions should be handled like in my example?
If there are no hard rules for this: What intention do the different approaches communicate?
Is it also possible to add form actions in the controller instead of the view?

Continuing on from the comments: That's not how the URL helper works - if you omit the first parameter, the current route is used instead. That's probably why you're not getting the form action you expect.
Personally, I always specify the route name when using the URL helper - makes things clearer.

Related

Custom view Helper with own view script

I have created my own view helper according to this example. The view helper calls my model to create a form. The view Helper returns a form object.
My question is wether it is possible to assign a custom view script to the View Helper for the form. Or should i just use a partial for the script?
Thanks very much
View helpers should be used to help construct HTML that requires anything more that the normal control logic, the <?php if ($foo) : and <? foreach($foo....
You will want to avoid cases where you view logic becomes too complex or repetitive, as this results in hard to maintain code.
The example you posted, a <span> tag is returned based on the the number of days ($numberOfDays) arguments passed to it. This is something that is most likely repetitive to check every time you wish to output the 'new' item, hence the need for a view helper.
In your case, what would be ideal, would be to fetch the form via the FormElementManager from within the controller.
class FooController {
function someAction() {
$serviceManager = $this-getServiceLocator();
$formElementManager = $serviceManager->get('FormElementManager');
$myForm = $formElementManager->get('My\Form\Class\Name\Or\Alias');
return new \Zend\View\Model\Model(array(
'form' => $myForm
);
}
}
Calling the form from the FormElementManager will ensure its dependencies are correctly injected and init() called.
In the view you would then render the form using Zend's built in ViewHelpers (Like FormRow taking our complex form object and return all the required HTML)
With the above in place the need for you to 'assign view scripts to the form' disappears as the form can be injected, via the controller, into any view script.

Yii Route All Actions To One Method In Controller

I'm trying to make a cms like app using Yii. The functionality is going to be something like:
http://www.example.com/article/some-article-name
What I am trying to do is have everything go the method actionIndex() in the ArticleController.php, and have that one method determine how to handle the action. So my question is how can I route all actions to one method in a controller in Yii?
In your case, I think it'll be better to use either a filter or a beforeAction method.
Filter way:
Filter is a piece of code that is configured to be executed before and/or after a controller action executes.
Sample:
class SomeController extends Controller {
// ... other code ...
public function filters() {
return array(
// .. other filters ...
'mysimple', // our filter will be applied to all actions in this controller
// ... other filters ...
);
}
public function filterMysimple($filterChain) { // this is the filter code
// ... do stuff ...
$filterChain->run(); // this bit is important to let the action run
}
// ... other code ...
}
beforeAction way:
This method is invoked right before an action is to be executed (after all possible filters.) You may override this method to do last-minute preparation for the action.
Sample:
class SomeController extends Controller {
// ... other code ...
protected function beforeAction($action) {
if (parent::beforeAction($action)){
// do stuff
return true; // this line is important to let the action continue
}
return false;
}
// ... other code ...
}
As a side note, you can access the current action within a controller this way also : $this->action , to get the value of id: $this->action->id:
if($this->action->id == 'view') { // say you want to detect actionView
$this->layout = 'path/to/layout'; // say you want to set a different layout for actionView
}
Add this in the beginning of urlManager rules in the config:
'article/*' => 'article',
Your rules will have to be similar to the following :-
'rules' => array(
'<controller:\w+>/<action:\w+>' => '<controller>/<action>',
'<controller:\w+>/<action:\w+>' => 'article/index',
),
This will pass all requests to the actionIndex function in the ArticleControllerPHP Class if the Controller and/or Action does not exist.
I'm guessing you just wanna throw in a bunch of static pages in your "view" folder and have them selected and rendered automatically without adding an action for each of them in your controller.
The above suggested filters() and beforeAction() and even __construct() do not work for this purpose (filters and beforeaction do not fire up at all if the action does not exist, and __construct is very messy because if you put your functionality in __construct - at that point Yii doesnt even know which controller/action/view it should call)
however, there is a simple workaround which involves URL manager
in your config, in URL manager's rules, add one of the following lines (depending on your path settings)
'articles/<action:\w+>' => 'articles/index/action/<action>',
OR
'articles/<action:\w+>' => 'articles/index?action=<action>',
and then, in your articles controller just put up this (or similar) index action
public function actionIndex() {
$name = Yii::app()->request->getParam('action');
$this->render($name);
}
then you can call pages like /articles/myarticle or /articles/yourarticle without putting up any function in your controller. All you would need to do is just add a file named myarticle.php or yourarticle.php in your views/articles folder, and type your html content inside those files.

Using several modules in the same view

I'm developing a web application with Zend Framework 1.12, which is something new to me, and I'm not sure about the way to do something I want to.
EDIT: When I talk about Module, I mean Controller, sorry for that, I still mistake the terms ...
On my home page, the module Index, I made what I wanted to do with it, created several actions and all the stuff, but I'd like to add a search engine I'll make myself.
The problem is that I'd like to create the search engine as a separate module named Search, for example, but put the SearchForm in the home page. Hitting submit would send the datas from the form to the Search module.
I don't quite understand how to do that without having to go to /search to access my form and every associated actions.
Do I have to use a View Helper ?
Also, the searchForm in the front page would be some sort of QuicKSearch and accessing /search would show a more elaborated form for the research.
Can someone explain me how to access the searchForm from the Index module or redirect me to the part of the documentation talking about that ? My research are unsuccessful and Google doesn't help me either.
EDIT: When I talk about Module, I mean Controller, sorry for that, I still mistake the terms ...
First of all, build the searchform as viewHelper, then you can reuse it in several views.
The action attribute in form snippet set to searchModule/controller/action.
Additionaly make research about viewHelpers and Forms in Zend Documentation.
I actually prefer to do this as a an action helper and then just use a standard placeholder view helper to present the search form.
let me demonstrate:
the actual action helper just initiates a form and prepares it for display. I'll leave the form structure to you.
//the action helper
//Just fill in the args for the form to be displayed
class NameSpace_Controller_Action_Helper_Search extends Zend_Controller_Action_Helper_Abstract
{
public function direct($action, $label = null, $placeHolder = null)
{
$form = new Application_Form_Search();
//set the action
$form->setAction($action);
//set the submit button text
$form->search->setLabel($label);
//set the hint text displayed in the form window
$form->query->setAttribs(array('placeholder' => $placeHolder,
'size' => 27,
));
return $form;
}
}
I put the helper in the predispatch method of the controller so that each action in the controller can use the search form with having to build it in every page.
//to use the helper in your controller
class IndexController extends Zend_Controller_Action
{
public function preDispatch()
{
//setup action helper and assign it to a placeholder
$this->_helper->layout()->search = $this->_helper->search(
'/index/display', 'Search Collection!', 'Title');
}
//in your view script
<?php echo $this->layout()->search ?>
I like to put the placeholder in my master layout.phtml so that any time I populate the placeholder it will display. Now all you have to do is style it however you want.
Remember: As with any html form the action parameter is just a url so any valid url can be assigned to the form action. In this example I used the /controller/action parameters, but there are many other ways to pass a url to the form. The url helper comes to mind as good way to do it.
url($urlOptions, $name, $reset, $encode): Creates a URL string based
on a named route. $urlOptions should be an associative array of
key/value pairs used by the particular route.

How to implement Zend routing with Ajax

i am new to zend and i could not find a way to implement Ajax in zend.
In general php, its pretty easy to make an ajax request and show the response in the desired part of our page. But coming to zend, i have no clue how to do it.
Suppose in my index controller's index.phtml file, i have a button, and when i click on it, i have to make an ajax request to a particular controller and action, and load the related controller action's view in my page.
But what i could not understand is how to specify the urls for the ajax request and how the routing works.
Currently, i made ajax request to load the view statically like this:
xmlhttp.open("GET","../application/views/scripts/register/register.phtml",true);
FYI, i am using regex routing in my application, so would it be better to use curl to route the requests?
First things first, you don't request the view directly. You need to request the specific controller action, eg
/register/register
Zend comes with a great action helper called AjaxContext. This helper lets you respond with a different view based on the type of request (XmlHttpRequest) and a format parameter, disabling any layouts normally present.
To set it up, place something like this in your controller's init() method
public function init()
{
$this->_helper->ajaxContext->addActionContext('register', 'html')
->initContext();
}
Then, add a view script with the ajax suffix, eg register/register.ajax.phtml.
Construct your AJAX GET request to include the format=html parameter, eg
xmlhttp.open('GET', '/register/register/format/html', true);
or
xmlhttp.open('GET', '/register/register?format=html', true);
What will be returned is the rendered contents of register.ajax.phtml, without any layouts.
Apart from what the other answers stated, there's also a URL view helper function that can be useful to call a specific action on a controller. So you could just use $this->url(array('controller' => 'your_controller', 'action' => 'your_action'), 'default', true); to get the link to "your_action" action on the "your_controller" controller (using the default route). You could also specify a specific route instead of 'default' if you have one defined.
So for your example you would use something like the following in your view phtml file (if you're using the default routing) :
xmlhttp.open("GET","<?php echo $this->url(array('controller' => 'register', 'action' => 'register'), 'default', true);?>");
For more information refer to Zend Framework - View Helpers.
You should never be requesting the view directly. That's just wrong. Request URI like this:
xmlhttp.open("GET","/register/register");
Which means "I am looking for default module, register controller and register action", or in other words RegisterController::registerAction().
It's the same as:
xmlhttp.open("GET","/default/register/register");
Which is the same, the default module can be omitted.
Zend Framework knows where to look for the view script (unless you are using some unusual directory structure).
You can just create a blank layout and use it for your ajax controller actions (or what Phil suggested, AjaxContent is probably better for this).
I'll write here an almost complete "how to" guide to implement AJAX calls in Zendframework 3.
We need:
A route declaration
A controller
Some javaScript (is out of skope, perhaps I'll show it in other place)
The route declaration:
<?php
// editing a module.config.php in your project
// typical lines:
namespace Ajax2l; // my ajax module name
use Zend\Router\Http\Segment;
// ...
'router' => [
// ... other routes
'ajax2lsajaxcall' => [
'type' => Segment::class,
'options' => [
'route' => '/ajax2l/ajaxrequest[/:action][/:garbage]',
// the :action wildcard is a place to have extra parameters
// the :garbage wildcard is a place to send random strings
// to ensure you break cache to get real process for
// all your calls, since every call will be different
'defaults' => [
'controller' => Controller\AjaxController::class,
'action' => 'ajaxrequest'
]
],
'may_terminate' => true,
'child_routes' => [
// other ajax routes if needed
]
],
// I only use one ajax route and one controller for all my sites' ajax
// calls because they fire small db actions and reflect them on small
// parts of my pages. So I think I do not need a route and a controller
// for each ajax call. Instead, I use a Library and some services to get
// sets of elementary components and to perform tasks.
The Controller
My Ajax2l module is located under "myzend_project/module" directory.
<?php
// Filename: /module/Ajax2l/src/Controller/AjaxController.php
namespace Ajax2l\Controller
use Zend\Mvc\Controller\AbstractActionController;
use Zend\View\Model\ViewModel;
// No more components are needed
class AjaxController extends AbstractActionController {
public function ajaxrequestAction(){
$request = $this->getRequest();
$content = $request->getContent();
$isHttpRequest = $this->ifIsValidHttpRequest();
// if you have a real ajax call you must disable the normal output
$viewmodel = new ViewModel();
$viewmodel->setTerminal($is_xmlhttprequest);
// If not an Ajax call (perhaps from untrusted eyes) serve a forgiven
// page message
if(!$isHttpRequest){
return $this->forgivenPage();
}
// Now prepare a response and responds
$requestParams = $this->preProcessRequest($content);
// call the logics to process your params
$output = $this->processRequestParams($requestParams);
// and send a response
$response = $this->getResponse();
$response->setContent($output);
return $response;
}
private function ifIsValidHttpRequest($content){
// I use a small token string to verify if posted data matches
// perhaps not the proper way but is fast and works
$token = 'my_secure_visitor_session_token_identifier';
return (strpos($content, $token) > 2) ? 1 : 0;
// the validation is simplified here it has some more
// complex logics. To ensure validation of requesting source
}
private function preProcessRequest($content){
// The logics to know what to do
// ...
// here you can identify and set properly the post params
}
function processRequestParams($requestParams){
// some logics to process your data
// ...
// some logics to create a Json output
// ...
return $jsonObject;
}
protected function forgivenPage(){
$view = new ViewModel([]);
// set a forgiven message page as output
$view->setTemplate('ajax2l/messages/forgiven.phtml');
// note: the views directory uses lowercase for module names
return $view;
}
}
Hope it helps. I lost a lot of time reading on zend ajax without results. So I hope I was the last losing his time on this topic.
Luis

CakePHP: using Security::allowedControllers and Security::allowedActions

I'm trying to use Security::allowedControllers and Security::allowedActions. So I have a controller which look more or less like this
class AppController extends Controller {
var $components = array('Security'); //other components
//other stuff
}
class BookController extends AppController {
function beforeFilter() {
parent::beforeFilter();
$this->Security->allowedControllers = array('Users');
$this->Security->allowedActions = array('view');
$this->Security->RequireAuth = array('search', 'results');
}
//other stuff
}
The action 'search' displays a form, which then calls 'results' to show the results of the search. I am intentionally trying to be blackholed.
For what I understand of $this->Security->allowedControllers and $this->Security->allowedActions, I should be able to get POST data only from the action 'view' of the controller 'Users'. In particular the action 'results' should redirect me to a black hole, since it obtains POST data from the action 'search' of the controller 'Books'.
But this is not the case. I can even make cross controller requests, and never get blackholed, so I guess I'm not using correctly this variables. What is the right way to trigger cross-controller requests control?
Try this:
$this->Security->allowedFields = array('Model.fieldname', ...);
You need to add the fields that are not in the model to the allowedFields like I guess your Model.search field in the form.
This is a good and short tutorial for doing Auth with CakePHP 1.3: http://tv.cakephp.org/video/jasonwydro/2011/01/29/cakephp_1_3_auth_authentication_component_tutorial_-_administrator_login

Categories