Select specific field of nested HABTM array - php

I have a tickets table, and a contacts table. A ticket can have many contacts.
I am paginating the tickets table, and want it to select specific fields including the first linked contact form the nested array. No matter what I try, I can't seem to figure this one out (or if it is even possible).
My code:
$this->paginate = array(<br>
'conditions' => array('status_id !=' => '3'),<br>
'limit'=>50,<br>
'fields'=>array('Ticket.title', 'Ticket.ticket_number', 'Priority.name', 'Status.name', 'Contact.0.full_name') <br>
);
(The Contact.0.full_name is causing it to fail. How can I make this work?)
So I can use this column with $this->Paginator->sort.

You cant call columns like this Contact.0.full_name CakePHP doesn't work like that. A valid field name is TableAlias.column_name
You cant use hasMany relation's children into sort operation.
To achieve similar functionality, You can drop hasMany and add hasOne to achieve the desired output. Because there's no schema changes in both relationship types.
$this->Ticket->unbindModel(array('hasMany' => array('Contact')));
$this->Ticket->bindModel(array('hasOne' => array('Contact')));
$this->paginate = array(
'conditions' => array('status_id !=' => '3'),
'limit'=>50,
'fields'=>array('Ticket.title', 'Ticket.ticket_number', 'Priority.name', 'Status.name', 'Contact.full_name')
);
Now you can use this column Contact.full_name for sorting purposes.

Related

Select in select relation order in CakePHP 3

I have problem with order in cakephp paginate.
My code view like this:
$list = $this->paginate(
$this->Akwaria->find()
->select(['countDrugs'=>"(SELECT COUNT(`ad`.`id`) FROM `akwaria_drugs` `ad` WHERE `ad`.`id_akwaria` = Akwaria.id)"])
->select($this->Akwaria)
->where($where),['limit'=>'30','order'=>['id'=>'desc']]
);
and in ctp files I have line like this:
<th><?= $this->Paginator->sort('countDrugs', "Podanych Leków") ?></th>
My problem is that it needs to be able to sort the column to the column "countDrugs" But this is not the standard way of working, and in the documentation I can find the instructions as if such a relationship to do it.
By default sorting can be done on any non-virtual column a table has.
This is sometimes undesirable as it allows users to sort on un-indexed
columns that can be expensive to order by. You can set the whitelist
of fields that can be sorted using the sortWhitelist option. This
option is required when you want to sort on any associated data, or
computed fields that may be part of your pagination query:
Control which Fields Used for Ordering
In your case use like this
$this->paginate = [
'sortWhitelist' => [
'countDrugs',
],
];
Its tested and working well

Cakephp 2.8.4 ignoring hasMany

This is a very odd issue that I have not been able to figure out for a very long time.
I have a model Upload, with several hasMany all being ignored by UploadModel.
For example. Upload hasMany Face.
On comments table I have a field called foreign_key that hosts the Upload.id.
So the relationship in UploadModel looks like this:
$hasMany = ['Face' => ['foreignKey' => 'Face.upload_id']];
When performing a find in Upload with contain Face + conditions [Face.celebrity_id = 4018] I get the following error because the query is missing the Left Join to Face:
$media = $this->Upload->find('all',array(
'conditions' => array(
'Face.celebrity_id' => 4018
),
'contain' => array(
'Face'
)
));
SQL Query:
SELECT `Upload`.`id`, `Upload`.`user_id`, `Upload`.`filename`, `Upload`.`created`
FROM `uploads` AS `Upload`
WHERE `Face`.`celebrity_id` = 4018;
SQL Error:
Unknown column 'Face.celebrity_id' in 'where clause'
As you can see the query is missing the left joing to Face, that's all the problem
If instead of in the $hasMany, I add the Face relationship in $belongsTo, it adds the relationship to the query and it works!
But an Upload can have many Faces, and Face.upload_id has the foreignKey to Upload, so IT NEEDS TO BE A motherf***** HASMANY... lol
As you can see, this is terrible, I am already desperate, I have bypassed the issue by adding a bindModel() before each query, but it is totally unnecessary, the hasMany should work!!!! f****!!!!!
The query that I want Cake to perform for me is this:
SELECT `Upload`.`id`, `Upload`.`filename` FROM `uploads` AS `Upload`
LEFT JOIN `upload_faces` AS `Face` ON (`Upload`.id = `Face`.`upload_id`)
WHERE `Face`.`celebrity_id` = 4018
I appreciate any help, thanks.
ok, so basically, it is not ignoring the hasMany, but I can not use conditions on a hasMany relationship, can only be done if it is a hasOne
In your test() function, you run query on upload table. So it can not match Face.celebrity_id field from your query. Two things you have know first:
Condition you are writing in conditions clause is applied on the table matching with your model. In your case, upload is the table on which query is executed and your table contains no field Face.celebrity_id.
$hasMany creates application level relations(associations). So by doing query like you have written in test() function, it doesn't join the query results.
What you can do is,
$this->find('all', array(
'contain' => array(
'Face' => array('conditions' => 'Face.celebrity_id = 4018'))))
This will return all rows of upload table with associated row of face table if celebrity_id is 4018, otherwise null array.
Hope this will help you and if you want that only those many rows will be returned which are associated with Face.celebrity_id = 4018, first you have to run query on face table and then another on first query result.
For more detail of second case you can refer this.

cakephp - how to get the data from a model->model->model type relationship

I've got three models, Equipment which hasmany Booking, which in turn hasmany PaypalTransaction, and I'm trying to find PaypalTransactions which belong to the booking which belongs to the particular listing I'm dealing with, and which was created less than 1 day ago. All this in the Listings Controller
So in my Listings controller I have
$oneDayAgo = date('Y-m-d H:i:s', strtotime('-1 day'));
$toBeConfdBookings = $this->Equipment->Booking->PaypalTransaction->find('all', array('conditions' => array('PaypalTransaction.created' > $oneDayAgo)));
All the models have the appropriate relationships in them as created by cake bake but what ends up in $toBeConfdBookings is all wrong.
Anyone tell me what I'm doing wrong?
Simple answer:
You cannot run a find() on models three-deep like that. Instead, try just loading the model, then running the find():
$this->loadModel('PaypalTransaction');
$oneDayAgo = date('Y-m-d H:i:s', strtotime('-1 day'));
$toBeConfdBookings = $this->PaypalTransaction->find('all', array('conditions' => array('PaypalTransaction.created' > $oneDayAgo)));
(You can only run find()s on loaded models or models that are directly related to a loaded model.)
Answer for how I first interpreted your question:
Normally when you want to pull related results, it's VERY simple - just use CakePHP's Containable Behavior.
What you're trying to do, however, is to get the related model data AND limit the results based on a related model. So, because Containable creates separate queries, you cannot limit based on related models - in that case, you'll need to utilize joins.
Code Example:
$oneDayAgo = date('Y-m-d H:i:s', strtotime('-1 day'));
$this->loadModel('Booking'); // if neccessary
$paypalTransactions = $this->Booking->find('all', array(
'conditions' => array(
'Booking.equipment_id' => $equipmentId
),
'contain' => array(
'Equipment'
),
'joins' => array(
array('table' => 'paypal_transactions',
'alias' => 'PaypalTransaction',
'type' => 'INNER',
'conditions' => array(
'PaypalTransaction.booking_id = Booking.id',
"PaypalTransaction.created > '".$oneDayAgo."'"
)
)
));
The above code basically reads:
The find: find all Bookings that are owned by $equipmentId
The contain: also retrieve the data for the associated Equipment (optional)
The join: limit the results of the Bookings and Paypal Transactions to only those where the transaction occurred after one day ago (and retrieve the transaction data too)
The way you have it now you are basically just calling the PaypalTransaction model and request its find to return you all transactions in the last day.
$this->FirstModel->SecondModel->ThirdModel doesn't mean to connect results from the first model to the second and then to the third, it's only a way to help you load the linked model instead of using $this->loadModel('ThirdModel') to load it.
So you still need to place conditions according to what you want to do. For example, if it was just one model linked (assuming $this->Booking is set to a record) you could do:
$this->Booking->PaypalTransaction->find('all', array(
'conditions' => array(
'PaypalTransaction.created >' => $oneDayAgo,
'PaypalTransaction.booking_id => $this->Booking->id
)
);
You can't extend this to 2 extra models unless your third model contains keys for both previous models which I doubt it does (and probably shouldn't).
CakePHP will autobind models in find conditions for belongsTo and hasOne associations, and for the rest 2 types you need to be joining your tables.
It is a little bit of extra work but you get used to it quickly.
When dealing with deeper associations i think you should look at this link. You can retrieve data from tables by bindModel.
http://mark-story.com/posts/view/using-bindmodel-to-get-to-deep-relations

How to build relations to Multiple IDs separated by comma in Yii

I was creating a form. Certian fields of the form uses checkbox and may return multiple choices and I have to store all of those IDs to a single field separated by a comma or semicolon.
Now what I am looking to is how can I build a relation to that record in Yii Framework.
Usually we use
'groupName' => array(self::BELONGS_TO, 'Lookup', 'group'),
'p_cpu' => array(self::BELONGS_TO, 'Product', 'cpu'),
But how will I do it in the following manner
'p_additionalSoftwares' => array(self::BELONGS_TO, 'Product', 'additionalSoftwares'),
When the additionalSoftwares contain something like 2,8
As this is not a real BELONGS_TO relation with a foreign key you can not use Yii's built in functions.
You may write a custom relation for this case based on CBaseActiveRelation or CActiveRelation, but I have no experience in that, but this may be the cleanest solution.
Another option would be to overwrite the get handler for your attribute, like*
public function getP_additionalSoftwares(){
//Use value from database in attributes and split into array with primary keys
$pks = explode(",",$this->attributes['p_additionalSoftwares']);
//Query database
$models = $this->findByPks($pks);
return $models;
}
In your view:
$model->p_additionalSoftwares
Should return an array of models, like a relation.
Note: This may impact performance, because you may get a large number of subrequests to the database, as the records are all lazy loaded.
**code untested*

adding hasMany association causes find() to not work well

OK, I am a little bit lost...
I am pretty new to PHP, and I am trying to use CakePHP for my web-site.
My DB is composed of two tables:
users with user_id, name columns
copies with copy_id, copy_name, user_id (as foreign key to users) columns.
and I have the matching CakePHP elements:
User and Copy as a model
UserController as controller
I don't use a view since I just send the json from the controller.
I have added hasMany relation between the user model and the copy model see below.
var $hasMany = array(
'Copy' => array(
'className' => 'Friendship',
'foreignKey' => 'user_id'
)
);
Without the association every find() query on the users table works well, but after adding the hasMany to the model, the same find() queries on the users stop working (print_r doesn't show anything), and every find() query I am applying on the Copy model
$copy = $this->User->Copy->find('all', array(
'condition' => array('Copy.user_id' => '2')
));
ignores the condition part and just return the whole data base.
How can I debug the code execution? When I add debug($var) nothing happens.
I'm not an expert, but you can start with the following tips:
Try to follow the CakePHP database naming conventions. You don't have to, but it's so much easier to let the automagic happen... Change the primary keys in your tabel to 'id', e.g. users.user_is --> users.id, copies.copy_id -->copies.id.
Define a view, just for the sake of debugging. Pass whatever info from model to view with $this->set('users', $users); and display that in a <pre></pre> block
If this is your first php and/or CakePHP attempt, make sure you do at least the blog tutorial
Make CakePHP generate (bake) a working set of model/view/controllers for users and copies and examine the resulting code
There's good documentation about find: the multifunctional workhorseof all model data-retrieval functions
I think the main problem is this:
'condition' => array('Copy.user_id' => '2')
It should be "conditions".
Also, stick to the naming conventions. Thankfully Cake lets you override pretty much all its assumed names, but it's easier to just do what they expect by default.
The primary keys should be all named id
The controller should be pluralised: UsersController
First off, try as much as possible to follow CakePHP convention.
var $hasMany = array(
'Copy' => array(
'className' => 'Friendship',
'foreignKey' => 'user_id'
)
);
Your association name is 'Copy' which is a different table and model then on your classname, you have 'Friendship'.
Why not
var $hasMany = array(
'Copy' => array('className'=>'Copy')
);
or
var $hasMany = array(
'Friendship' => array('className'=>'Friendship')
);
or
var $hasMany = array(
'Copy' => array('className'=>'Copy'),
'Friendship' => array('className'=>'Friendship')
);
Also, check typo errors like conditions instead of condition
Your table name might be the problem too. I had a table named "Class" and that gave cake fits. I changed it to something like Myclass and it worked. Class was a reserved word and Copy might be one too.

Categories