I am using Zend\Paginator to construct a paginated result set. This works fine, however, after adding a search form, I cannot get the two to play nicely together.
The URL produced by the search form on the page is:
user/index/?searchTerm=hello
How do I amend the Zend paginator configuration so that it retains the searchTerm in the URLs produced?
I was hoping for something like:
user/index/page/4/?searchTerm=hello
What am I missing?
The module config route is defined as follows:
'user' => array(
'type' => 'Zend\Mvc\Router\Http\Segment',
'options' => array(
'route' => '/user[/[:action[/]]][[id/:id]][/[page/:page]]',
'defaults' => array(
'controller' => 'Application\Controller\User',
'action' => 'index',
'id' => null,
),
// the below was added to try and get the searchTerm query to be retained
'may_terminate' => true,
'child_routes' => array(
'searchTerm' => array(
'type' => 'Query',
),
),
),
),
The pagination is constructed using this in the view:
echo $this->paginationControl(
$this->users, 'sliding', array('paginator', 'User'), array('route' => 'user', 'action' => 'index')
);
Pagination template snippet:
<li>
<a href="<?php echo $this->url($this->route, array('action' => $this->action, 'page' => $this->next), true); ?>">
Next ยป
</a>
</li>
(I was under the impression that passing true as the third parameter to url() would retain the query params.)
I now see what that third parameter to url() is doing. I can simplify the pagination links and remove the 'action' key as follows:
<a href="<?php echo $this->url($this->route, array('page' => $this->next), true); ?>">
The page's action was matched as part of the URL (due to that third param being true) which is why that works. By the same token I can change the route to this:
'route' => '/user[/[:action[/]]][[id/:id]][/[page/:page]][/[search/:search]]',
And then the search will be retained in the pagination links.
If I amend the search form to submit via JavaScript, I can construct the search URL and direct the user to it.
Simple jQuery example for that approach:
$(".search-form").submit(function() {
var baseUrl = $(this).attr('action'),
search = $(this).find('.search').val();
window.location = baseUrl + '/search/' + search;
return false;
});
Another option would be to redirect to the current/route/search/term route in the controller if it receives a searchTerm query.
I'm posting this as an answer but I am open to better solutions.
Related
I'm not sure that i've written something intelligibile in the title either.
I'll try to explain with some codes line. First some information.
I'm working with CakePhP and this problem comes up while creating the arrays for the actions allowed.
Long story short, i'm using an ACL to check whenever a page is loaded if the current user has access to the actions available on that page. This is an example:
//Controller
$role = $this->User->Role;
$role->id = $this->Auth->user('Role.id');
$actionsMenu = array();
$actionsMenu['Files'] = array(
'label' => 'Manage Files',
'controller' => 'project_files',
'action' => 'index',
'parameters' => $id,
'authorized' => $this->Acl->check($role, 'ProjectFiles')
);
$actionsMenu['Groups'] = array(
'label' => 'Manage Groups',
'controller' => 'groups',
'action' => 'index',
'parameters' => $id,
'authorized' => $this->Acl->check($role, 'Groups')
);
In the view I just loop and if the "authorized" is set to true, i'll print the related button.
Now, the problem rise when i've more that one parameter to pass. This is another snippet that follows the one up there:
//Controller [following]
$this->Project->id = $id;
if ($this->Project->field('archived')) {
$actionsMenu['Archiviation'] = array(
'label' => 'Restore',
'controller' => 'projects',
'action' => 'archiviation',
'parameters' => array($id, 0),
'authorized' => $this->Acl->check($role, 'controllers/Projects/archiviation')
);
} else {
$actionsMenu['Archiviation'] = array(
'label' => 'Archive',
'controller' => 'projects',
'action' => 'archiviation',
'parameters' => array($id, 1),
'authorized' => $this->Acl->check($role, 'controllers/Projects/archiviation')
);
}
This is what you can find in the views:
//View
<div class="actions">
<h3><?php echo __('Actions'); ?></h3>
<ul>
<li><?php echo $this->Html->link(__('<- Projects Management'), array('action' => 'index')); ?></li>
<li> </li>
<?php foreach($actionsMenu as $action) : ?>
<?php if($action['authorized']) : ?>
<li><?php echo $this->Html->link(__($action['label']), array('controller' => $action['controller'], 'action' => $action['action'],
is_array($action['parameters']) ? implode(', ', $action['parameters']) : $action['parameters']
)); ?></li>
<?php endif; ?>
<?php endforeach; ?>
</ul>
I thought the array format was the most CakePhP friendly way to pass the values just to discover that in case of multiple parameters, cake tend to prefer an associative array.
The problem here is that CakePhP read that implode as a whole parameter composed by a string.
This is an example:
<!--HTML-->
Restore
What I want to achieve is that the values separated by the comma should be read as different variables.
Honestly I never run in a situation like this and I've neither idea of what to look for on google to find something fitting (being a non-native english speaking isn't helping) so any suggestion is welcome.
Edit1
To clarify, the code listed in the second box (starting with Controller [following]) is the one that is causing problems.
The previous box just build the array with a single parameters that match the simple structure of CakePhP for building link but the second block will need to pass a second parameter. If the values was static one could simply type something like this:
//CakePhP HTML::Link
echo $this->Html->link(
'text of link',
array(
'controller' => 'controller_name',
'action' => 'action_name',
'parameter1', 'parameter2'
));
If I send the list of parameters as string, Cake are considering them a single parameter, reading it like (string)("'parameter1', 'parameter2'").
The same happens with the code i've written above in which i'm converting the array of values to string with implode().
So what I'm asking for, is if there a function, option or practice that I'm missing.
Edit2
As the user user221931 pointed out, the CakePhP function should support multiple parameters as array in the form of array('parameter1', 'parameter2', 'paramenterN').
So i've tried just removing the is_array() check and simply pass the value of $action['parameters']. The view section now look like this:
<?php echo $this->Html->link(__($action['label']), array(
'controller' => $action['controller'],
'action' => $action['action'],
//is_array($action['parameters']) ? implode(', ', $action['parameters']) : $action['parameters']
$action['parameters']
)); ?>
But i've got a warning as follows:
rawurlencode() expects parameter 1 to be string, array given
Going to open the context of the warning, seems like CakePhP is reading the information provided as follows:
$params = array(
'controller' => 'projects',
'action' => 'archiviation',
'plugin' => null,
'pass' => array(
(int) 0 => array(
(int) 0 => '1',
(int) 1 => (int) 1
)
),
'named' => array()
)
Honestly I'm lost here.
I've tried changing the second value of the array to a string too and passing an associative array instead of a numeric just to try something obvious but the error persist and the link comes out truncated without parameters.
The correct format to use Html::link()
echo $this->Html->link(
'text of link',
array(
'controller' => 'users', //Or any controller name
'action' => 'view', //Or any action
1, //Several parameters
'test1', //go here
'test2'
)
);
If you don't know the number of parameters beforehand, you only need to array_merge your fixed array with the array of dynamic parameters.
Assuming $arrayOfParameters holds array('test1', 'test2', 'test3')
$urlArray = array_merge(
array('controller' => 'users', 'action' => 'view'),
$arrayOfParameters
);
echo $this->Html->link('text of link', $urlArray);
Additionally you could implode your array of parameters as a url, i.e:
$urlString = implode('/', $arrayOfParameters); //creates a string 'test1/test2/test3'
echo $this->Html->link('text of link', array(
'controller' => 'users',
'action' => 'view',
$urlString //One parameter that will be looking as many
));
I am having problems trying to get my cake application to display items from the database on a single page based on the url of the page... For example I want to display items that are in the "Party" category and I type "mydomain.com/All/party" in the address bar and I get a list of all the items in that category on that page.
This is my code but the pages I am getting from the route is blank:
My routes.php:
Router::connect('/categories/:name', array(
'controller' => 'All', 'action' => 'categories'
),
array(
'pass' => 'catname',
'catname' => '[a-zA-Z0-9]+'
));
my AllController.php:
function categories($catname = null) {
$options = array(
'conditions' => array('Category.name'=>$catname)
);
$Category = $this->Category->find('all', $options);
$this->set('Category', $Category);
$this ->render('/All/categories');
}
Any help would be appreciated.
I don't get at all the code you just posted, but what you want to achieve is quite trivial:
Router::connect(
'/products/:category',
['controller' => 'products', 'action' => 'categorized'],
['pass' => ['category']]
);
In ProductsController:
function categorized($category) {
$conditions = ['Category.name' => $category];
$this->set('products', $this->Prodcut->find('all', compact('conditions')));
}
That's it. You may want to reuse the index action if you don't need a separate view template for that, in that case just set the conditions (for example to the Paginator component) and call $this->setAction('index')
I found a much easier way to do what I wanted. I just remember that I had a database search option that allows persons to search for products and categories. What I did was simply do a search for the categories and then copy the URL of the page I got and set that as a link to the category in my application.
Example code:
<ul class="filter-options">
<li><a style="color:#fff" href="http://mydomain.com/products/search?q=parties">Parties</a></li>
<li><a style="color:#fff" href="http://mydomain.com/products/search?q=festivals">Festivals</a></li>
<li><a style="color:#fff" href="http://mydomain.com/products/search?q=theatres">Cinemas/Theatres</a></li>
Everything looks good on your side just change your routes.php as follows
Router::connect('/categories/:catname', array(
'controller' => 'all',
'action' => 'categories'),
array('pass' => array('catname'),
'catname' => '[a-zA-Z0-9]+'));
I have some simple pagination set up in my controller like so:
public function index() {
$this->set('items', $this->paginate());
}
In my view I'm using the Paginator helper to output numbered links:
echo $this->Paginator->numbers(array(
'separator' => '',
'tag' => 'li',
'currentTag' => 'a',
'currentClass' => 'active'
));
This all works fine, however I want to use a custom URL for the paginated links. I added this to my routes.php file:
Router::connect('/things/:page', array('controller' => 'things', 'action' => 'index'), array('page' => '[0-9]+'));
Now the links outputted by the Paginator helper are the way I want. They look like http://mysite.com/things/2, http://mysite.com/things/3 etc.
But when I click the links the Paginator in my controller doesn't seem to recognize it's on a certain page, as I just get the first page's results shown. I'm guessing I need to somehow pass the page number to $this->paginate(), but I don't know what the best method is. Is there a way for Cake to get the page number automatically if I modify my route?
Thanks!
Since the Pagination component by default expect named parameter 'page:1' you need somehow to pass same variable in index.
if you make print of $this->request->params in your controller, you will see that it's missing.
See that example how you can pass named parameters in the Router:
Router::connect(
'/:controller/:action/*',
array(),
array(
'named' => array(
'wibble',
'fish' => array('action' => 'index'),
'fizz' => array('controller' => array('comments', 'other')),
'buzz' => 'val-[\d]+'
)
)
);
For more info see this section in Cakephp book
I obviously have a fundamental misunderstanding of how pagination works in CakePHP.
I have the following route set up which shows all posts in a category:
Router::connect('/:parent/:category',
array('controller' => 'posts', 'action' => 'viewCategory'),
array('parent' => '[a-z0-9-]+', 'category' => '[a-z0-9-]+'));
The pages work fine, however the pagination helper is outputting the wrong links for pagination.
I'm using $this->Paginator->numbers().
It's outputting links in this format: mysite.com/posts/viewCategory?page=2
rather than like this: mysite.com/parent-category/sub-category?page=2.
I've tried adding the following route after the first one and it still doesn't work:
Router::connect('/:parent/:category/:page',
array('controller' => 'posts', 'action' => 'viewCategory'),
array('parent' => '[a-z0-9-]+',
'category' => '[a-z0-9-]+',
'page' => '[0-9]+'));
For reference, my pagination options set in my view are as so:
<?php $this->Paginator->options(
array('url' =>
array('controller' => 'posts', 'action' => 'viewCategory')
)); ?>
What am I doing wrong here?
You are setting the url yourself
This is your paginator options call:
<?php
$this->Paginator->options(array(
'url' => array(
'controller' => 'posts',
'action' => 'viewCategory'
)
));
?>
Where you are overriding the current url - and explicitly requesting that the paginator uses the the '/posts/viewCategory' url (with no arguments) as it's base url.
Just don't define the url
Simply don't call options and the helper will use the current url - that should mean that if the current url is:
/parent-category/sub-category
Then page 2 will be (assuming you are using the paramType option to use GET arguments rather than named parameters):
/parent-category/sub-category?page=2
If that's not the case there's information missing from the question; it's important to distinguish between "vanity routes not being used" and "the url is not equivalent (the current situation).
Just had a battle fixing something similar and came across this post. Though old, but I think my answer might save someone the time I had to spend fixing it.
Basically, what you need to do is call the Paginator->options() before Paginator->numbers(), thus:
$this->Paginator->options(
array(
'controller' => 'parent-category',
'action' => 'sub-category'
)
);
Though the controller and action do not exist, it just tricks CakePHP to use them "AS IS", since the reverse routing isn't working!
And for those (like me), who want have set up a route similar to
Router::connect(
'/go/page:id',
array(
'controller' => 'blog',
'action' => 'paginated'
)
);
There might be difficulty setting up the Paginator options. This, however, worked for me:
$this->Paginator->options(
array(
'controller' => 'go',
'action' => '/'
)
);
I guess you know why it worked ;)
I'm using Zend Framework 1.12 and have this route:
$router->addRoute('item_start',
new Zend_Controller_Router_Route_Regex(
'(foo|bar|baz)',
array(
'module' => 'default',
'controller' => 'item',
'action' => 'start'
),
array(
1 => 'area'
),
'%s'
)
);
Problem is, when I call '/foo' and use the Url Helper in the View, it doesn't give me any parameters:
$this->url(array("page"=>1));
// returns '/foo' (expected '/foo/page/1')
$this->url(array("page"=>1), "item_start", true);
// also returns '/foo'
Any idea how to get the page-parameter into the URL? I can't use the wildcard like in the standard route, can't I?
In addition to David's suggestions, you could change this route to use the standard route class, and then keep the wildcard option:
$router->addRoute('item_start',
new Zend_Controller_Router_Route(
':area/*',
array(
'module' => 'default',
'controller' => 'item',
'action' => 'start'
),
array(
'area' => '(foo|bar|baz)'
)
)
);
// in your view:
echo $this->url(array('area' => 'foo', 'page' => 1), 'item_start');
Your Regex route doesn't have a page parameter, so when the url view-helper ends up calling Route::assemble() with the parameters you feed it, it ignores your page value.
The two choices that come to mind are:
Modify your regex to include a (probably optional with default value) page parameter
Manage the page parameter outside of your route in the query string.