Attaching condition to find() in controller - php

i've been trying to learn more about how to have fat models and skinny controllers the right way, because before my models would have basically no code and i'm trying to change that. My function works, but now i'm trying to combine two find() queries that look almost exactly the same except one of them has a simple condition.
My model looks something like this:
function pieChart() {
//Get Data for PieChart
$this->RecordDrug->virtualFields['sum'] ='COUNT(*)';
$records = array();
$records=$this->RecordDrug->find('list',
array('fields' => array( 'Drug.drug', 'sum'),
'contain' => array( 'Drug', 'Record' ),
'group' => 'Drug.Drug'
));
$this->set('output',$records);
return $records;
}
I will have two controllers using this. One of them will use this code as is, just simply call the pieChart() function. The other controller will have to see a condition that only selects the users entries. So
'conditions' => array('Record.user_id' => $this->Auth->user('id'))
How do I go about this the right way? I think i'm having trouble with this because my OOP knowledge is pretty limited. If anyone has any examples or resources that can help me make my find() functions more efficient and streamlined, i'd really appreciate it.

I done that kind of things very simple:
public function myQuery($conditions = null) {
$this->virtualFields['sum'] ='COUNT(*)';
$result = $this->find('all', array('conditions' => $conditions,
'fields' => array('Drug.drug', 'sum'),
'contain' => array('Drug','Record'),
'group' => 'Drug.Drug'
));
return $result;
}
Now you can call this with your argument:
$conditions = array('Record.user_id' => $this->Auth->user('id'));
$data = $this->RecordDrug->myQuery($conditions);
Or without it:
$data = $this->RecordDrug->myQuery();
Note that in this case you need to put myQuery() in to RecordDrug model and you need to use 'all' instead of 'list', because 'list' doesn't support contain option.
So now if you have additional conditions - you just need to pass it in the argument. If you leave it null - it do the query without the conditions statement.

Related

WP MVC: Pagination in index controller not working

I'm using the WP MVC plugin to create a plugin which provides a database of names as part of my Wordpress website.
The below controller retrieves the right information from 2 database tables, but suddenly pagination is not working anymore. Do I need to use paginate() instead of find()? If yes, how would a similar query look using paginate()? Unfortunately there are no examples available.
Thanks!
names_controller.php
<?php
class NamesController extends MvcPublicController {
public function index() {
$objects = $this->Name->find(array(
'joins' => array('Origin'),
'selects' => array('Origin.id', 'Origin.origin', 'Name.id', 'Name.name', 'Name.gender', 'Name.meaning', 'Name.origin_id'),
'page' => 1,
'per_page' => 20,
'conditions' => array(
'Name.name LIKE' => 'A%'
)
));
$this->set('objects', $objects);
}
}
?>
=================== UPDATE ======================
I replaced the find() with paginate() unfortunately the join doesn't work anymore. Furthermore it IGNORES e.g. page, per_page etc parameters.
Anyone an idea?
$params = $this->params;
$params['page'] = empty($this->params['page']) ? 1 : $this->params['page'];
$params['per_page'] = 20;
$params['joins'] = array('Origin');
$params['selects'] = array('Origin.id', 'Origin.origin', 'Name.id', 'Name.name', 'Name.gender', 'Name.meaning', 'Name.origin_id');
$collection = $this->Name->paginate($this->params);
$this->set('objects', $collection['objects']);
$this->set_pagination($collection);

A better way to re-use a boilerplate search parameter across controller actions

How do I re-use boilerplate query code across multiple controller actions in CakePHP 2.4?
I've got some join code I need to re-use across multiple actions, which excludes all Posts which belong to a Project where Project.published = 0 from my find(). I've done this by creating a public class array to hold the query code.
This works, however I'd like to add some additional parameters based on variables- specifically, allowing the owner of a Project to see data belonging to their project, even if it's unpublished.
If the array were integrated as part of the controller action, I'd simply add 'ProjectAlias.user_id' => CakeSession::read("Auth.User.id") to the final OR array below. However, I can't include that as part of a class array, and I need to create it in the action, as seen below.
This doesn't feel especially elegant. Is there a cleaner / more Cake way to handle this?
My current code:
//==============
// ADDITIONAL JOIN TO RESTRICT RESULTS TO LIVE PROJECTS
//================
public $joins = array(
array(
'table' => 'projects',
'alias' => 'ProjectAlias',
'type' => 'right',
'conditions' => array(
'OR' => array( // One of these two things:
'Post.project_id' => null, // Posts with no project
'AND' => array( // And posts with a Project that is published.
'Post.project_id = ProjectAlias.id',
'OR' => array(
'ProjectAlias.published !=' => 0,
)
)
)
),
)
);
//===============
// Example function showing how this array is used. There are four in all
// so repeating the above code would get to be too much.
//================
public function example() {
// Let project leads see data from their hidden projects, by modifying the array.
// This doesn't seem very elegant!
$this->joins[0]['conditions']['OR']['AND']['OR'][] = array(
'ProjectAlias.user_id' => CakeSession::read("Auth.User.id")
);
// Use the array
$this->paginator->settings['joins'] = $joins;
$this->set('posts', $this->Paginator->paginate());
}
If I understood it right, you can create a function which requires arguments in AppController which returns the join array & call it from any actions of any controllers. Now, regarding different params for different cases, first you can use-
$this->request->action
to get the current action (or controller as well if needed).
Now, you can set an associative array or if else block in the function in AppController to define the join array, using function arguments as required. Then you can get custom made $joins array from any actions.

get a single value from a table using the query() method inside a model

I have a very complex setup on my tables and achieving this via any of the find() methods is not an option for me, since I would need to fix relationships between my tables and I don't have the time right now, so I'm looking for a simple fix here.
All I want to achieve is run a query like this:
SELECT MAX( id ) as max FROM MyTable WHERE another_field_id = $another_field_id
Then, I need to assign that single id to a variable for later use.
The way I have it now it returns something like [{{max: 16}}], I'm aware I may be able to do some PHP on this result set to get the single value I need, but I was hoping there was already a way to do this on CakePHP.
Assuming you have a model for your table and your are using CakePHP 2.x, do:
$result = $this->MyTable->field('id', array('1=1'), 'id DESC');
This will return a single value.
see Model::field()
This example is directly from the CakePHP documentation. it seems you can use the find method of a model to get count
$total = $this->Article->find('count');
$pending = $this->Article->find('count', array(
'conditions' => array('Article.status' => 'pending')
));
$authors = $this->Article->User->find('count');
$publishedAuthors = $this->Article->find('count', array(
'fields' => 'DISTINCT Article.user_id',
'conditions' => array('Article.status !=' => 'pending')
));

Cakephp ClassRegistry::init

I have this code:
$userObj = ClassRegistry::init('User');
$userObj->contain();
$conditions = "User.studio_id = '".$studioID."' AND User.usergroup_id = 5";
$studioAdmin = $userObj->find($conditions);
The one that is causing the error is this line:
$studioAdmin = $userObj->find($conditions);
When I say error, it does not print anything or any warning of error, it just stops the code below it, I noticed that one because when I try to echo a code above it, it prints it, but when I try to echo a code below it, it does not print anything,
What is the problem here. Your help will be greatly appreciated! Thanks! :)
The better practice way of loading models in components is to go via the controller, and use loadModel()
In your component, set up the initialize()
function initialize($controller, $settings) {
$this->Controller =& $controller;
}
Then in your component function, use loadModel to load the model
$this->Controller->loadModel('Modelname');
$this->Modelname->save($data);
and also for find condition
$users = $this->Modelname->find('all', array(
'conditions' => array(
'User.studio_id' => $studioID,
'User.usergroup_id' => 5
)
));
You should be doing this:
$studioAdmin = $userObj->find('all', array( 'conditions' => $conditions ) );
Do you have PHP error messaging turned on? Did you check your logs to see what the specific error is?
Also, by cake standards, it is better to build your conditions clause this way:
$conditions = array(
"User.studio_id" => $studioID,
"User.usergroup_id" => 5"
);

Using the CakeDC search plugin with associated models

I'm using CakePHP 1.3.8, and I've installed the CakeDC Search plugin. I have a Tutorial model, which is in a HABTM relationship with a LearningGoal model.
I have a search action & view in the Tutorials controller with which I can successfully search fields in the Tutorial model. I'd also like to filter my tutorial search results using LearningGoal checkboxes on the same form. I've tried adding various parameters to Tutorial's $filterArgs and TutorialsController's $presetVars. I've also tried moving the relevant $filterArgs to the LearningGoal model. I have not yet been able to successfully trigger the entry for learning goals in $filterArgs.
I think I must be missing something obvious. Or maybe the Search plugin doesn't support what I'm trying to do. Does anyone know how to use this plugin to search on associated models?
So here's what I've figured out. You can combine what's below with the Search plugin directions to search on related models.
The $filterArgs piece in the Tutorial model must look like this:
var $filterArgs = array(
array('name' => 'LearningGoal', 'type' => 'subquery', 'method' => 'findByLearningGoals', 'field' => 'Tutorial.id'),
);
Here's the supporting function in the Tutorial model:
function findByLearningGoals($data = array()) {
$ids = explode('|', $data['LearningGoal']);
$ids = join(',', $ids);
$this->LearningGoalsTutorial->Behaviors->attach('Containable', array('autoFields' => false));
$this->LearningGoalsTutorial->Behaviors->attach('Search.Searchable');
$query = $this->LearningGoalsTutorial->getQuery('all',
array(
'conditions' => array('LearningGoalsTutorial.learning_goal_id IN (' . $ids . ')'),
'fields' => array('tutorial_id'),
)
);
return $query;
}
In TutorialsController, $presetVars should look like this:
public $presetVars = array(
array('field' => 'LearningGoal', 'type' => 'checkbox', 'model' => 'Tutorial'),
);
And in my search action in TutorialsController, I did this:
$this->LearningGoal = $this->Tutorial->LearningGoal;
The Prg component seems to need that.
I am using CakePHP version 2.X
Every time I come to do this in a project I always spend hours figuring out how to do it using CakeDC search behavior so I wrote this to try and remind myself with simple language what I need to do. I've also noticed that although Michael is generally correct there is no explanation which makes it more difficult to modify it to one's own project.
When you have a "has and belongs to many" relationship and you are wanting to search the joining table i.e. the table that has the two fields in it that joins the tables on either side of it together in a many-to-many relationship you want to create a subquery with a list of IDs from one of the tables in the relationship. The IDs from the table on the other side of the relationship are going to be checked to see if they are in that record and if they are then the record in the main table is going to be selected.
In this following example
SELECT Handover.id, Handover.title, Handover.description
FROM handovers AS Handover
WHERE Handover.id in
(SELECT ArosHandover.handover_id
FROM aros_handovers AS ArosHandover
WHERE ArosHandover.aro_id IN (3) AND ArosHandover.deleted != '1')
LIMIT 20
all the records from ArosHandover will be selected if they have an aro_id of 3 then the Handover.id is used to decide which Handover records to select.
On to how to do this with the CakeDC search behaviour.
Firstly, place the field into the search form:
echo $this->Form->create('Handover', array('class' => 'form-horizontal'));?>
echo $this->Form->input('aro_id', array('options' => $roles, 'multiple' => true, 'label' => __('For', true), 'div' => false, true));
etc...
notice that I have not placed the form element in the ArosHandover data space; another way of saying this is that when the form request is sent the field aro_id will be placed under the array called Handover.
In the model under the variable $filterArgs:
'aro_id' => array('name' => 'aro_id', 'type' => 'subquery', 'method' => 'findByAros', 'field' => 'Handover.id')
notice that the type is 'subquery' as I mentioned above you need to create a subquery in order to be able to find the appropriate records and by setting the type to subquery you are telling CakeDC to create a subquery snippet of SQL. The method is the function name that are going to write the code under. The field element is the name of the field which is going to appear in this part of the example query above
WHERE Handover.id in
Then you write the function that will return the subquery:
function findByAros($data = array())
{
$ids = ''; //you need to make a comma separated list of the aro_ids that are going to be checked
foreach($data['aro_id'] as $k => $v)
{
$ids .= $v . ', ';
}
if($ids != '')
{
$ids = rtrim($ids, ', ');
}
//you only need to have these two lines in if you have not already attached the behaviours in the ArosHandover model file
$this->ArosHandover->Behaviors->attach('Containable', array('autoFields' => false));
$this->ArosHandover->Behaviors->attach('Search.Searchable');
$query = $this->ArosHandover->getQuery('all',
array(
'conditions' => array('ArosHandover.aro_id IN (' . $ids . ')'),
'fields' => array('handover_id'), //the other field that you need to check against, it's the other side of the many-to-many relationship
'contain' => false //place this in if you just want to have the ArosHandover table data included
)
);
return $query;
}
In the Handovers controller:
public $components = array('Search.Prg', 'Paginator'); //you can also place this into AppController
public $presetVars = true; //using $filterArgs in the model configuration
public $paginate = array(); //declare this so that you can change it
// this is the snippet of the search form processing
public function admin_find()
{
$this->set('title_for_layout','Find handovers');
$this->Prg->commonProcess();
if(isset($this->passedArgs) && !empty($this->passedArgs))
{//the following line passes the conditions into the Paginator component
$this->Paginator->settings = array('conditions' => $this->Handover->parseCriteria($this->passedArgs));
$handovers = $this->Paginator->paginate(); // this gets the data
$this->set('handovers', $handovers); // this passes it to the template
If you want any further explanation as to why I have done something, ask and if I get an email to tell me that you have asked I will give an answer if I am able to.

Categories