I don't succeed in using the pagination with a custom query. I follow exactly what's in the doc, but it doesn't work. The point is to paginate a list of records, having the flag 'valid' to 'Y' or 'N'.
In the controller:
if (!isset($this->request->query['valid']) || $this->request->query['valid'] == '')
$allFilters['valid'] = 'N';
else
$allFilters['valid'] = 'Y';
$this->paginate = ['finder' => ['curnames' => $allFilters]];
$data = $this->paginate($this->Names)->toArray();
In the model:
public $paginate = ['finder' => 'curnames', 'limit' => 25, 'order' => ['Names.id' => 'asc']];
public function findCurnames(Query $query, array $options) {
$query->where([
'valid' => $option['valid']
]);
return $query;
}
When I execute the code, I get a Cake\Network\Exception\NotFoundException exception. What I am missing?
Update: The version is 3.3. The error is triggered when this is executed:
$data = $this->paginate($this->Names)->toArray();
Update : In the controller, I've change the line
$this->paginate = ['finder' => ['curnames' => $allFilters]];
to
$paginate = ['finder' => ['curnames' => $allFilters]];
and the error doesn't pop up anymore. But the condition filtering on the 'valid'='Y' or 'N' is not taken into account. So it still doesn't work.
I got the answer. The error comes from a typo. Correct code should be
'valid' => $options['valid']
Options with a 's'.
Now it works.
Isn't it easier to do something like this (without any custom finder method):
if ($this->request->getQuery('valid', '') == '')
$valid = 'N';
else
$valid = 'Y';
$this->paginate = [
'limit' => 25,
'order' => ['Names.id' => 'asc']
];
$query = $this->Names->find('all')
->where([
'valid' => $valid
]);
$data = $this->paginate($query)->toArray();
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 have two models called Batch and User
Batch has the following
public $belongsTo = array(
'Customer' => array(
'className' => 'User',
'foreignKey' => 'customer_id',
'conditions' => array('Customer.group_id' => CUSTOMERS),
'fields' => '',
'order' => '',
),
);
When I do the following:
$customers = $this->Batch->Customer->find('list');
I fully expected to get back just the users whose group_id matches CUSTOMERS. It returns ALL the records in the users table.
However, I actually have to write
$customers = $this->Batch->Customer->find('list', array('conditions' => array('Customer.group_id' => CUSTOMERS)));
Is there a way so that the chained model User knows that it is called as Customer by Batch and therefore automatically reads the correct conditions in the associations found in Batch model?
I want to make my code more readable hence the motivation for this question.
I want to write simply
$customers = $this->Batch->Customer->find('list');
or something similarly straightforward.
Of course, I realized that if I do the following:
$batches = $this->Batch->find('all');
The condition stated in the associations will be used. But I don't want to find batches. I want to find just customers.
I am using CakePHP 2.4
I think you can't
but you can create custom find types in User model file
public $findMethods = array('customer' => true); //this enable a custom find method named 'customer'
protected function _findCustomer($state, $query, $results = array()) {
if ($state === 'before') {
$query['conditions'] = array('group_id' => CUSTOMERS);
}
return parent::_findList($state, $query, $results);
}
and in BatchesController
$this->Batch->Customer->find('customer');
There are several ways to do this.
1)
do nothing.
Continue to use code like
$customers = $this->Batch->Customer->find('list', array('conditions' => array('Customer.group_id' => CUSTOMERS)));
2)
create a custom find method as suggested by arilia.
3)
write a getCustomers method inside Batch model
where it looks something like this:
public function getCustomers($type, $query = array()) {
if (empty($query['conditions'])) {
$query['conditions'] = array();
}
$query['conditions'] = array_merge($query['conditions'], array('Customer.group_id' => CUSTOMERS));
return $this->Customer->find($type, $query);
}
then you can call
$customers = $this->Batch->getCustomers('list');
UPDATE:
I have written a Plugin that helps with this kind of behavior, utilizing the 3rd solution.
class Batch extends AppModel {
public $name = 'Batch';
public $actsAs = array('UtilityBehaviors.GetAssoc');
public $belongsTo = array(
'Customer' => array(
'className' => 'User',
'foreignKey' => 'customer_id',
'conditions' => array('Customer.group_id' => 7),
'fields' => '',
'order' => '',
),
);
}
You can fetch just the customer data when you are in BatchesController this way:
$customers = $this->Batch->getAssoc('Customer', 'list');
$customers = $this->Batch->getAssoc('Customer', 'all');
$customerCount = $this->Batch->getAssoc('Customer', 'count');
This behavior has tests at travis and you can read about the tests written at github.
In my model I have this
function pieChart($conditions = null) {
//Get Data for PieChart
$this->RecordDrug->virtualFields['sum'] ='COUNT(*)';
$records = array();
$records=$this->RecordDrug->find('list',
array(
'conditions' => $conditions,
'fields' => array( 'Drug.drug', 'sum'),
'order' => array('sum' => 'desc'),
'contain' => array( 'Drug', 'Record' ),
'group' => 'Drug.Drug'
));
return $records;
}
Which basically means that when this function is called by it's self, there are no conditions set. So inside my controller, I am able to define a condition, if i'd like. I want to do the exact same thing except with
'limit' => $limit,
and I assume I need to set
$limit = null
Inside the parenthesis of the function. I've tried & and , and $limit,$conditions = null
but neither of these options worked. I am not too experience with OOP but I assume there is a way to do this?
EDIT:
Updated code, Still not working. The first varialbe that comes inside the functions parenthesis is the one that works, the second one just act's like it's not there
Model:
function pieChart($limit = null, $conditions = null) {
//Get Data for PieChart
$this->RecordDrug->virtualFields['sum'] ='COUNT(*)';
$records = array();
$records=$this->RecordDrug->find('list',
array(
'conditions' => $conditions,
'fields' => array( 'Drug.drug', 'sum','Record.unit'),
'order' => array('sum' => 'desc'),
'limit' => $limit,
'contain' => array( 'Drug', 'Record' ),
'group' => 'Drug.Drug'
));
debug($records);
return $records;
}
Controller:
$conditions = array('Record.user_id' => $this->Session->read('Auth.User.id'));
$pieChart = $this->Record->pieChart($conditions);
$this->set('output',$pieChart);
Even after this conditions variable, it does not only load the users data. If I were to remove '$limit = null' it will work as intended.
This way:
function pieChartTwo($limit = null, $conditions = null) {
...
}
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.
I've go some validation functions written to check if the user's email exists in the system.
I am getting the following error
Notice (8): Undefined offset: 0 [CORE/cake/libs/model/model.php, line 1122]
This is the code which causes the error
'email' => array(
'emailRule-1' => array(
'rule' => 'email',
'message' => 'email format is incorrect',
'last' => true
),
'emailRule-2' => array(
'rule' => 'checkEmailExist',
'message' => 'email already exists in the system'
)
),
And rule2 seems to be responsible for the error, and here is the rule2:
function checkEmailExist($emailAddress, $user_id){
$this->recursive = -1;
if($user_id > 0){
$user = $this->read(array('email'), $user_id);
if($emailAddress == $user['User']['email'])
return true;
}
$result = $this->find('count', array('conditions' => array('User.email' => $emailAddress)));
return $result > 0 ? false : true;
}
Why not do it like this?
public $validate = array(
'email' => array(
'rule' => array('email', 'isUnique')
)
);
You might want to split it up into two separate rules to apply your own error messages, but this should work just fine.
Did you try to debug what $emailAddress contains?
I bet this is an array^^
function checkEmailExist($emailAddress, $user_id){
$this->recursive = -1;
$email = array_shift(emailAddress);
...
you need to get the child element first
so remember: always a good idea to use debug() or pr() to debug your variables first.