I use find method which use "FORCE INDEX" in Model. Model is fine, but when I make a test for that find method, the SQL error happened.
I use test/fixture and define DB schema and data. In the test/fixture, I don't know how to define index. Therefore, DB for test didn't have index.
It would be great if you could show me how to define index in test/fixture.
In Model...
$this->Model->find('all', array(
'fields' => array('foo'),
'conditions' => array('foo' => foo),
'joins' => array('FORCE INDEX(foo)'),
);
In test/fixture
var $fields = array(
'id' => ....
'foo' => ....
'created' => ....
'modified' => ....
);
i think that ll help u:
http://cakephp.1045679.n5.nabble.com/Using-USE-INDEX-or-FORCE-INDEX-in-Model-gt-find-amp-relationships-td3281552.html#a3300205
"1- Create my own datasource - (in my case extending DboMysql)
data source has two tasks:
its overrides read method and checks if model has set useIndex field
if (!empty($model->useIndex)) {
$this->useIndex = $model->useIndex;
}
return parent::read($model, $queryData);
and it overrides renderStatement method and if $model->useIndex field was set, adding its value after table alias in select statement.
if (strtolower($type) == 'select' && !empty($this->useIndex)) {
$res = "SELECT {$fields} FROM {$table} {$alias} {$this->useIndex} {$joins} {$conditions} {$group} {$order} {$limit}";
} else {
$res = parent::renderStatement($type, $data);
}
$this->useIndex = null;
return $res;
2- Setting up model field whitch contains sql part of use index, force index or ignore index
eg in controller:
$this->Task->useIndex = 'IGNORE INDEX(ind_usr_id)';
$this->paginate = array(
'fields' => array('Task.id', 'Task.name','User.id', 'User.name'),
'order' => 'Task.id',
'limit' => 10
);
$this->paginate('Task');
"
Related
I'm using Cakephp V2.0 and having huge application working on it. Below is the issue I'm facing.
Select Query is what automatically set condition like "TableName.deleted" != 1
Which does the creating issue actually, I want to add my custom condition in this query to get all soft-deleted records i.e.: "TableName.deleted" == 1.
But when we are using $this->paginate() function, it will append default condition at the end of MySQL query condition and then MySQL query will look like this:
("TableName.deleted" == 1) AND "TableName.deleted" != 1 Order By xyz
So it's retrieving the record set only by taking last condition and return only records which are not deleted.
How do I remove this default CakePHP condition ("TableName.deleted" != 1)?
Edited (added code):
if (isset($this->passedArgs['showdeleted']) && $this->passedArgs['showdeleted'] == 1) {
$displayConditions['AND']['tableName.deleted'] = "1";
} else {
$displayConditions['AND']['tableName.deleted'] = "0";
}
$this->paginate = array(
'conditions' => $displayConditions,
'fields' => $this->displayFields,
'limit' => $show_page,
'group' => 'tableName.id',
'contain' => array(
'tbl1',
'tbl2',
'tbl3',
'tbl4',
'tbl5',
),
);
$returnRecords = $this->paginate();
What should I do to resolve this?
Thanks for your suggesting and comments.
Actually i got the answer for the same. we have created behavior for the same
SoftDeletableBehavior.php
In which we have defined related methods which was appending the condition "TableName.deleted" != 1. and it was also mentioned in Model as well like below:
var $actsAs = array(
'SoftDeletable' => array(
'field' => 'deleted',
'find' => true
),
);
so i just made one very minor change and it's working.
var $actsAs = array(
'SoftDeletable' => array(
'field' => 'deleted',
'find' => false
),
);
I'm new to CakePHP and I'm still learning the basics, through working in a live project and taking help from the CakePHP documentations when necessary. Currently, I'm having the following problem : I've recently changed my database table name and structure, so I was forced to change my view, controller and model names. After changing names, whenever I run the index.ctp page, I get the following error:
Fatal error: Call to undefined method stdClass::read() in C:\wamp\www\sdb\app\controllers
\home_loan_distributions_details_controller.php on line 32
Previously, my view folder was named home_loan_distributions, now it's renamed to home_loan_distributions_details.
My previous controller name was home_loan_distributions_controller.php and current name is home_loan_distributions_details_controller.php. The codes:
class HomeLoanDistributionsDetailsController extends AppController {
var $name = 'HomeLoanDistributionsDetails';
function index() {
$user = $this->Session->read('User');
$user_role = $user['User']['user_role_id'];
$actions = $this->Session->read('actions');
$display_actions = array();
foreach ($actions as $action) {
array_push($display_actions, $action['pm_controller_actions']['name']);
}
$this->set('display_actions', $display_actions);
$this->set('user_role', $user_role);
$branch_id = 18;
$this->set('branch_id', $branch_id);
$conditions = array('branch_id' => $branch_id);
$this->set('HomeLoanDistributionsDetails', $this->paginate($conditions));
$this->HomeLoanDistributionDetail->Branch->recursive = 0;
$this->set('BranchDetailInformation', $this->HomeLoanDistributionDetail->Branch->read(array('Branch.id', 'Branch.name', 'RegionalOffice.name', 'DistrictOffice.name', 'SubDistrictOffice.name', 'ClusterOffice.name'), $branch_id));
}
My model was previously named home_loan_distribution.php and now it's named home_loan_distribution_detail.php. The codes:
class HomeLoanDistributionDetail extends AppModel {
var $name = 'HomeLoanDistributionDetail';
var $actsAs = array('Logable' => array(
'userModel' => 'User',
'userKey' => 'user_id',
'change' => 'list', // options are 'list' or 'full'
'description_ids' => TRUE // options are TRUE or FALSE
));
var $validate = array(
'entry_date' => array(
'rule' => 'date',
'message' => 'Enter a valid date',
'allowEmpty' => true
),
'branch_id' => array('numeric'),
'customer_id' => array('numeric'),
'loan_amount' => array('numeric'),
'service_charge' => array('numeric'),
'security' => array('numeric'),
'loan_taken_term' => array('numeric'),
'purpose_id' => array('numeric'),
'installment_amount' => array('numeric'),
'installment_service_charge' => array('numeric'),
);
//The Associations below have been created with all possible keys, those that are not needed can be removed
var $belongsTo = array(
'Branch' => array(
'className' => 'Branch',
'foreignKey' => 'branch_id',
'conditions' => '',
'fields' => 'id,name',
'order' => ''
)
);
function paginate($conditions, $fields, $order, $limit, $page = 1, $recursive = null, $extra = array()) {
$recursive = 0;
$group = $fields = array('branch_id', 'entry_date');
$order = array('entry_date DESC');
$limit = 4;
$this->paginateCount($conditions);
return $this->find('all', compact('conditions', 'fields', 'order', 'limit', 'recursive', 'group'));
}
function paginateCount($conditions = null, $recursive = 0, $extra = array()) {
$recursive = 0;
$group = $fields = array('branch_id', 'entry_date');
$order = array('entry_date DESC');
$results = $this->find('all', compact('conditions', 'fields', 'order', 'limit', 'page', 'recursive', 'group'));
return count($results);
}
}
What my guess is: probably I messed up the naming conventions while renaming everything. The problem is definitely within this line in the controller:
$this->set('BranchDetailInformation',
$this->HomeLoanDistributionDetail->Branch->read(array('Branch.id', 'Branch.name',
'RegionalOffice.name', 'DistrictOffice.name', 'SubDistrictOffice.name', 'ClusterOffice.name'),
$branch_id));
Whenever I comment out this line, I stop getting the above mentioned error message and my view page loads (although that still have some data missing - because I need those Branch related data to be displayed in my view.)
I can't figure out what exactly my problem is, but at least I know where it is. I need someone to pinpoint it.
My CakePHP version is 1.2.5, PHP version - 5.2
There is no function read for model.If you want to find some model data then try with -
$this->set('BranchDetailInformation', $this->HomeLoanDistributionDetail->Branch->find('all', $consitions);
$conditions will be the array of all requirements you want to provide. See the docs for more info.
Apparently, the problem seemed to be related to my 'className' => 'Branch' element of the Branch array used in my model, since the stdClass::read() method is related to classes and not models. But I discovered that the problem was elsewhere. This error was part of that problem, but it itself is not the actual problem.
I figured out this morning that my Model name is HomeLoanDistributionDetail, but my table name is home_loan_distributions_details (because someone else has changed the table name). CakePHP convention requires corresponding table name to be plural and model class name to be singular and CamelCased.
Quoting from the CakePHP Cookbook:
Model class names are singular and CamelCased. Person, BigPerson, and ReallyBigPerson are all examples of conventional model names.
Table names corresponding to CakePHP models are plural and underscored. The underlying tables for the above mentioned models would be people, big_people, and really_big_people, respectively.
Considering the above convention, I just had to rename my model class name from HomeLoanDistributionDetail to HomeLoanDistributionsDetail, in order to match with the table name home_loan_distributions_details. Also, I had to change the model file name from home_loan_distribution_detail.php to home_loan_distributions_detail.php.
After doing that, I stopped getting the error and I was successful in retrieving data from table and viewing it.
I'm asking myself something... Here is the question :
I have a model Request. This model has a status. (let's say it is an integer)
As the status will change through the time AND as I want to keep a record of each changes this was pretty clear in my mind that the status will not be a field of the Request table/model.
I thought I will add a table/model RequestStatus and say to CakePHP
Request hasMany RequestStatus
Then I thought that it will be simple to retrieve all the Request that has a valid status only.
But I was wrong.
Using containable is not good as it retrieve the Request even if they have a wrong status (or even if they don't have a status).
So, I tested 2 solutions :
Binding on the fly the RequestStatus model as a hasOne relation but
it not works as expected
Use a joins key/array when making my find call.
There is clearly something that I missed because, this is not working.
Am I missing something ? Or the best way to do it is by having a status field in the Request table ?
EDIT : What I forgot to mention is that I need to use this query with the pagination of CakePHP
I will assume a couple of things you didn't mention. First of all, I suppose you also created a RequestStatus belongsTo Request relationship. And I imagine your RequestStatus table have four columns: id, status, request_id and created. The request_id column is the foreign key and created is the magic field that CakePHP populates when a new row is created. I also assume you are using Containable behaviour.
With that, you can go three different ways:
1) You can retrieve last valid Statuses along with their Requests.
In your RequestModel:
public function getValidRequests() {
$db = $this->RequestStatus->getDataSource();
$subquery = $db->buildStatement(array(
'fields' => array('request_id', 'max(created) as lastcreated'),
'table' => $db->fullTableName($this->RequestStatus),
'alias' => 'temp',
'group' => array('request_id')
), $this->RequestStatus);
return $this->RequestStatus->find('all', array(
'contain' => array('Request'),
'joins' => array(
array(
'table' => '(' . $subquery . ')',
'alias' => 'rs2',
'type' => 'right',
'conditions' => array(
'RequestStatus.request_id = rs2.request_id',
'RequestStatus.created = rs2.lastcreated'
)
)
),
'conditions' => array('RequestStatus.status' => 1),
'order' => array('RequestStatus.request_id' => 'desc')
));
}
2) You can retrieve all Requests with their Status, then return only those with last valid status.
In your RequestModel:
public function getValidRequests() {
$requests = $this->find('all', array(
'contain' => array(
'RequestStatus' => array(
'order' => array('RequestStatus.created' => 'desc')
)
)
));
$result = array();
foreach ($requests as $request) {
if ($request['RequestStatus'][0]['status'] == 1)
$result[] = $request;
}
return $result;
}
3) Denormalization.
Add a last_status column in your Request table. So every time you update a request's status, insert that new status into your RequestStatus table and save that new status to Request.last_status. Now you can simply do:
public function getValidRequests() {
return $this->find('all', array(
'conditions' => array('Request.last_status' => 1)
));
}
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.
I have a model association that works as followed:
Users hasAndBelongsToMany Tags
I want to be able to paginate a group of users based on their tags. Users can have many tags and filter requested can be complex i.e. a query can ask for all the users with tags A and B but not C. Its way more manageable to handle this by using pagination on tags (so that the condiditions array can apply to the Tag model) and getting back the users that are within that tags subset.
The issue then comes when trying to paginate, because I set up the pagination as followed:
$this->paginate = array(
'conditions' => $conditions,
'contain' => array('User'),
'limit' => 5,
);
$this->paginate('Tag')
So then pagination does exactly what is expected, it returns the array for pagination based on tags, but what I want is to paginate by the users. Switching the pagination to hinge on the users creates its own set of problems ('that is what I was using before'). Just pagination exist for model relates like this? I feel like this is the type of thing cakephp might be able to do. Thank you to anyone who helps.
Place this into the model.
function paginateCount($conditions = null, $recursive = 0, $extra = array()) {
$parameters = compact('conditions');
$this->recursive = $recursive;
$count = $this->find('count', array_merge($parameters, $extra));
if (isset($extra['group'])) {
$count = $this->getAffectedRows();
}
return $count;
}
You should use custom finder: cakephp documentation link. In you example in Users Model create protected function:
protected function _findUsersByTag($state, $query, $results = array()) {
if($state == 'before') {
$query['joins'] = array(
'Tag' => array('type' => 'inner', 'alias' => 'Tag', 'table' => 'tags', 'conditions' => array('Users.tag_id = Tag.id')
);
return $query;
}
return $results;
}
Second add in Users Model:
public $findMethods = array('usersByTag' => true);
And in the controller you definie you pagination to use custom finder using 0 index of array or in 2.3 and higther cake version property called findType:
public $pagination = array('User' => array(
'usersByTag',
'limit' => 5,
'conditions' => $conditions
));