zendframework2 : How to pass a parameter from one controller to another - php

There was a similar thread but it didn't address this exact problem so I thought I would create a new thread to make this clear.
Code/problem explained:
This is my SystemsController.
Here I have viewsystemAction(). This is loaded, and from the view a script is called to load ajaxviewsystemAction().
In the viewsystemAction(), I use the params function to get the "id" parameter from my route. For example systems/viewsystems/222 ( <-- 222 is id. ). I echo this out just to show that it is correct, and it gives the correct id for the page.
The ajaxviewsystemsAction is kind of where the problem is. This is because the route of ajaxviewsystemsAction id is not the same as viewsystemsAction. If I echo the id from the params here, it displays as 0.
Although if i go to this page in the browser for example ajaxviewsystemsAction/222 then it is working perfectly. However, nothing is rendered here except the ajax table, as remember it is called in viewsystemAction from a script.
What i need to do, is somehow pass the id route value from viewsystemAction into ajaxviewsystemAction, to both use this same route id.
This is so when someone clicks on the link, it loads viewsystems/222, the correct id is executed in both viewsystemsAction and ajaxviewsystemsAction.
Is this possible? If not how could i make something similar like this. I am using zftable which integrates ajax. I need to pass this id parameter through into a query.
private function getSourceViewAllSystems($id)
{
return $this->getSystemsTable()->fetchViewAllSystems($id); //paramater to model which executes sql ->where system = $id
}
public function viewsystemAction()
{
$id = (int) $this->params()->fromRoute('id', 0);
echo $id; //i see the correct id for example 220 from the route in the browser
}
public function ajaxviewsystemAction()
{
$id = (int) $this->params()->fromRoute('id', 0);
echo $id; //to see the id of the route with the ajax page
//displays 0 and not the route id from the viewsystemAction
$table = new TableExample\Advance();
$table->setAdapter($this->getDbAdapter())
->setSource($this->getSourceViewAllSystems($id))
->setParamAdapter($this->getRequest()->getPost())
;
return $this->htmlResponse($table->render('custom' , 'custom-b2'));
}
--- update ---
<?php
return array(
'controllers' => array(
'invokables' => array(
'Systems\Controller\Systems' => 'Systems\Controller\SystemsController',
),
),
// The following section is new and should be added to your file
'router' => array(
'routes' => array(
'systems' => array(
'type' => 'segment',
'options' => array(
'route' => '/systems[/][:action][/:id]',
'constraints' => array(
'action' => '[a-zA-Z][a-zA-Z0-9_-]*',
'id' => '[0-9]+',
),
'defaults' => array(
'controller' => 'Systems\Controller\Systems',
'action' => 'index',
),
),
),
),
),
'view_manager' => array(
'template_path_stack' => array(
'systems' => __DIR__ . '/../view',
),
),
);

Related

Zend framework 2 routing required query parameters not working

I'm working on zf2 to make one of my routes only accessible when a query string parameter is passed. Otherwise, it will not. I've added a filter on the route section but when accessing the page without the query parameter, it is still going thru.
'router' => array(
'routes' => array(
'show_post' => array(
'type' => 'segment',
'options' => array(
'route' => '[/]show/post/:filter',
'constraints' => array(
'filter' => '[a-zA-Z0-9-.]*',
),
'defaults' => array(
'controller' => 'blog_controller',
'action' => 'show'
)
),
),
),
),
http://example.com/show/post/?postId=1235 = This should work
http://example.com/show/post?postId=1235 = This should work
http://example.com/show/post/ = This should not work
http://example.com/show/post = This should not work
The way you currently have this setup you would have to structure your url like this
http://example.com/show/post/anything?postId=1235
I think what you are wanting is to structure your route like this
'route' => '[/]show/post',
Not sure what you are trying to accomplish with [/] before show though, you are making that dash optional there?
I would write it like this
'route' => '/show/post[/:filter]',
This way you can structure your urls like this
http://example.com/show/post/anything?postId=1235
or
http://example.com/show/post?postId=1235
Then in your action you can access those parameters like this
$filter = $this->params('filter');
$post_id = $this->params()->fromQuery('post_id');
or just
$post_id = $this->params()->fromQuery('post_id');
***************UPDATE***************
It looks like zf2 used to include what you are trying to do and removed it because of security reasons.
http://framework.zend.com/security/advisory/ZF2013-01
Don't try to bend ZF2 standard classes to your way. Instead write your own route class, a decorator to the segment route, which will do exactly as you please:
<?php
namespace YourApp\Mvc\Router\Http;
use Zend\Mvc\Router\Http\Segment;
use use Zend\Mvc\Router\Exception;
use Zend\Stdlib\RequestInterface as Request;
class QueryStringRequired extends Segment
{
public static function factory($options = [])
{
if (!empty($options['string'])) {
throw new Exception\InvalidArgumentException('string parameter missing');
}
$object = parent::factory($options);
$this->options['string'] = $options['string'];
return $object;
}
public function match(Request $request, $pathOffset = null, array $options = [])
{
$match = parent::match($request, $pathOffset, $options);
if (null === $match) {
// no match, bail early
return null;
}
$uri = $request->getUri();
$path = $uri->getPath();
if (strpos($path, $this->options['string']) === null) {
// query string parametr not found in the url
// no match
return null;
}
// no query strings parameters found
// return the match
return $match;
}
}
This solution is very easy to unit test as well, it does not validate any OOP principles and is reusable.
Your new route definition would look like this now:
// route definition
'router' => array(
'routes' => array(
'show_post' => array(
'type' => YourApp\Mvc\Router\Http\QueryStringRequired::class,
'options' => array(
'string' => '?postId=',
'route' => '[/]show/post/:filter',
'constraints' => array(
'filter' => '[a-zA-Z0-9-.]*',
),
'defaults' => array(
'controller' => 'blog_controller',
'action' => 'show'
)
),
),
),
),

Trying to redirect page using Zend FW.

I am trying to redirect the page if condition is true I dont want to use core php function header() because I am using zend so, I have tried many things but in vain.
Here is my code.
$viewer = Engine_Api::_()->user()->getViewer();
$table = Engine_Api::_()->getItemTable('sitebusiness_business');
$select = $table->select()->where('owner_id = ?' , $viewer->getIdentity());
$result = $table->fetchAll($select);
if(count($result) == 0){
$url = Zend_Controller_Front::getInstance()->getRouter()->assemble(array('action' => 'package'), 'sitebusiness_general', true); // url is /mysite/businessitems/package
$this->_redirect($url); //Not Working ----
$this->_helper->redirector($url); //Not Working ----
$this->_redirector->gotoSimple($url); //Not Working ----
$this->_redirector->gotoUrl($url); //Not Working ----
}
Everytime I get the message: Fatal error: Call to a member function redirector() [whatever the function name is] on a non-object.
Thanks in advance.
you can use the following one to redirect in zf2
$this->redirect()->toRoute('routename');
Generaly route name is the function name in the controller.
like addEmployeeAction(), editEmployeeAction()...
which is routes defined in module.config.php
'zfcadmin' => array(
'type' => 'segment',
'options' => array(
'route' => '/admin',
'defaults' => array(
'controller' => 'ZfcAdmin\Controller\AdminController',
'action' => 'index',
),
),
'may_terminate' => true,

Zend2 navigation

I would like to make navigation buttons in my view, for example index.phtml but it's not working. I did know how to do it in Zend1 but in Zend2 I have a problem. My code looks like this (file index.phtml):
$container = new \Zend\Navigation\Navigation($tableActions);
var_dump($container);
echo '<div class="table-column">';
echo $this->navigation($container)->menu();
echo '</div>';
Variable $tableAction looks like this:
public $tableActions = array(
array(
'label' => 'On/Off',
'module' => 'import',
'controller' => 'import',
'action' => 'setstatus',
'params' => array('id' => null),
),
);
I did not get any error, just whole site die on this line. var_dump returns object(Zend\Navigation\Navigation) so it's fine so far. Problem is, how to show it...
The navigation pages have dependencies which aren't being met by just creating a new container class in a view. The Mvc page needs a RouteStackInterface (Router) instance and a RouteMatch instance. Similarly Uri pages need the current Request instance.
You can see this clearly if you take a look at the Zend\Navigation\Service\AbstractNavigationFactory and its preparePages and injectComponents methods.
The view is not the right place to be instantiating menus, instead put the menu configuration spec in your module.config.php...
<?php
return array(
'navigation' => array(
'table_actions' => array(
array(
'label' => 'On/Off',
'module' => 'import',
'controller' => 'import',
'action' => 'setstatus',
'params' => array('id' => null),
),
),
),
);
Write a factory extending the AbstractNavigationFactory class and implement the getName() method which returns the name of your menu spec key (table_actions in this example)
<?php
namespace Application\Navigation\Service;
use Zend\Navigation\Service\AbstractNavigationFactory;
class TableActionsFactory extends AbstractNavigationFactory
{
/**
* #return string
*/
protected function getName()
{
return 'table_actions';
}
}
Map the factory to a service name in the service_manager spec of module.config.php ...
<?php
return array(
'navigation' => array(// as above ... )
'service_manager' => array(
'factories' => array(
'TableActionsMenu' => 'Application\Navigation\Service\TableActionsFactory',
),
),
);
Now you can call the view helper using the service name TableActionsMenu you just mapped
<div class="table-column">
<?php echo $this->navigation('TableActionsMenu')->menu(); ?>
</div>
Finally, if, as I suspect, you need to change an attribute of the page depending on the view, you can do that too, navigation containers have find* methods which can be accessed from the navigation helper and used to retrieve pages.
Here's an example looking for the page with a matching page label, then changing it before rendering (obviously not an ideal search param, but it gives you the idea)
$page = $this->navigation('TableActionsMenu')->findOneByLabel('On/Off');
$page->setLabel('Off/On');
// and then render ...
echo $this->navigation('TableActionsMenu')->menu();

CakePHP friendly seo url

I want to make my url seo friendly. www.example.com/posts/view/1 change for www.example.pl/:slug-:id. Everything works fine, but probably I'm doing something wrong with routing, because when after clicking the urls in paginator, the url is correct, it looks like www.example.pl/:slug-:id , but it appears an error
"The requested address 'www.example.pl/:slug-:id' was not found on this server."
I don't know what's wrong. Here's my code:
Router::connect(
'/:slug-:id',
array(
'controller' => 'posts',
'action' => 'view'
),
array(
'pass' => array('slug' , 'id'),
'id' => '[0-9]+'
)
);
in paginator view:
echo $this->Html->link($ad['Post']['title'], array(
'controller' => 'posts',
'action' => 'view',
'slug' => Inflector::slug($post['Post']['title'],'-'),
'id'=>$post['Post']['id'])
);
I solved the problem.
Its too simple i'll give you an example from my project ..
in your routes.php
Router::connect(
'/:slug-:id',
array('controller'=>'posts','action'=>'view'),
array('pass'=>array('slug','id'),'slug'=>'[a-zA-Z0-9 -]+','id'=>'[0-9]+')
);
your link in views should be like .
$this->Html->link(__('link desu'),array('controller'=>'posts','action'=>'view','id'=>$post['Post']['id'],'slug'=>$post['Post']['slug']));
and your PostsController.php
public function view($slug,$id){
$this->Post->id = $id;
// ....
}
Quick tip : try to create an array in your PostModel to avoid creating it every time in your view .
example :
Post.php
class Post extends AppModel{
// ....
public function afterFind($results,$primary = false){
foreach ($results as $key => $value) {
if(isset($value[$this->alias]['id'])){
$results[$key][$this->alias]['url'] = array(
'controller'=>'posts',
'action'=>'view',
'id'=>$results[$key][$this->alias]['id'],
'slug'=>$results[$key][$this->alias]['slug']
);
}
// ....
}
return $results;
}
}
}
so you can call it in your view simply like that
$this->Html->link(__('link desu'),$post['Post']['url']);
It's probably a problem with the regex on the route. Your slug contain hyphens - which you also use to separate between the slug and the id. i.e.:
example.com/my-slug-has-hyphens-1
The regex is not smart enough to know that the "last" hyphen separates the slug from the id.
To test if this is the problem, try using a route like this '/:slug__:id', just to see if it works.
I solved the problem. In the posts controller my view function was wrong. Here's right correct:
function view($id = null, $slug = null) {
$this->Post->id = $this->params['post'];
$this->set('post', $this->Post->read());
Pass is order sensitive
In the question the route is as follows:
Router::connect(
'/:slug-:id',
array(
'controller' => 'posts',
'action' => 'view'
),
array(
'pass' => array('slug' , 'id'), # <-
'id' => '[0-9]+'
)
);
That means the post function will recieve:
public function view($slug, $id)
As indicated by the edited question, the code is expecting the id to be the first parameter. The easiest solution is simply to specify the passed parameters in the order that they are expected:
...
'pass' => array('id', 'slug'), # <-
Router::connect(
'/:slug/:id',
array(
'controller' => 'posts',
'action' => 'view'
),
array(
'pass' => array('slug' , 'id'),
'id' => '[0-9]+'
)
);
the above code will create correct link as www.example.com/posts/view/title/1
echo $this->Html->link($post['Post']['title'], array('controller' => 'posts', 'action' => 'view', Inflector::slug($post['Post']['title'],'-'),$post['Post']['id']));

Kohana 3.2 - Routing questions

Firstly, Kohana's documentation is terrible, before people go "read the docs" I have read the docs and they don't seem to make much sense, even copying and pasting some of the code doesn't work for some things in the documentation.
With that in mind, I have a route like so:
//(enables the user to view the profile / photos / blog, default is profile)
Route::set('profile', '<userid>(/<action>)(/)', array( // (/) for trailing slash
"userid" => "[a-zA-Z0-9_]+",
"action" => "(photos|blog)"
))->defaults(array(
'controller' => 'profile',
'action' => 'view'
))
This enables me to go http://example.com/username and be taken to the users profile, http://example.com/username/photos to be taken to view the users photos and http://example.com/username/blog to view the blog.
If somebody goes http://example.com/username/something_else I want it to default to the action view for the user specified in <userid> but I can't seem to find any way of doing this.
I could do it like this:
Route::set('profile', '<userid>(/<useraction>)(/)', array(
"userid" => "[a-zA-Z0-9_]+",
"useraction" => "(photos|blog)"
))->defaults(array(
'controller' => 'profile',
'action' => 'index'
))
then in the controller do this:
public function action_index(){
$method = $this->request->param('useraction');
if ($method && method_exists($this, "action_{$method}")) {
$this->{"action_{$method}"}();
} else if ($method) {
// redirect to remove erroneous method from url
} else {
$this->action_view(); // view profile
}
}
(it might be better in the __construct() function but you get the gist of it.)
I'd rather not do that though if there is a better method available (which there really should be)
I think the answer might be in the regex but the following does not work:
$profile_functions = "blog|images";
//(enables the user to view the images / blog)
Route::set('profile', '<id>/<action>(/)', array(
"id" => "[a-zA-Z0-9_]+",
"action" => "($profile_functions)",
))->defaults(array(
'controller' => 'profile'
));
Route::set('profile_2', '<id>(<useraction>)', array(
"id" => "[a-zA-Z0-9_]+",
"useraction" => "(?!({$profile_functions}))",
))->defaults(array(
'controller' => 'profile',
'action' => 'view'
));
although it does match when nothing is after the ID.
I would set up the route like this:
Route::set('profile', '<userid>(/<action>)(/)', array(
"userid" => "[a-zA-Z0-9_]+",
"action" => "[a-zA-Z]+"
))->defaults(array(
'controller' => 'profile',
'action' => 'index'
))
And then in the controllers before() method:
if(!in_array($this->request->_action, array('photos', 'blog', 'index')){
$this->request->_action = 'view';
}
Or somethig similiar, just validate the action in the controller...
EDIT:
This could also work:
if(!is_callable(array($this, 'action_' . $this->request->_action))){
$this->request->_action = 'view';
}

Categories