CakePHP Pagination: how can I sort by multiple columns to achieve "sticky" functionality? - cakephp

I see that this paginate can't sort two columns at the same time ticket is still open, which leads me to believe that what I'm trying to do is not possible without a workaround. So I guess what I'm looking for is a workaround.
I'm trying to do what many message boards do: have a "sticky" function. I'd like to make it so that no matter which table header link the user clicks on to sort, my model's "sticky" field is always the first thing sorted, followed by whatever column the user clicked on. I know that you can set $this->paginate['Model']['order'] to whatever you want, so you could hack it to put the "sticky" field first and the user's chosen column second. The problem with this method is that pagination doesn't behave properly after you do it. The table header links don't work right and switching pages doesn't work right either. Is there some other workaround?

User ten1 on the CakePHP IRC channel helped me find the solution. I told him that if he posted the answer here then I would mark it as the correct one, but he said I should do it myself since he doesn't have a Stack Overflow account yet.
The trick is to inject the "sticky" field into the query's "order" setting using the model's "beforeFind" callback method, like this:
public function beforeFind($queryData) {
$sticky = array('Model.sticky' => 'DESC');
if (is_array($queryData['order'][0])) {
$queryData['order'][0] = $sticky + $queryData['order'][0];
}
else {
$queryData['order'][0] = $sticky;
}
return $queryData;
}

What you can do is code it in the action. Just create the query you want when some parameters exist on the URL. (parameters has to be sent by GET)
For example:
public function posts(){
$optional= array();
if(!empty($this->params->query['status'])){
if(strlower($this->params->query['status']=='des')){
$optional= array('Post.status DESC');
}
else if(strlower($this->params->query['status']=='asc')){
$optional= array('Post.status ASC');
}
}
if(!empty($this->params->query['department'])){
//same...
}
//order first by the sticky field and then by the optional parameters.
$order = array('Post.stickyField DESC') + $optional;
$this->paginate = array(
'conditions' => $conditions,
'order' => $order,
'paramType' => 'querystring',
);
$this->set('posts', $this->paginate('Post'));
}
I have used something similar to filter some data using $conditions instead of $order and it works well.

You can use custom field for sorting and update pagination component.
Controller code
$order['Document.DATE'] = 'asc';
$this->paginate = array(
"conditions"=> $conditions ,
"order" => $order ,
"limit" => 10,
**"sortcustom" => array('field' =>'Document.DATE' , 'direction' =>'desc'),**
);
Changes in pagination component.
public function validateSort($object, $options, $whitelist = array()) {
if (isset($options['sort'])) {
$direction = null;
if (isset($options['direction'])) {
$direction = strtolower($options['direction']);
}
if ($direction != 'asc' && $direction != 'desc') {
$direction = 'asc';
}
$options['order'] = array($options['sort'] => $direction);
}
if (!empty($whitelist) && isset($options['order']) && is_array($options['order'])) {
$field = key($options['order']);
if (!in_array($field, $whitelist)) {
$options['order'] = null;
}
}
if (!empty($options['order']) && is_array($options['order'])) {
$order = array();
foreach ($options['order'] as $key => $value) {
$field = $key;
$alias = $object->alias;
if (strpos($key, '.') !== false) {
list($alias, $field) = explode('.', $key);
}
if ($object->hasField($field)) {
$order[$alias . '.' . $field] = $value;
} elseif ($object->hasField($key, true)) {
$order[$field] = $value;
} elseif (isset($object->{$alias}) && $object->{$alias}->hasField($field, true)) {
$order[$alias . '.' . $field] = $value;
}
}
**if(count($options['sortcustom']) > 0 )
{
$order[$options['sortcustom']['field']] = $options['sortcustom']['direction'];
}**
$options['order'] = $order;
}
return $options;
}

Easy insert 'paramType' => 'querystring',
Show Code Example:
$this->paginate = array(
'conditions' => $conditions,
'order' => array(
'Post.name' => 'ASC',
'Post.created' => 'DESC',
),
'paramType' => 'querystring',
);
$this->set('posts', $this->paginate('Post'));

Related

Retrive data using cakephp

I am new to cakephp and i want to find data that has been created
This is my sql function works
Select Trv_No from Ticket_LO
This is my model in cakephp
$ticket = $this->Ticket->find('first', array('conditions' => array('AND' => array('Ticket.TRV_No' => $trv_no)));
if(empty($ticket))
{
$table_name = 'Ticket_L0';
$this->Ticket->setSource($table_name);
//$ticket = $this->Ticket->find('first', array('conditions' => array('Ticket.TRV_No' => $trv_no)));
$ticket = $this->Ticket->find('first', array('conditions' => array('AND' => array('Ticket.TRV_No' => $trv_no, 'Ticket.HIDDEN_STAT LIKE' =>'0'))));
if(empty($ticket)) { return false; } else { return true; }
}
else
{ return true; }
}
First things first,
You don't need to set $this->Ticket->setSource($table_name); in your model.
You can use $this->find
I'm not sure what do you really want. But, I guess you want something like that.
$ticket = $this->find('first', array(
'conditions' => array(
'Ticket.TRV_No' => $trv_no,
'Ticket.HIDDEN_STAT'=> 0 //if you want to find 0 only, you don't need LIKE. But, if you want some string, you can use something like 'Ticket.HIDDEN_STAT LIKE'=>'yourString%'
)
)
);
And, you can add your condition after that.
if(!empty($ticket)) return true;
else return false;

Cakephp Custom Find Type Pagination

I created a custom find type, and am trying to paginate the results, but the paginator seems to be ignoring the findType setting. Can someone tell me what I'm doing wrong?
(CakePHP 2.X)
In my Controller:
public function list($username=null) {
$this->Paginator->settings = array(
'Question' => array(
'findType' => 'unanswered',
'conditions' => array('Question.private' => 0),
);
$data = $this->Paginator->paginate('Question');
$this->set('data', $data);
);
Custom find type setup in my Model:
public $findMethods = array('unanswered' => true);
protected function _findUnanswered($state, $query, $results = array()) {
if ($state == 'before') {
$query['order'] = array('Question.created DESC');
$query['conditions'] = array_merge($query['conditions'], array('Question.date_answered' => ''));
return $query;
$this->log($query);
} elseif ($state == 'after') {
return $results;
}
}
Edit
I can paginate the query if I remove $this->Paginate->settings, and replace it with this:
$this->paginate = array('unanswered');
However, I want to add some additional conditions., this doesn't work:
$this->paginate = array('unanswered' => 'conditions' => array('Question.user_id' => $id, 'limit' => 4)) );
Is this possible?
findType was added to the paginator component in CakePHP 2.3, I was on 2.0
http://api.cakephp.org/2.3/class-PaginatorComponent.html

passing data to view CakePHP

This might be easy, but I have tried a few ideas but no solution at all, I'm no expert with cakePhp but I'm trying to modify a existing project with limited time.
The problem is that I have a entity called 'centro' but I'm using its model on a different controller, 'rw', but I can't get it to pass that data to the view at all; it seems it's because the variable gets erased when I use a redirect to go back to the 'index'.
I need that data to generate a google map, the method $this->set(.....) is not working.
Here's the code of the 'rw' controller.
<?php
class RwController extends AppController {
var $name = 'Rw';
// var $paginate = array(
// 'Tip' => array(
// 'limit' => 1,
// 'order' => array(
// 'tip.created' => 'desc'
// ),
// ),
// 'Evento' => array(
// 'limit' => 1,
// 'order' => array(
// 'evento.fecha' => 'desc'
// ),
// )
// );
function map() {
$this->helpers[]='GoogleMapV3';
}
function pageForPagination($model) {
$page = 1;
// $chars = preg_split('/model:/', $this->params['url']['url'], -1, PREG_SPLIT_OFFSET_CAPTURE);
// #print_r($chars);
// if(sizeof($chars) > 1 && sizeof($chars) < 3) {
// #echo "Belongs to ".$model.": \n";
// #echo var_dump($chars);
// }
// $params = Dispatcher::parseParams(Dispatcher::uri());
// echo "<p>".var_dump($params)."</p><br />";
#echo $this->params['named']['model'].$model;
#echo $this->params['named']['page'];
$sameModel = isset($this->params['named']['model']) && $this->params['named']['model'] == $model;
$pageInUrl = isset($this->params['named']['page']);
if ($sameModel && $pageInUrl) {
$page = $this->params['named']['page'];
} else {
#echo var_dump($this->passedArgs);
}
$this->passedArgs['page'] = $page;
return $page;
}
function index() {
$this->loadModel('User');
$this->loadModel('Evento');
$this->loadModel('Tip');
$dataEvento = $this->Evento->find('all');
$dataTip = $this->Tip->find('all');
$page = $this->pageForPagination('Evento');
$this->paginate['Evento'] = array(
'contain' => false,
'order' => array('Evento.fecha' => 'desc'),
'limit' => 1,
'page' => $page
);
$dataEvento = $this->paginate('Evento');
$page = $this->pageForPagination('Tip');
$this->paginate['Tip'] = array(
'contain' => false,
'order' => array('Tip.created' => 'desc'),
'limit' => 1,
'page' => $page
);
$dataTip = $this->paginate('Tip');
$this->set('users', $this->User->find('all'));
$this->set('eventos', $dataEvento);
$this->set('tips', $dataTip);
$this->set('rw');
if(isset($this->params['named']['model'])) {
if (strcmp($this->params['named']['model'], 'Evento') == 0) {
if($this->RequestHandler->isAjax()) {
$this->render('/elements/ajax_rw_evento_paginate');
return;
}
} elseif (strcmp($this->params['named']['model'], 'Tip') == 0) {
if($this->RequestHandler->isAjax()) {
$this->render('/elements/ajax_rw_tip_paginate');
return;
}
}
}
}
function about($id = null) {
$this->Rw->recursive = 0;
$this->set('rw', $this->paginate());
}
function beforeFilter() {
parent::beforeFilter();
$this->Auth->allow(array('*'));
}
function getCentros($id = null ){
$this->loadModel('Centro');
$this->log('getcentros','debug');
$this->log('el id'.$id,'debug');
if( sizeof($id) > 1){
$this->set('centros', $this->Centro->query("SELECT centros.id, name, latitud ,longitud
FROM `centros`,`centrosmateriales`
WHERE centros.id = centro_id
AND material_id ='".$id[0]."'
OR material_id='".$id[1]."'"));
$this->log('size id > 1 ','debug');
}elseif( sizeof($id) >0) {
if($id == 0){
$this->set('centros', $this->Centro->find('all'));
}else{
$this->set('centros', $this->Centro->query("SELECT centros.id, name, latitud ,longitud
FROM `centros`,`centrosmateriales`
WHERE centros.id = centro_id
AND material_id ='".$id[0]."'"));
}
}
$this->Session->write('saludos', 'Saludando');
$this->redirect(array('action' => 'index'));
}
}
?>
I have been thinking about ajax but I'm not sure.
Anyways, thanks in advance.
Using redirect() sends an actual HTTP redirect to the browser. This causes the browser to send another request to the server, to the URL it has been redirected to.
The data that you pass to set() is only available within that single CakePHP request cycle. All PHP data is wiped from memory afterwards. The only way to pass data to the next request is in the URL or in the session.
In your case you should rethink your design and set() all data you need in the index() action. You could possibly move the logic from getCentros() into your Centro model. Then, in your index() function, you could just do
$this->set(
'centro',
$this->Centro->getCentros();
);

how to tell cakephp to use function index($type) to just go to the index page?

I have this application that directs users to Types of attractions with this function:
public function index($type=null) {
$this->set('title','What to do when you visit Gulf Shores');
$this->paginate['Attraction']=array(
'limit'=>9,
'order'=>array('Attraction.id'=>'asc'),
'conditions'=>array(
'active'=>1,
'attr_type'=>$type
)
);
$c=$this->paginate('Attraction');
$this->set('attractions', $c);
}
and it works great, but I'd like users to also be able to go to a front page /attractions/ that doesn't filter out by attr_type. This function shows zero results (as obviously $type still = null) for the front page. Is there a step I'm missing or must I have a view.ctp file and function in my controller?
You could use an if statement to determine the conditions:
public function index($type = null) {
$this->set('title', 'What to do when you visit Gulf Shores');
$conditions = array(); //create $conditions outside of the if statement
if ($type) { //if $type is equal to anything other than null or 0
$conditions = array(
'active' => 1,
'attr_type' => $type
);
} else {
$conditions = array(
'active' => 1
);
}
$this->paginate['Attraction'] = array(
'limit' => 9,
'order' => array('Attraction.id' => 'asc'),
'conditions' => $conditions
);
$c = $this->paginate('Attraction');
$this->set('attractions', $c);
}
It's not actually necessary to create $conditions outside of the if statement in PHP but it is in a lot of other programming languages because of scope.
If you create a variable inside a if statement is it available outside the if statement?

Never display some records in CakePHP

I would like return some records from my base (eg. users roles)
And I use usually function find(), findAll(), etc., and I always must write 'conditions' with like this: not display role admin (name!=admin).
My question is, how I can in RoleModel set for all function will be return with this conditions.
Sorry for english!
Bye!
Use the beforeFind() (http://book.cakephp.org/view/680/beforeFind) callback for this kind of thing. Here's one I use from time to time that ensures only active records are returned:
function beforeFind( $queryData )
{
$conditions = $queryData['conditions'];
if( !is_array( $conditions ) ) {
if( !$conditions ) {
$conditions = array();
}
else {
$conditions = array( $conditions );
}
}
if( !array_key_exists( $conditions, 'active' ) && !isset( $conditions[$this->alias . '.active'] ) ) {
$conditions[$this->alias . '.active'] = 1;
}
return true;
}
That's a bit off the cuff, so the syntax may not be exact, but it should give you something to start with. I think everything's in order except, perhaps, the argument order in a few function calls. Anyway, it should be close.
I think a better solution would be setting the condition in your hasMany relationship.
// User.php Model:
var $hasMany = array('Role' => array('conditions' => array('name <>' => admin)));
and vice versa, you can do it for your Role model:
// Role.php Model:
var $belongsTo = array('User' => array('conditions' => array('User.name <>' => admin)));

Resources