I have a rather complex query, that I'm using CSqlDataProvider with:
$sql = "
SELECT c.id AS id,
c.enabled AS enabled,
s.store_name AS store_name,
s.store_url AS store_url,
c.verified AS verified,
r.rating_total AS rating
FROM compete_settings c
LEFT JOIN stores s
ON c.compete_id = s.id
LEFT JOIN (
(SELECT store_id, rating_total FROM amazon_sellers)
UNION
(SELECT store_id, rating_total FROM ebay_sellers)
) AS r
ON c.compete_id = r.store_id
WHERE c.store_id = :store_id
";
$itemCount = Yii::app()->db->createCommand("SELECT COUNT(id) FROM compete_settings WHERE store_id = $store_id")->queryScalar();
return new CSqlDataProvider($sql, array(
'totalItemCount' => $itemCount,
'params' => array(
'store_id' => $store_id,
),
'sort' => array(
'attributes' => array ( 'enabled, store_name, rating' ),
'defaultOrder' => array('store_name'=>false)
),
'pagination' => array('pageSize' => Yii::app()->user->getState('pageSize_cs', Yii::app()->params['defaultPageSize']),),
));
The query works great. However, the columns are not sortable.
I've made sure to follow Yii CGridview sorting with CSqlDataProvider, but they are still not clickable column headers.
Here are my column arrays:
'columns' => array(
array(
'header' => 'Enabled',
'type'=>'raw',
'name'=>'enabled',
'htmlOptions'=>array('class'=>'enabled-column'),
'value' => 'CHtml::checkbox("", $data["enabled"], array("id"=>"c{$data[\'id\']}", "class"=>"enable-switch"))',
),
array(
'header' => 'Name',
'htmlOptions' => array('class' => 'account-name-column'),
'type' => 'raw',
'name'=>'store_name',
),
array(
'header' => 'Store Rating',
'name'=>'rating',
),
),
Help? ):
Turns out unlike what was mentioned in the question I followed, Yii accepts the attributes param as an array of values and not an array with one CSV value.
So:
'sort' => array(
'attributes' => array ( 'enabled', 'store_name', 'rating' ),
'defaultOrder' => array('store_name'=>false)
),
And not:
'sort' => array(
'attributes' => array ( 'enabled, store_name, rating' ),
'defaultOrder' => array('store_name'=>false)
),
Related
I need to do the following query using the CakePHP find method:
SELECT * FROM `ads`
INNER JOIN fields_values ON fields_values.ref_id = ad.id
WHERE ad.active = 1
I tried this function, but she doesn't work :
$ads = $this->Ad->find('all', array(
'joins' => array(
array(
'table' => 'fields_values',
'alias' => 'fv',
'type' => 'INNER',
'conditions' => array(
"Ad.id = fv.ref_id "
)
)
),
'conditions' => array(
'Ad.active' => 1
),
));
your query is wrong it should be
SELECT * FROM `ads`
INNER JOIN fields_values as fv ON fv.ref_id = ads.id
WHERE ads.active = 1
Now you can build it up with cake query. Which left join also does it.
$ads = $this->Ad->find('all', array(
'conditions' => array(
'Ad.active' => 1
),
'joins' => array(
array(
'table' => 'fields_values',
'alias' => 'fv',
'type' => 'LEFT',
'conditions' => array(
"fv.ref_id = Ad.id",
// or you can add the top condition here
//'Ad.active' => 1
)
)
),
));
New to CakePHP etc and I'm massively confused with following problem so any guidance would be greatly appreciated. Essentially I'm having problems with hasAndBelongsToMany relationships and I don’t know if I’m going about it correctly as I’m doing most of the work inside one controller and one model.
I have a client’s pages, clients have many jobs (this works), clients belong to a client type (this works), clients also have many case studies (also works) and clients have jobs (fine).
Jobs have and belong to many disciplines – this doesn’t work, however, it appears as though the queries are being run (the SQL output in debug mode shows this, so I ran the SQL directly into MySQL - it queries fine) but Cake is not providing me the data into the clients array.
Here is the code for my Client Model and Controller.
Client.php (Model)
public $belongsTo = array(
'ClientType' => array(
'className' => 'ClientType',
'foreignKey' => 'type_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);
public $hasMany = array(
'CaseStudy' => array(
'className' => 'CaseStudy',
'foreignKey' => 'main_contractor',
'conditions' => '',
'fields' => '',
'order' => ''
),
'Job' => array(
'className' => 'Job',
'foreignKey' => 'client_id',
'conditions' => '',
'fields' => '',
'order' => 'Job.id desc'
)
);
ClientsController.php (Controller)
$options['joins'] = array(
array('table' => 'case_studies',
'alias' => 'CaseStudy',
'type' => 'LEFT',
'conditions' => array(
'CaseStudy.client_id = Client.id',
)
),
array('table' => 'jobs',
'alias' => 'Job',
'type' => 'LEFT',
'conditions' => array(
'Job.client_id = Client.id',
)
),
array('table' => 'sectors',
'alias' => 'Sector',
'type' => 'LEFT',
'conditions' => array(
'Job.sector_id = Sector.id',
)
),
array('table' => 'disciplines_jobs',
'alias' => 'DisciplinesJobs',
'type' => 'LEFT',
'conditions' => array(
'Job.id = DisciplinesJobs.job_id',
)
),
array(
'table' => 'disciplines',
'alias' => 'Discipline',
'type' => 'LEFT',
'conditions' => array(
'DisciplinesJobs.discipline_id = Discipline.id'
)
)
);
$options['conditions'] = array('Client.id' => $client_id);
$clients = $this->Client->find('all', $options);
Output of $clients array above:
array(
(int) 0 => array(
'Client' => array(
'id' => '47',
'type_id' => '2',
'name' => 'Balfour Beatty',
'logo' => '1361786198_thumbnail_balfour beatty.jpg',
'website_url' => 'http://www.google.com',
'date_added' => '2013-02-25 10:56:38',
'date_modified' => '2013-02-25 10:56:38'
),
'ClientType' => array(
'id' => '2',
'name' => 'Constructors'
),
'CaseStudy' => array(
(int) 0 => array(
'id' => '23',
'client_id' => '47',
'sector_id' => '1',
'name' => 'Shoreham Academy',
'header_image' => '1365088787_thumbnail_1365088787_header copy.jpg',
'main_contractor' => '47',
'architect' => 'Architecture PLB',
'project_value' => '565000',
'scope_of_works' => '<table></table>',
'text' => '<p><</p>',
'type' => 'flooring',
'date_added' => '2013-04-04 11:19:47',
'date_modified' => '2013-04-04 11:19:47'
)
),
'Job' => array(
(int) 0 => array(
'id' => '1',
'client_id' => '47',
'sector_id' => '2',
'project' => 'Shoreham Academy (Project not case study)',
'date' => '2012-10-19',
'cost' => '£416k',
'quantity_of_flooring' => '7000m',
'date_added' => '2013-08-06 21:46:59',
'date_modified' => '2013-08-06 21:47:01'
)
)
)
Notice the above $clients array doesn’t have any disciplines data from the discipline_jobs table but the SQL output is which runs successfully:
SELECT `Client`.`id`, `Client`.`type_id`, `Client`.`name`, `Client`.`logo`, `Client`.`website_url`, `Client`.`date_added`, `Client`.`date_modified`, `ClientType`.`id`, `ClientType`.`name`
FROM `ar_flooring`.`clients` AS `Client`
LEFT JOIN `ar_flooring`.`case_studies` AS `CaseStudy` ON (`CaseStudy`.`client_id` = `Client`.`id`)
LEFT JOIN `ar_flooring`.`jobs` AS `Job` ON (`Job`.`client_id` = `Client`.`id`)
LEFT JOIN `ar_flooring`.`sectors` AS `Sector` ON (`Job`.`sector_id` = `Sector`.`id`)
LEFT JOIN `ar_flooring`.`disciplines_jobs` AS `DisciplinesJobs` ON (`Job`.`id` = `DisciplinesJobs`.`job_id`) LEFT JOIN `ar_flooring`.`disciplines` AS `Discipline` ON (`DisciplinesJobs`.`discipline_id` = `Discipline`.`id`)
LEFT JOIN `ar_flooring`.`client_types` AS `ClientType` ON (`Client`.`type_id` = `ClientType`.`id`)
WHERE `Client`.`id` = 47
I don’t have a discipline model or controller. I don’t have a jobs model or controller because I’m trying to do everything with the clients model and controller – is this correct?
Does anyone know why this is happening?
Hope this makes sense.
Cheers!
It appears that you want to run a
$this->Client->find('all')
at your ClientsController.
In that find all, you want to retrieve a list of Clients, the ClientType each Client belongsTo, the CaseStudy list each Client has, the Job list each Client has, and the Discipline each Job hasAndBelongsTo.
First, as a practitioner of CakePHP, I almost always avoid using the habtm relationship.
The reason is that it is almost always the case that I have extra fields in the join table. In this case, jobs_disciplines is likely to have more than just job_id and discipline_id. Maybe it will also have another field called status.
What I would do is that I will bake the JobsDiscipline model and the Discipline Model.
JobsDiscipline will belong to both Discipline and Job.
Both Discipline and Job will have many JobsDiscipline.
After this, the query to construct is basically just
$this->Client->find('all', array(
'contain' => array('ClientType', 'CaseStudy', 'Job' => array('JobsDiscipline'=>array('Discipline')))
));
This should work even if you set the recursive at your AppModel to be -1.
If this does not work, let me know again.
How to create this query on cakePHP 1.3
SELECT "Redeem_log"."benefit_id", count("Redeem_log"."benefit_id") as JUMLAH FROM "redeem_logs" "Redeem_log"
LEFT JOIN "benefits" "Benefit" ON ("Redeem_log"."benefit_id" = "Benefit"."id")
LEFT JOIN "merchants" "Merchant" ON ("Benefit"."merchant_id" = "Merchant"."id")
LEFT JOIN "merchant_types" "Merchant_type" ON ("Merchant"."merchant_type_id" = "Merchant_type"."id")
WHERE "Redeem_log"."benefit_id" IS NOT NULL AND ("Merchant_type"."merchant_type"='lokal' OR "Merchant_type"."merchant_type"='nasional') GROUP BY "Redeem_log"."benefit_id" ORDER BY "JUMLAH" DESC
I don't want to use belongsTO, hasMany or ect
If I use
var $belongsTo = array(
'Benefit' => array('className' => 'Benefit', 'foreignKey' => 'benefit_id'),
'Merchant' => array('className' => 'Merchant', 'foreignKey' => 'merchant_id')
);
left join is like :
SELECT "Redeem_log"."benefit_id", count("Redeem_log"."benefit_id") as JUMLAH
FROM "redeem_logs" "Redeem_log"
LEFT JOIN "benefits" "Benefit" ON ("Redeem_log"."benefit_id" = "Benefit"."id")
LEFT JOIN "merchants" "Merchant" ON ("Redeem_log"."merchant_id" = "Merchant"."id")
WHERE "Redeem_log"."benefit_id" IS NOT NULL GROUP BY "Redeem_log"."benefit_id"
ORDER BY "JUMLAH" DESC
I would do it this way, the params for the query looks bit scary, but when you look on it in details it makes sense:
$params = array(
'recursive' => -1,
'fields' => array('RedeemLog.benefit_id', 'COUNT(RedeemLog.benefit_id) as JUMLAH'),
'conditions' => array(
array('NOT' => array('RedeemLog.benefit_id' => null)),
array('Merchanttype.merchant_type' => array('lokal', 'nasional')),
),
'joins' => array(
array(
'table' => 'benefits',
'alias' => 'Benefit',
'type' => 'LEFT',
'conditions' => array('RedeemLog.benefit_id = Benefit.id')
),
array(
'table' => 'merchants',
'alias' => 'Merchant',
'type' => 'LEFT',
'conditions' => array('Benefit.merchant_id = Merchant.id')
),
array(
'table' => 'merchant_types',
'alias' => 'Merchanttype',
'type' => 'LEFT',
'conditions' => array('Merchant.merchant_type_id = Merchanttype.id')
),
),
'order' => 'JUMLAH DESC',
'group' => 'RedeemLog.benefit_id',
);
and finaly the cake find
$result = $this->RedeemLog->find('all', $params);
hope it works for you.
Add joins field in params parameter of MOdel::find();
like this
$params = array(
'joins'=>array(
array(
'table'=>'users',
'alias'=>'User',
'type'=>'LEFT/RIGHT/INNER',
'conditions'=>array("$this->Alias.foreignKey=User.pk")
)
)
);
I've been trying to figure out how to add some computed fields to a model in CakePHP. I'm able to achieve the desired result with the following query:
SELECT project_id,
COUNT(*) AS clicks_total,
SUM(CASE WHEN clicks.redirect_url = projects.url THEN 1 ELSE 0 END) AS clicks_redirected,
SUM(CASE WHEN clicks.redirect_url = projects.url THEN 0 ELSE 1 END) AS clicks_not_redirected
FROM clicks
LEFT JOIN projects ON clicks.project_id = projects.id
GROUP BY project_id
If I attempt to execute it as a custom query Cake transforms the result in such a way that it would require much array manipulation to be usable. I tried to do it the Cake way with the following code, but for some reason the calculated fields end up in a separate array which causes strange behavior in the view:
$this->paginate = array(
'Project' => array(
'fields' => array(
'id', 'name', 'url', 'url_mac', 'url_mobile',
'COUNT(*) AS clicks_total',
'SUM(CASE WHEN Click.redirect_url = Project.url THEN 1 ELSE 0 END) AS clicks_redirected',
'SUM(CASE WHEN Click.redirect_url = Project.url THEN 0 ELSE 1 END) AS clicks_not_redirected'
),
'joins' => array(
array(
'table' => 'clicks',
'alias' => 'Click',
'type' => 'LEFT',
'conditions' => array('Click.project_id = Project.id')
)
),
'group' => 'project_id'
));
$this->set('projects', $this->paginate());
Produces the following result:
array(
(int) 0 => array(
'Project' => array(
'id' => '508705c8-126c-48f9-bd9a-6d79d13bb9ea',
'name' => 'Test Project',
'url' => 'http://www.test.com',
'url_mac' => 'http://www.mac.com',
'url_mobile' => 'http://www.mobile.com'
),
(int) 0 => array(
'clicks_total' => '80',
'clicks_redirected' => '35',
'clicks_not_redirected' => '45'
)
),
(int) 1 => array(
'Project' => array(
'id' => '508b1073-2aa8-4895-b8d9-152ed13bb9ea',
'name' => 'Another Project',
'url' => 'http://another.com',
'url_mac' => 'http://anothermac.com',
'url_mobile' => 'http://anothermobile.com'
),
(int) 0 => array(
'clicks_total' => '134',
'clicks_redirected' => '70',
'clicks_not_redirected' => '64'
)
)
)
Does anyone have any ideas for getting the calculated click counts to show up under the Project array?
You can add virtual fields to your model:
Check it out: http://book.cakephp.org/1.3/view/1608/Virtual-fields
In Project model:
public $virtualFields = array('clicks_total' => 'COUNT(*)');
As the title says, I'm having troubles with joins in my find-query, with errors appearing like:
preg_match() expects parameter 2 to be string, array given [CORE/cake/libs/model/behaviors/containable.php, line 301]
I've tried adding joins to the array over there, but it didn't change anything.
These are the options I'm passing to the find method.
$options = array(
'contain' => array(
'Answer' => array(
'conditions' => array('Answer.type' => 'answer'),
'joins' => array(
$this->votesJoin()
),
'Comment' => array(
'conditions' => array('Comment.type' => 'comment'),
'joins' => array(
$this->votesJoin()
)
)
),
'Comment' => array(
'conditions' => array('Comment.type' => 'comment'),
'joins' => array(
$this->votesJoin()
)
),
'User',
'Tag' => array()
),
'joins' => array(
$this->votesJoin()
),
'conditions' => array(
'Question.id' => $id
)
);
return $this->find('first', $options);
with votesJoin() returning the following array.
(
[table] => (SELECT Vote.node_id, SUM(value) as total FROM votes AS `Vote` WHERE 1 = 1 GROUP BY `Vote`.`node_id` )
[alias] => Vote
[conditions] => Vote.node_id = Question.id
)
What I'm trying to do:
Each user can up/downvote a node (question/answer/comment). With the join I'm trying to add the sum of those votes.
database http://github.com/navale/QA/wiki/img/datamodel.png
You should use "joins" only for things that can't be done with Cake model relationship and Containable. I don't know the details of your database, but I think the find operation can be simplified. Why don't you post the schema for these tables on here?
try this:
$options = array(
'contain' => array(
'Answer' => array(
'conditions' => array('Answer.type' => 'answer'),
'Vote' => array(
'fields' => array('SUM(Vote.value)'),
'group' => array('Vote.parent_id')
),
'Comment' => array(
'conditions' => array('Comment.type' => 'comment'),
'Vote' => array(
'fields' => array('SUM(Vote.value)'),
'group' => array('Vote.parent_id')
)
)
),
'Comment' => array(
'conditions' => array('Comment.type' => 'comment'),
'Vote' => array(
'fields' => array('SUM(Vote.value)'),
'group' => array('Vote.parent_id')
)
),
'User',
'Tag' => array()
),
'conditions' => array(
'Question.id' => $id
)
);
You get the Vote sum value for each answer, comment, and comment for answer. (You might need to add 'hasMany' Vote in the Node model if you haven't done that yet)
If instead you want to get one single total sum of Vote for the question, then I'd suggest:
get the list of the answers and comments of the question:
$lvl1 = find('list','fields'=>array('id'),'conditions'=>array('Node.parent_id'=>$id))
then get list of the comments of the answers
$lvl2 = find('list','fields'=>array('id'),'conditions'=>array('Node.parent_id'=>$lvl1))
then just combine the 2 array then do a sum over that.