Zend adding parameter to URL before generating view - zend-framework

The title might be misleading but I'm trying to do something very simple but cant figure it out.
Lets say I have a Question controller and show action and question id is the primary key with which I look up question details - so the URL looks like this
http://www.example.com/question/show/question_id/101
This works fine - So when the view is generated - the URL appears as shown above.
Now in the show action, what I want to do is, append the question title (which i get from database) to the URL - so when the view is generated - the URL shows up as
http://www.example.com/question/show/question_id/101/how-to-make-muffins
Its like on Stack overflow - if you take any question page - say
http://stackoverflow.com/questions/5451200/
and hit enter
The question title gets appended to the url as
http://stackoverflow.com/questions/5451200/make-seo-sensitive-url-avoid-id-zend-framework
Thanks a lot

You will have to add a custom route to your router, unless you can live with an url like:
www.example.com/question/show/question_id/101/{paramName}/how-to-make-muffins
You also, if you want to ensure that this parameter is always showing up, need to check if the parameter is set in the controller and issue a redirect if it is missing.
So, in your bootstrap file:
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
public function _initRoutes ()
{
// Ensure that the FrontController has been bootstrapped:
$this->bootstrap('FrontController');
$fc = $this->getResource('FrontController');
/* #var $router Zend_Controller_Router_Rewrite */
$router = $fc->getRouter();
$router->addRoutes( array (
'question' => new Zend_Controller_Router_Route (
/* :controller and :action are special parameters, and corresponds to
* the controller and action that will be executed.
* We also say that we should have two additional parameters:
* :question_id and :title. Finally, we say that anything else in
* the url should be mapped by the standard {name}/{value}
*/
':controller/:action/:question_id/:title/*',
// This argument provides the default values for the route. We want
// to allow empty titles, so we set the default value to an empty
// string
array (
'controller' => 'question',
'action' => 'show',
'title' => ''
),
// This arguments contains the contraints for the route parameters.
// In this case, we say that question_id must consist of 1 or more
// digits and nothing else.
array (
'question_id' => '\d+'
)
)
));
}
}
Now that you have this route, you can use it in your views like so:
<?php echo $this->url(
array(
'question_id' => $this->question['id'],
'title' => $this->question['title']
),
'question'
);
// Will output something like: /question/show/123/my-question-title
?>
In your controller, you need to ensure that the title-parameter is set, or redirect to itself with the title set if not:
public function showAction ()
{
$question = $this->getQuestion($this->_getParam('question_id'));
if(!$this->_getParam('title', false)) {
$this->_helper->Redirector
->setCode(301) // Tell the client that this resource is permanently
// residing under the full URL
->gotoRouteAndExit(
array(
'question_id' => $question['id'],
'title' => $question['title']
)
);
}
[... Rest of your code ...]
}

This is done using a 301 redirect.
Fetch the question, filter out and/or replace URL-illegal characters, then construct the new URL. Pass it to the Redirector helper (in your controller: $this->_redirect($newURL);)

Related

CSRF field is missing when I embed my form with a requestAction in CakePHP 3

I want to embed a contact form in multiple places on my website.
I developed a contact form in a contact() function within my MessagesController.php:
// MessagesController.php
public function contact()
{
$this->set('title', 'Contact');
$message = $this->Messages->newEntity();
... // shortened for brevity
$this->set(compact('message'));
$this->set('_serialize', ['message']);
}
I loaded the CSRF component in the initialize() function of the AppController.php:
// AppController.php
public function initialize()
{
parent::initialize();
$this->loadComponent('Csrf');
... // shortened for brevity
}
The form is rendered with a contact.ctp and it works fine.
I followed CakePHP's cookbook which suggests using requestAction() within an element, then echoing the element where I want it:
// contact_form.ctp
<?php
echo $this->requestAction(
['controller' => 'Messages', 'action' => 'contact']
);
?>
And:
// home.ctp
<?= $this->element('contact_form'); ?>
The problem is that the form is rendered fine, but the CSRF hidden field is missing. It should be automatically added to the form since the CSRF component is called in the AppController.php.
I guess either using an element with a requestAction() isn't the solution for this particular case, or I am doing something wrong.
Any ideas? Thanks in advance for the input!
Request parameters need to be passed manually
requestAction() uses a new \Cake\Network\Request instance, and it doesn't pass the _Token and _csrf parameters to it, so that's why things break.
While you could pass them yourself via the $extra argument, like
$this->requestAction(
['controller' => 'Messages', 'action' => 'contact'],
[
'_Token' => $this->request->param('_Token'),
'_csrf' => $this->request->param('_csrf')
]
);
Use a cell instead
I would suggest using a cell instead, which is way more lightweight than requesting an action, also it operates in the current request and thus will work with the CSRF component out of the box.
You'd pretty much just need to copy your controller action code (as far as the code is concerned that you are showing), and add a loadModel() call to load the Messages table, something like
src/View/Cell/ContactFormCell.php
namespace App\View\Cell;
use Cake\View\Cell;
class ContactFormCell extends Cell
{
public function display()
{
$this->loadModel('Messages');
$this->set('title', 'Contact');
$message = $this->Messages->newEntity();
// ... shortened for brevity
$this->set(compact('message'));
$this->set('_serialize', ['message']);
}
}
Create the form in the corresponding cell template
src/Template/Cell/ContactForm/display.ctp
<?php
echo $this->Form->create(
/* ... */,
// The URL needs to be set explicitly, as the form is being
// created in the context of the current request
['url' => ['controller' => 'Messages', 'action' => 'contact']]
);
// ...
And then wherever you want to place the form, just use <?= $this->cell('ContactForm') ?>.
See also
API > \Cake\Routing\RequestActionTrait::requestAction()
Cookbook > Views > Cells

Zend Framework 2 - Including a variable in a form action

My login form may be called with a re-direct query and I am wondering if there is a simple way to include this in the subsequent post action.
The use case is for SSO login.
My normal login route is:
/customer/login
and when called from a third party client becomes:
/customer/login?redirectTo=http://www.example.com
My login action:
public function loginAction()
{
$prg = $this->prg();
if ($prg instanceof Response) {
return $prg;
} elseif ($prg === false) {
return new ViewModel(['form' => $this->loginForm]);
}
This loads my view and I currently define my action as so:
$form = $this->form;
$form->setAttribute('action', $this->url());
Now when the action is called, I am losing the redirectTo parameter.
So my question is this, is it possible to update the action to include the re-direct url so that when a user clicks to login, it is posted back to my form?
thanks!
EDIT -
Obviously I can create a redirectTo route in the configs and test on the initial call to the page for the existence of such a route and include this in the form. My question however is whether or not this can be done automagically simply from the viewscript.
To generate query string arguments from the view helper, you need to assign them as the third argument using the query key. Please refer to the ZF2 docs http://framework.zend.com/manual/current/en/modules/zend.view.helpers.url.html
$form->setAttribute('action', $this->url('application', array('action' => 'login'), array('query' => array('redirectTo' => 'http://www.example.com,))));
$form->setAttribute('action', $this->url('login', [
'query' => [
'redirectTo' => $this->params()->fromQuery('redirectTo')
]
]);
Where 'login' is the name of the login route.
See Url View Helper
Well my solution is not as elegant as I hoped it would be. I wanted to avoid using the controller for the query params. As #Stanimir pointed out, the view helpers are in fact, to help with view so my original idea was unfounded.
This is an end to end of what I have put together:
Controller:
$redirect_url = $this->params()->fromQuery('redirectTo',null);
Returns this to view on initial load:
return new ViewModel( ['form' => $this->loginForm , 'redirect_url' => $redirect_url] );
View
$form->setAttribute(
'action',
$this->url(
'customer/login', [] ,
[ 'query'=>
[ 'redirectTo' => $this->redirect_url ]
]
)
);

how to make optional parametres in zendframework URL

I am new to Zend, but very very keen to learn. This is really just a quick question on routing in Zend Framework.
I understand the basic of it but I am still confused about how I can create some optional parameters at the end of my URL. For example, I have the following default page URL:
examplesite.com/accounts/enquiry
I now want to add two additional parameters to it i.e:
userid= 6
location= 12
So, the eventual URL should look like:
examplesite.com/accounts/enquiry/6/12
but
examplesite.com/accounts/enquiry
Will get you to the same page.
I am not clear. How do it do this? I mean, this is not a bespoke URL. so, I don't need to create a custom route. It basically just the last two parameters that need to be added to the page.
How do I do this?
First 2 parameters are controller and action name, the named params.
Here you are:
examplesite.com/accounts/enquiry/userid/6/location/12
or you can define your own route like this:
$route = new Zend_Controller_Router_Route('accounts/enquiry/:userid/:location);
and then add it to router:
$router->addRoute('accounts', $route);
You could add a custom route inside your Bootstrap.php, e.g. (untested):
protected function _initRoutes()
{
[...]
$frontController = Zend_Controller_Front::getInstance();
$router = $frontController->getRouter();
$accounts = new Zend_Controller_Router_Route(
'accounts/enquiry/:userid/:location',
array(
'userid' => '[0-9]{2}',
'location' => '[0-9]{2}',
'controller' => 'accounts',
'action' => 'enquiry',
)
);
$router->addRoute('accounts', $accounts);
[...]
}
http://framework.zend.com/manual/1.12/en/zend.controller.router.html

Zend Custom Route if no match found

I have a ZF app with several modules as this: ( as usual )
root\
\application\
\default
\items
\me
\controllers
\views
The application uses the default routing like /module/controller/action;
What I want is this: if no match has been found for the default Zend Routing (no action / controller / module has been found ) then route to a desired path with the url endpoint spitted into parameters.
For example:
mydomain.lh/me -> will match the module me, controller index, action index ( as default )
mydomain.lh/my_category_name -> will match the module items, controller index, action index, params: category => my_category_name -> using the desired path route
no my_category_name module exists to match against
I have tried with this, into bootstrap.php:
public function _initRoutes ()
{
$router = $this->_front->getRouter(); // returns a rewrite router by default
$router->addRoute(
'cat-item',
new Zend_Controller_Router_Route('/:category',
array(
'module' => 'items',
'controller' => 'index',
'action' => 'index'))
);
}
Witch points to the correct location ( I know because I var_dump -ed the request url into the items/index/index action and the expected url and parameters were there, but if I do not do var_dump(something);exit; into the action, a blank page is served.
no output is made but also no error is generated, the request status is 200 - OK
Can anybody have a suggestion ?
Thank you!

Custom routing for forms action

I want to make routing for form when submitting the form,
$reportRoute = new Zend_Controller_Router_Route('blogsedit/blog_id/:blog_id', array('module' => 'blogs', 'controller' => 'blog', 'action' => 'edit','blog_id' =>NULL));
$routesArray = array('blogs' => $reportRoute);
$router->addRoutes($routesArray);
and in the form I used to make like this
<form action="/blogs/blog/edit/blog_id/<?php echo $blogId?>"
</form>
How can I make the custom routing of the form action ?
You can use the view helper url().
url($urlOptions, $name, $reset): Creates a URL string based on a named
route. $urlOptions should be an associative array of key/value pairs
used by the particular route.
Generates an url given the name of a route.
#access public
#param array $urlOptions Options passed to the assemble method of the Route object.
#param mixed $name The name of a Route to use. If null it will use the current Route
#param bool $reset Whether or not to reset the route defaults with those provided
#return string Url for the link href attribute.
In your case, you would have something like:
url(array('blog_id' => $blogId), 'blogs', true)