I would like generate list like day=>title:
$news = $this->News->find('list', array(
'conditions' => array(
'News.created LIKE' => '2008-09%'),
'fields' => array(
'DAY(News.created) AS day',
'News.name'),
'recursive' => -1));
...but doesn't work, why?
Function SUBSTR/SUBSTRING also...
When I use find with 'all' property, the function DAY works good!
It won't work because DAY(News.created) is a computed value, but it will probably work if you make it a virtual field.
Note that when you use 'all', each row on the results array will have a [0][day] key for that value, instead of [News][day]. This means Cake won't recognize it as a field from model News, and that's why you can't use it with 'list'.
Related
I am using cakephp 2.4.5. I want to find the last record of an associated table. Below is my code
$Latest = $this->ParentModel->find('first',array(
'conditions' => array('ParentModel.id'=>$id),
'order' => array('ParentModel.AssociatedModel.id' => 'DESC')
));
This code has missing column error with ParentModel.AssociatedModel.id but the id column is definitely there. Does it look right in the first place? If not, how can I fix this part or is there a better implementation? Thank you
Have you tried to read the manual about retrieving data? It is clearly explained there.
If AssociatedModel is somehow associated with one of the four association types with ParentModel, you'll have to use this syntax:
$Latest = $this->ParentModel->find('first',array(
'contain' => array('AssociatedModel'),
'conditions' => array('ParentModel.id' => $id),
'order' => array('AssociatedModel.id' => 'DESC')
));
Make sure the other model is included by using recursive or contain(). See this page about Containable.
If your field name in the DB is really containing a dot CakePHP2 will have a problem with that because it uses the dot notation as separator. At least I've never used a dot in a database field, it's doable but doesn't make much sense and obviously breaks Cake, no idea if you can work around that.
You can't do "cross conditions" though associated models, but you can use Containable behavior to filter associated models.
For this, you have to bind the behavior to your model through:
public $uses = array('Containable');
And then do your query:
$this->ParentModel->find('first', array(
'conditions' => array('ParentModel.id' => $id),
'contain' => array(
'AssociatedModel' => array(
'limit' => 1,
'order' => array('id DESC')
)
)
));
This will give you the parent model, and the last associated model.
I guess it should be like this
$Latest = $this->ParentModel->AssociatedModel->find('first',array(
'conditions' => array('ParentModel.id'=>$id),
'order' => array('AssociatedModel.id DESC')
));
Check if it is correct.
I have this on a HABTM statement:
$this->set('usergroups', $this->User->find('first', array('conditions' => array('User.id' => $this->Auth->user('id')))));
This gets groups that are associated to the user id via a courses_users table.
This works perfectly find, except I also need to find all groups that a user doesn't belong to. How do I get the opposite of the statement above?
I used 'not' as a condition, and it still gave me the same result.
Thanks!
Something like this should work:
$user = $this->User->find('first', array(
'conditions' => array(
'User.id' => $this->Auth->user('id')
)
));
$otherGroups = $this->Group->find('all', array(
'conditions' => array(
'NOT' => array('id' => Hash::extract($user, '{n}.Group.id'))
)
));
Side Note: You should really be setting recursive to -1 in your AppModel and not relying on recursive to return additional data. Instead, use CakePHP's Containable Behavior.
The following query returns an array containing the proper ids, but null for all values.
If I remove the aggregation function (AVG()), it returns values (not the averaged ones of course), if I choose e.g. find('all') it returns the average, but not in the list format I want (I could work with that, but I want to try to do it with 'list' first).
$progress = $this->Trial->find('list', array(
'fields' => array(
'Trial.session_id',
'AVG(Trial.first_reaction_time_since_probe_shown) AS average_reaction_time'
),
'group' => 'Trial.session_id',
'conditions' => array(
'Trial.first_valid_response = Trial.probe_on_top',
'TrainingSession.user_id IS NOT NULL'
),
'contain' => array(
'TrainingSession' => array(
'conditions' => array(
'TrainingSession.user_id' => $this->Auth->user('id')
)
)
),
'recursive' => 1,
));
The generated SQL query returns exactly the result I want, when I send it to the DB via PhpMyAdmin.
SELECT
`Trial`.`session_id`,
AVG(`Trial`.`first_reaction_time_since_probe_shown`) AS average_reaction_time
FROM
`zwang`.`trials` AS `Trial`
LEFT JOIN
`zwang`.`training_sessions` AS `TrainingSession` ON (
`Trial`.`session_id` = `TrainingSession`.`id` AND
`TrainingSession`.`user_id` = 1
)
WHERE
`Trial`.`first_valid_response` = `Trial`.`probe_on_top`
GROUP BY
`Trial`.`session_id`
I've examined the source for find('list'). I think it's due to the "array path" for accessing the list getting screwed up when using functions in the query, but I couldn't fix it yet (or recognise my abuse of CakePHP logic).
Once I posted the question, Stackoverflow started relating the correct answers to me.
Apparently, it can't be done with 'list' without virtualFields.
I didn't expect that because it worked using the other find-types.
$this->Trial->virtualFields = array(
'average_reaction_time' => 'AVG(Trial.first_reaction_time_since_probe_shown)'
);
$progress = $this->Trial->find('list', array(
'fields' => array('Trial.session_id','average_reaction_time')
/* etc... */
));
I'm using the Containable behavior to get a list of Comments (belongsTo Post, which belongs to Question; Question hasMany Post, and Post hasMany Comments; all of these belong to Users).
$data = $this->Question->find ( 'first',
array ('contain' =>
array ('User',
'Post' => array ('User', /* 'order' => 'User.created DESC'*/ )
)
)
);
It works, when I comment out the section in comments above. I suppose this is to be expected, but what I want is all of the Posts that are found, should be sorted in order of the 'created' field of the 'User' they belong to. How do I accomplish this deeper level sorting in CakePHP? I always get, "Warning (512): SQL Error: 1054: Unknown column 'User.created' in 'order clause'"
Thanks for your help!
Also, you might be trying to group on a related table from a find call that doesn't use joins.
Set your debug level to something greater than 1 so you can see the query log and make sure that Cake isn't doing two queries to fetch your data. If that is the case then the first query is not actually referencing the second table.
If you want to manually force a join in these situations you can use the Ad-Hoc joins method outlined by Nate at the following link.
http://bakery.cakephp.org/articles/view/quick-tip-doing-ad-hoc-joins-in-model-find
I have found two ways to get around this.
The first is to define the second level associacion directly in the model.
Now you will have access to this data everywhere.
It should look something like this.....
var $belongsTo = array(
'Foo' => array(
'className' => 'Foo', //unique name of 1st level join ( Model Name )
'foreignKey' => 'foo_id', //key to use for join
'conditions' => '',
'fields' => '',
'order' => ''
),
'Bar' => array(
'className' => 'Bar', //name of 2nd level join ( Model Name )
'foreignKey' => false,
'conditions' => array(
'Bar.id = Foo.bar_id' //id of 2nd lvl table = associated column in 1st level join
),
'fields' => '',
'order' => ''
)
);
The problem with this method is that it could make general queries more complex than they need be.
You can thus also add the second level queries directly into te find or paginate statement as follows: (Note: I found that for some reason you can't use the $belongsTo associations in the second level joins and will need to redefine them if they are already defined. eg if 'Foo' is already defined in $belongsTo, you need to create a duplicate 'Foo1' to make the association work, like the example below.)
$options['joins'] = array(
array('table' => 'foos',
'alias' => 'Foo1',
'type' => 'inner',
'conditions' => array(
'CurrentModel.foo_id = Foo1.id'
)
),
array('table' => 'bars',
'alias' => 'Bar',
'type' => 'inner',
'foreignKey' => false,
'conditions' => array(
'Bar.id = Foo1.bar_id'
)
)
);
$options['conditions'] = array('Bar.column' => "value");
$this->paginate = $options;
$[modelname] = $this->paginate();
$this->set(compact('[modelname]'));
I hope this is clear enough to understand and that it helps someone.
Check your recursive value. If it's too limiting, it will ignore the containable links, IIRC. I remember bumping into this a few times. I'd try containing multiple models, but my recursive option was set to 0 and nothing would get pulled. For your example, I'd think that a value of 1 (the default) would suffice, but maybe you've explicitly set it to 0 somewhere?
You can add before your call to find() the following:
$this->Question->order = 'Question.created DESC';
Yeah, I couldn't work out how to sort based on the related/associated model, so ended up using the Set::sort() method. Checkout this article for a good explanation.
// This finds all FAQ articles sorted by:
// Category.sortorder, then Category.id, then Faq.displaying_order
$faqs = $this->Faq->find('all', array('order' => 'displaying_order'));
$faqs = Set::sort($faqs, '{n}.Category.id', 'ASC');
$faqs = Set::sort($faqs, '{n}.Category.sortorder', 'ASC');
...And yes, it should probably be a Category->find() but unfortunately the original developer didn't code it that way, and I didn't wanna rework the views.
How to write this query using the find statement in cakephp
$this->Form->query("Select id from forms order by id DESC LIMIT 1")
This should do it:
$this->Form->find('all', array(
'fields' => array('Form.id'),
'order' => 'Form.id DESC',
'limit' => 1
));
Have a look at the documentation: http://book.cakephp.org/view/73/Retrieving-Your-Data
I agree with the first response here,
2 things I would like to mention
I assume you have a model called Form,Now Cakephp has its own FormHelper and sometimes there maybe a conflict of names, I got this error when I had created a FilesController in my project,
Also you will get an array back which will be of the form
$result['Form] => array(
[id] => 1
[name] => whatever)
and so on, you can easily use this to get whatever data you want.
While this should be find('first'), the real issue is probably even simpler. I can only assume the only reason for doing such a query is to get the last inserted id. In the same "run" as a save you can get the inserted id in the model property by the same name:
$this->ModelName->create($this->data);
if ($this->ModelName->save()) {
$this->Session->setFlash('Saved it!');
$id_of_new_row = $this->ModelName->id;
$this->redirect(array('action' => 'view',$id_of_new_row));
}
As paolo said, but adding 'recursive' to the query:
$this->Form->find('all', array(
'fields' => array('Form.id'), // just return the id, thank you
'order' => 'Form.id DESC', // sort the query result by id DESC
'limit' => 1, // gimme the top id
'recursive' => -1, // don't scan associated models in the query
));
but I'd also use
$this->Form->find('first', array(
'fields' => array('Form.id'),
'order' => array('Form.id DESC'),
'recursive' => -1,
)
);
Which is not much shorter, but is more expressive of what you want.
And I'd suggest you take care, because there's a form helper already, and confusion could happen. On the other hand, you use the $form variable in views, generally, while this is controller code.