Pagination helpers outputting incorrect results - cakephp - php

I have a project model which has many Invoices and Credits. I'm trying to paginate both the Invoices and Credits info in a particular function in my ProjectsController.
No problems getting the correct info paginated:
App::Import('Model', 'Invoice');
$this->Invoice = new Invoice;
$this->Paginator->settings = array(
'conditions' => array('Invoice.paid' => 1, 'Invoice.project_id'=>$id),
'limit' => 5
);
$paidInvoices = $this->paginate('Invoice');
App::Import('Model', 'Credit');
$this->Credit = new Credit;
$this->Paginator->settings = array(
'conditions' => array('Credit.project_id' => $id),
'limit' => 3
);
$credits = $this->paginate('Credit');
The problem is that even though I specify the model like it says in the docs 6 page links will appear: there should only be 2. There will always be a next link even though there might be no page to visit.
echo '<div class="right pagination">'.$this->Paginator->first('<< first', array('model'=>'Credit')).
$this->Paginator->prev('< ' . __('previous'), array('model'=>'Credit'), null, array('class' => 'prev disabled')).
$this->Paginator->numbers(array('separator' => '', 'model'=>'Credit')).
$this->Paginator->next(__('next', array('model'=>'Credit')) . ' >', array('model'=>'Credit'), null, array('class' => 'next disabled')).
$this->Paginator->last('last >>', array('model'=>'Credit')).'</div>';

It looks like CakePHP is getting confused when calculating the number of available results. The $settings property seems like the culprit.
You have to include the key for you model name, as follows:
$this->Paginator->settings['Invoice'] = array(
'conditions' => array('Invoice.paid' => 1, 'Invoice.project_id'=>$id),
'limit' => 5
);
and
$this->Paginator->settings['Credit'] = array(
'conditions' => array('Credit.project_id' => $id),
'limit' => 3
);

Related

Why only last item is saved in database?

I have problem, becouse only last item from loop is saved in database.
Im using CakePhp 2.x
Controller:
for ($x=1; $x <= count($this->request->data['Goodsandoffer'])/3;$x++){
$promID = $this->request->data['Goodsandoffer']['promotionaloffer_id_'.$x];
if($this->request->data['Goodsandoffer']['cenaPromocyjna_'.$x] != ''){
$helperReqestTable3 = array('promotionaloffer_id'=>$this->request->data['Goodsandoffer']['promotionaloffer_id_'.$x],'good_id'=>$this->request->data['Goodsandoffer']['good_id_'.$x],'cenaPromocyjna'=>$this->request->data['Goodsandoffer']['cenaPromocyjna_'.$x]);
$helperReqestTable['Goodsandoffer']=$helperReqestTable3;
debug($helperReqestTable);
$this->Goodsandoffer->save($helperReqestTable);
}
}
Here is how look my debug in loop:
array(
'Goodsandoffer' => array(
'promotionaloffer_id' => '7',
'good_id' => '18',
'cenaPromocyjna' => '1'
)
)
And in next interation:
array(
'Goodsandoffer' => array(
'promotionaloffer_id' => '7',
'good_id' => '19',
'cenaPromocyjna' => '2'
)
)
In database is created only one row with last item.
Model:
class Goodsandoffer extends AppModel {
public $displayField = 'id';
public $belongsTo = array(
'Promotionaloffer' => array(
'className' => 'Promotionaloffer',
'foreignKey' => 'promotionaloffer_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Good' => array(
'className' => 'Good',
'foreignKey' => 'good_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
}
Have you tried calling $this->Goodsandoffer->create() before you call save? That way you're definitely telling Cake to create a new record each time.
The general process of creating and saving data in Cake is:
$this->Model->create()
$this->Model->set($data_array)
$this->Model->save()
You can also eliminate step 2 above by passing you $data_array to the save() function:
$this->Model->create();
$this->Model->save($data_array);
NOTE: from the manual (if you aren't using create()):
When calling save in a loop, don’t forget to call clear().
Another way you could create new data would be to make sure the primary key for your model is null in the data set you pass into save, although this is a little less obvious and probably best to stick to the create/[set/]save flow:
$data = array('id' => null, 'somefield' => 'foobar');
$this->Model->save($data); // new record created
In cakephp 2.0 $this->Model->create() create work fine. But if you are using cakephp version 3 or greater then 3. Then follow the below code
$saveData['itemId'] = 1;
$saveData['qty'] = 2;
$saveData['type'] = '0';
$saveData['status'] = 'active';
$saveData = $this->Model->newEntity($saveData);
$this->Model->save($materialmismatch);
In normal case we use patchEntity
$this->Model->patchEntity($saveData, $this->request->data);
It will only save last values of array so you have to use newEntity() with data

CakePHP paginate and order by

It feels like I've tried everything so I now come to you.
I am trying to order my data but it isn't going so well, kinda new to Cake.
This is my code:
$this->set('threads', $this->paginate('Thread', array(
'Thread.hidden' => 0,
'Thread.forum_category_id' => $id,
'order' => array(
'Thread.created' => 'desc'
)
)));
It generates an SQL error and this is the last and interesting part:
AND `Thread`.`forum_category_id` = 12 AND order = ('desc') ORDER BY `Thread`.`created` ASC LIMIT 25
How can I fix this? The field created obviously exists in the database. :/
You need to pass in the conditions key when using multiple filters (i.e. order, limit...). If you just specify conditions, you can pass it as second parameter directly.
This should do it:
$this->set('threads', $this->paginate('Thread', array(
'conditions' => array(
'Thread.hidden' => 0,
'Thread.forum_category_id' => $id
),
'order' => array(
'Thread.created' => 'desc'
)
)));
or perhaps a little clearer:
$this->paginate['order'] = array('Thread.created' => 'desc');
$this->paginate['conditions'] = array('Thread.hidden' => 0, ...);
$this->paginate['limit'] = 10;
$this->set('threads', $this->paginate());
if you get an error, add public $paginate; to the top of your controller.
Try
$this->set('threads', $this->paginate('Thread', array(
'Thread.hidden' => 0,
'Thread.forum_category_id' => $id
),
array(
'Thread.created' => 'desc'
)
));
I'm not a Cake master, just a guess.
EDIT. Yes, thats right. Cake manual excerpt:
Control which fields used for ordering
...
$this->paginate('Post', array(), array('title', 'slug'));
So order is the third argument.
try
$all_threads = $this->Threads->find('all',
array(
'order' => 'Threads.created'
)
);
$saida = $this->paginate($all_threads,[
'conditions' => ['Threads.hidden' => 0]
]);
There are a few things to take note of in paginate with order. For Cake 3.x, you need :
1) Ensure you have included the fields in 'sortWhitelist'
$this->paginate = [
'sortWhitelist' => [
'hidden', 'forum_category_id',
],
];
2) for 'order', if you put it under $this->paginate, you will not be able to sort that field in the view. So it is better to put the 'order' in the query (sadly this wasn't stated in the docs)
$query = $this->Thread->find()
->where( ['Thread.hidden' => 0, 'Thread.forum_category_id' => $id, ] )
->order( ['Thread.created' => 'desc'] );
$this->set('threads', $this->paginate($query)

Searching for data from model in CakePHP

I am working on my first project experimenting with CakePHP. Basically I have a site where I want users to be able to search through different workouts stored in a MySQL database.
When users enter a search term, I want to simply return any workouts whose names contain the search term. On top of that, I want them to be able to apply filters to narrow down the search to specific muscle groups.
The filters are passed to the search action through a querystring, and the search term is passed using the post method.
First off, here are the two models I care about right now:
class Workout extends AppModel {
public $name = 'Workout';
public $hasAndBelongsToMany = array(
'MuscleGroup' =>
array(
'className' => 'MuscleGroup',
'joinTable' => 'workouts_muscle_groups',
'foreignKey' => 'workout_id',
'associationForeignKey' => 'muscle_group_id',
'unique' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
);
}
class MuscleGroup extends AppModel {
public $name = 'MuscleGroup';
public $hasAndBelongsToMany = array(
'Workout' =>
array(
'className' => 'Workout',
'joinTable' => 'workouts_muscle_groups',
'foreignKey' => 'muscle_group_id',
'associationForeignKey' => 'workout_id',
'unique' => true,
'conditions' => '',
'fields' => '',
'order' => '',
'limit' => '',
'offset' => '',
'finderQuery' => '',
'deleteQuery' => '',
'insertQuery' => ''
)
);
}
Now below is the search action in my WorkoutsController:
function search() {
$hasFilters = false;
$filters = array(
"abs" => false,
"back" => false,
"biceps" => false,
"chest" => false,
"forearms" => false,
"legs" => false,
"shoulders" => false,
"triceps" => false
);
if(isset($this->params['url']['abs']))
if($this->params['url']['abs'] == "1")
$filters['abs'] = $hasFilters = true;
if(isset($this->params['url']['back']))
if($this->params['url']['back'] == "1")
$filters['back'] = $hasFilters = true;
if(isset($this->params['url']['biceps']))
if($this->params['url']['biceps'] == "1")
$filters['biceps'] = $hasFilters = true;
if(isset($this->params['url']['chest']))
if($this->params['url']['chest'] == "1")
$filters['chest'] = $hasFilters = true;
if(isset($this->params['url']['forearms']))
if($this->params['url']['forearms'] == "1")
$filters['forearms'] = $hasFilters = true;
if(isset($this->params['url']['legs']))
if($this->params['url']['legs'] == "1")
$filters['legs'] = $hasFilters = true;
if(isset($this->params['url']['shoulders']))
if($this->params['url']['shoulders'] == "1")
$filters['shoulders'] = $hasFilters = true;
if(isset($this->params['url']['triceps']))
if($this->params['url']['triceps'] == "1")
$filters['triceps'] = $hasFilters = true;
$query = $this->request->data['search'];
//insert code here to actually perform the search!
}
I was really hoping for an easy way to do this but I have been poking around the documentation all night and can't seem to find any relevant examples. In EntityFramework I could simply say something like:
List<Workout> results = context.Workouts.Where(w => w.Name.Contains(searchTerm) && w.MuscleGroups.Any(g => g.Id == mg_id)).ToList();
Note: no idea if that C# syntax is anywhere near correct, but the ease of accessing data from the model was somewhere close to that from what I remember.
How can I get the equivalent of that from CakePHP?
Try to use MuscleGroup IDs for filtering, instead of their names. I guess the user will select them from a list, anyway.
Then, the find() should work like this:
$this->Workout->find(
'all',
array(
'conditions' => array(
'Workout.name LIKE' => '%'.$searchTerm.'%',
// List of included MuscleGroups:
'MuscleGroup.id' => array(1, 5, 9, 17)
)
)
);
Note that you need to take care that the related MuscleGroup is included in the Workout query, either by setting $this->Workout->recursive = 1 or (recommended) by using the more flexible ContainableBehavior.

CakePHP pagination (last param cut)

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',
)));

Cakephp returns empty but sql query has results

I have been fighting with this code:
function getNextActionFObyBalance($when) {
$theQuery = $this->find('first', array(
'fields' => array(
'Contract.id',
'Contract.start_balance'
),
'conditions' => array(
'AND' => array(
'Status.next_action_by' => 'frontoffice',
'Status.status_type' => 'active',
'Status.visibility' => 'frontoffice',
'OR' => array(
'Contract.next_action_on' => null,
'Contract.next_action_on <=' => $when
)
)),
'order' => 'Contract.start_balance DESC',
'recursive' => 0,
));
return $theQuery;
}
I have enabled logging on the MySQL server at this is what the server indicates that CakePHP is requesting:
SELECT `Contract`.`id`, `Contract`.`start_balance` FROM `contracts` AS `Contract` LEFT JOIN `statuses` AS `Status` ON (`Contract`.`status_id` = `Status`.`id`) LEFT JOIN `users` AS `User` ON (`Contract`.`user_id` = `User`.`id`) WHERE ((`Status`.`next_action_by` = 'frontoffice') AND (`Status`.`status_type` = 'active') AND (`Status`.`visibility` = 'frontoffice') AND (((`Contract`.`next_action_on` IS NULL) OR (`Contract`.`next_action_on` <= '2010-09-13 10:13:04')))) ORDER BY `Contract`.`start_balance` DESC LIMIT 1
if I use that in the phpmyadmin tool, I get exactly what I was expecting 1 record with two fields. BUT CakePHP just gives me an empty result set.
Can anyone enlighten me?
PS the code was working but I can figure out what changed!
The problem was with a stub to do some post processing afterFind. The problem is that I have completely forgotten to return $results;
I found the error by doing a step by step debugging down the find method in model.php. Found that the after find was called at some point and went to check my afterFind.
Took my about 4 hours for a simple error but I am learning!
Presumably this method is defined in models/contract.php?
The recursive = 0 statement looks a bit suspect to me. Are the models correctly related in their respective model files?
Have you tried loadModel in case the associations aren't working properly?
It would be useful to see the relationship definitions from the respective models.
--EDIT--
I've formatted the code from your comment here as I can't edit your OP
var $belongsTo = array(
'Status' => array(
'className' => 'Status',
'foreignKey' => 'status_id',
),
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id',
)
);
var $hasMany = array(
'Transaction' => array(
'className' => 'Transaction',
'foreignKey' => 'contract_id',
'dependent' => false,
)
);

Categories