I have the following method which takes a query to search my notes:
function search( $q = null )
{
if ( $q == null )
{
$this->redirect(array('action' => 'index'));
}
$this->paginate = array(
'limit'=>5,
'order'=>'Note.datetime DESC',
'conditions' => array(
'OR' => array(
'Note.title LIKE' => '%'. $q . '%',
'Note.content LIKE' => '%'. $q . '%'
)
),
'Note.status'=>1
);
$this->set('notes', $this->paginate());
$this->render('index');
}
As you can see it takes a single parameter called 'q' which is used to query the model data.
I have hooked up this to the router like so:
Router::connect('/notes',
array('controller'=>'notes','action'=>'index', 'page' => 1),
array(
'pass' => array('page')
)
);
Router::connect('/notes/page/:page',
array('controller' => 'notes', 'action' => 'index'),
array(
'pass' => array('page'),
'page' => '[1-9]+'
)
);
Router::connect('/notes/search/:page/:q',
array('controller'=>'notes','action'=>'search', 'page' => 1),
array(
'pass' => array('page','q')
)
);
Router::connect('/notes/search/:q/page/:page',
array('controller' => 'notes', 'action' => 'search'),
array(
'pass' => array('page','q'),
'page' => '[1-9]+'
)
);
This way I should be getting urls like:
domain.com/notes - loads page 1 of notes
domain.com/notes/page/2 - loads page 2 of notes
domain.com/notes/search/Hello - searches for Hello in notes
domain.com/notes/search/Hello/page/2 - shows page 2 of the above search
The pager in the view looks like:
<?php if(isset($this->request->params['named']['q'])) { ?>
<?php $this->Paginator->options(array('url'=>array('controller' => 'notes', 'action' => $action, 'q' => $this->request->params['named']['q']))); ?>
<?php } else { ?>
<?php $this->Paginator->options(array('url'=>array('controller' => 'notes', 'action' => $action))); ?>
<?php } ?>
It works fine for the index method, but for the search method it is getting confused as when I do a search it's not matching the pager with the route as expected. For example I'm getting urls like domain.com/notes/search/2/:q
Also I don't really like having to wrap the paginator options in an if statement so if I can get it to figure out the url automatically that would be awesome as it's messy having to do this and seems to be the cause of the above problems.
I have connected the named parameter at the top of the router like so:
Router::connectNamed(array('q'));
In the end I opted to make my search working using POST instead of GET so that everything is handled sever-side instead of doing it with messy url rewrites and trying to be clever.
Here's what I made the form look like:
<?php echo $this->Form->create('search', array('url'=>array('controller'=>'notes','action'=>'search'),'class'=>'search')); ?>
<label class="placeholder" for="q">Search</label>
<?php if( isset($q) ) { $term = $q; } else { $term = ''; } ?>
<?php echo $this->Form->input('q', array('label'=>false,'id'=>'q','value'=>$term)); ?>
<button type="submit" class="btn ss-icon ss-search"></button>
<?php echo $this->Form->end(); ?>
the search method:
function search()
{
if ($this->request->is('post')) {
$this->Session->write('q', $this->request->data['search']['q']);
$this->redirect(array('action' => 'search'));
} else {
$q = $this->Session->read('q');
$this->paginate = array(
'limit'=>5,
'order'=>'Note.datetime DESC',
'conditions' => array(
'OR' => array(
'Note.title LIKE' => '%'. $q . '%',
'Note.content LIKE' => '%'. $q . '%'
)
),
'Note.status'=>1
);
$this->set('q',$q);
$this->set('action','search');
$this->set('notes', $this->paginate());
$this->render('index');
}
}
and the routes:
Router::connect('/notes/search',
array('controller'=>'notes','action'=>'search', 'page' => 1),
array(
'pass' => array('page')
)
);
Router::connect('/notes/search/page/:page',
array('controller' => 'notes', 'action' => 'search'),
array(
'pass' => array('page'),
'page' => '[1-9]+'
)
);
and I clean up the session if any other page but the search method is being used in the AppController:
if(strpos($this->here, Router::url(array('controller'=>'notes','action'=>'search'))) === 0 ) {
//echo 'yes';
} else {
$this->Session->delete('q');
}
Which gives me urls like:
domain.com/notes - loads page 1 of notes
domain.com/notes/page/2 - loads page 2 of notes
domain.com/notes/search - searches for Hello in notes (stored in session)
domain.com/notes/search/page/2 - shows page 2 of the above search
Related
Within CakePHP 2 I am using pagination which works great until I see the URL which is page:2, how can I make this ?page=2 ?
The next question is that I use this code for my controller which powers /domain.com/offers/top, /domain.com/offers/newest, /domain.com/offers/popular and then the categories like /domain.com/offers/tv-and-video. The thing is when it is paginated for /domain.com/offers/top instead of being /offers/top/page:2 it goes to /offers/bycategory/top/page:2.
public function bycategory($slug = null)
{
$userId = $this->Session->read("UserAuth.User.id");
if ($slug == 'top') {
//Get the top rated offers
$this->paginate = array(
'limit' => 15,
'order' => array(
'Offer.vote' => 'desc'
)
);
} elseif ($slug == 'newest') {
//Get the latest offers
$this->paginate = array(
'limit' => 15,
'order' => array(
'Offer.created' => 'desc'
)
);
} elseif ($slug == 'popular') {
//Get the most talked about offers
} else {
//This is the categories, so just get the category slug.
$this->paginate = array(
'conditions' => array('Category.slug =' => $slug),
'limit' => 15,
'order' => array(
'Offer.created' => 'desc'
)
);
}
$offers = $this->paginate('Offer');
// pass the value to our view.ctp
$this->set('offers', $offers);
$this->set('userId', $userId);
$this->render('/Offers/index');
}
This is my custom route:
Router::connect(
'/offers/:catslug',
array('controller' => 'offers', 'action' => 'bycategory'),
array(
'pass' => array('catslug')
));
how can I make this ?page=2 ?
By setting the paramType option in paginator component options as mentioned in manual.
You second issue looks like reverse routing issue. Have you setup any custom routes?
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']));
I'm using Cake 2.3.0. If I submit my form using POST, the selected form fields carry over however if I submit my form using GET, all of the form fields return to their default values.
Is there a way to make the GET submission to work like that of the POST?
Here's my contorller:
class ListingsController extends AppController {
public function results() {
$conditions = array(
'Listing.Beds >=' => $this->request->query['beds'],
'Listing.ListingStatus >=' => $this->request->query['status'],
);
$this->paginate = array(
'conditions' => $conditions,
);
$this->set('listings', $this->paginate());
}
}
Here's what my view looks like.
echo $this->Form->create(null, array(
'controller' => 'listings',
'action' => 'results',
'type' => 'get'
));
echo $this->Form->input('name');
$beds = array('1' => '1+', '2' => '2+', '3' => '3+', '4' => '4+', '5' => '5+');
echo $this->Form->input('beds', array('options' => $beds));
$status = array('Active' => 'Active', 'Pending' => 'Pending', 'ActivePending' => 'Active and Pending');
echo $this->Form->input('status', array('options' => $status));
echo $this->Form->end('Update');
So basically if I change 'type' => 'get' to 'type' => 'post' it works just fine. But I need to be able to do this via GET.
Thanks
I agree that this is annoying (IMO CakePHP should be smart enough to automatically determin 'where' to get its data from, based on the 'type').
You'll have to copy the 'query' of the request to the 'data' of the request;
$this->request->data = $this->request->query;
Not behind my computer to test it (as usual, lol), but should probably work.
Try adding this to your controller:
$this->request->data = $this->params['url'];
My solution:
$this->params->data = array('Tablefilter' => $this->params->query);
where "Tablefilter" depends on form definition (usually the model name)
$this->Form->create('Tablefilter', array('type' => 'get'))
Use PRG. Checkout this plugin.
What I ended up doing was looping through the $this->request->query array and pass those values to the matching $this->request->data.
So I added this:
foreach($this->request->query as $k => $v){
$this->request->data['Listing'][$k] = $this->request->query[$k];
}
Which ultimately gave me this:
class ListingsController extends AppController {
public function results() {
foreach($this->request->query as $k => $v){
$this->request->data['Listing'][$k] = $this->request->query[$k];
}
$conditions = array(
'Listing.Beds >=' => $this->request->query['beds'],
'Listing.ListingStatus >=' => $this->request->query['status'],
);
$this->paginate = array(
'conditions' => $conditions,
);
$this->set('listings', $this->paginate());
}
}
I'm not going to accept this as the answer though as I don't know if this is the optimal or suggested way to do it. But it works for me for now.
This works for me :
$this->request->data['Cluster'] = $this->params->query ; //controller side
Form definition:
$this->Form->create('Cluster',array('type'=>'get'));
I have a form with a search input:
echo $this->Form->create('Job', array('url'=>array('controller'=>'jobs', 'action'=>'search'), 'action'=>'search', 'inputDefaults' => array( 'label' => false, 'div'=>false)));
echo '<br/>'.$this->Form->input('search', array( 'label' => false, 'div'=>false));
echo $this->Form->submit('Search', array('div' => false));
in my controller I have the paginate array like so:
public $paginate = array(
'Job' => array(
'fields' => array('Job.id', 'Job.name', 'Job.description', 'Job.tag_words'),
'limit' => 5,
'order' => array(
'Job.name' => 'asc'
)),
);
and then my search function is:
public function search () {
if ($this->request->is('post')) {
$conditions = array();
$search_terms = explode(' ', $this->request->data['Mix']['search']);
foreach($search_terms as $search_term){
$conditions[] = array('Mix.name Like' =>'%'.$search_term.'%');
$conditions[] = array('Mix.description Like' =>'%'.$search_term.'%');
$conditions[] = array('Mix.tag_words Like' =>'%'.$search_term.'%');
}
$searchResults = $this->paginate('Mix', array('Mix.published'=>1, 'OR' => $conditions));
$this->set('searchResults', $searchResults);
}
}
That works for the first page of results but in my view I have the previous/next helpers and clicking to go to page two just brings up an empty page. How am I meant to keep the search term and be able to go to page two?
The Paginator::sort method uses a get to pass the parameters, you should pass the search term as GET then in your view, before you call the pagination links try this:
$this->Paginator->options = array(
'url' => $this->passedArgs
);
That should have your search term persist with your pagination links.
I have a "little" problem with the Pagination system of CakePHP (1.2). Here is the query:
$this->paginate = array (
'fields' => array (
'Content.slug',
'Content.title',
'Content.resume',
'Content.format',
'Content.image',
'Content.video',
'Criteria.name'
),
'conditions' => $conditions,
'order' => 'Content.created DESC',
'limit' => 10,
'contain' => array (
'Category',
'Criteria',
)
);
$this->set("PRODUCTS", $this->Paginate("Content"));
And the code of view:
<?php $total_pages = (int)$paginator->counter(array('format' => '%pages%')); ?>
<?php if($total_pages > 1){ ?>
<div class="paginar">
<div class="next_pre_arrow">
<?=$paginator->prev("Anterior", array("class" => "pre", "escape" => false))?>
<?=$paginator->next("Siguiente", array("class" => "next", "escape" => false))?>
<div class="pages">
<span>Página</span> <?=$paginator->numbers(array('separator' => ' | '))?>
</div>
</div>
</div>
<?php } ?>
What is the problem? The pagination works OK but with a little problem. In the "next" and "prev" buttons, and in the page numbers, the URL is truncated, deleting the last param, for example:
"http://www.domain.com/controller-name/caction-name/option-1/option-2"
Show paging links with this URL:
"http://www.domain.com/controller-name/caction-name/option-1/page:2"
NOT the correct:
"http://www.domain.com/controller-name/caction-name/option-1/option-2/page:2"
What is the cause of this?
I think you can customize the links that are generated by the Paginator helper using the options() method.
Specifically, you can use $options['url'] to pass a custom URL, as if you were setting parameters of a link() call:
$paginator->options(array(
'url' => array(
'controller' => 'YourController',
'action' => 'your_action'
'param1' => 'value_1',
'param2' => 'value_2',
)));