TableAlias__ColumnAlias syntax in virtual fields not being parsed as documented - cakephp

Docs state that we can name calculated fields using a syntax like Timelog__TotalHours and PHP will parse it as:
[0] => Array
(
[ProductsItems] => Array
(
[Total] => 50
)
)
In my CakePHP/2.9.4 set up I'm trying it in several places, such as pagination, but I get the column name handled as any other string:
[0] => Array
(
[ProductsItems__Total] => 50
)
My test code in the controller is:
$this->Paginator = $this->Components->load(
'Paginator',
array(
'fields' => array(
"CONCAT_WS(' ', Usuario.nombre, Usuario.apellidos) AS Usuario__Empleado",
),
'contain' => array(
'Usuario',
),
'limit' => 2,
)
);
debug($this->Paginator->paginate());
... and prints:
array(
(int) 0 => array(
(int) 0 => array(
'Usuario__Empleado' => 'Juan García'
),
'Usuario' => array(
'id' => '56'
)
),
(int) 1 => array(
(int) 0 => array(
'Usuario__Empleado' => 'María López'
),
'Usuario' => array(
'id' => '385'
)
)
)
Do I need explicitly to enable this feature somewhere?

First you should define the virtual field before your query (can be model or controller).
$this->CurrentModel->Usuario->virtualFields['Empleado'] = 0;
$this->Paginator = $this->Components->load(
'Paginator',
array(
'fields' => array(
"CONCAT_WS(' ', Usuario.nombre, Usuario.apellidos) AS Usuario__Empleado",
),
'contain' => array(
'Usuario',
),
'limit' => 2,
)
);
debug($this->Paginator->paginate());
It should work.

Related

How to get rid of null associated model in cakephp 2.x

I am trying to get the item types that Order.Item.ItemType.show_type = 1. I have written the query but I want to show only Items that their ItemType.show_type = 1 not all items.
$brief = $this->Order->find('first', array(
'fields' => array(
'Order.*'
),
'conditions' => array(
'Order.order_id' => $orderId,
),
'contain' => array(
'Item' => array(
'fields' => array(
'Item.*', 'CHAR(64 + Item.num) AS letter'
),
'conditions' => array(
'Item.deleted' => 0,
),
'ItemType' => array(
'conditions' => array(
'ItemType.show_type' => 1
),
)
),
)
));
The query shouldn't show Item id = 25741
Associations:
// Order
public $hasMany = array(
'BriefInstalment' => array(
'foreignKey' => 'order_id'
)
);
// Item Model
public $belongsTo = array(
'Order',
'ItemType' => array(
'type' => 'inner'
)
);
// ItemType Model
public $hasMany = array('Item');
Print:
array(
'Order' => array(
'order_id' => '67817',
'service' => '',
),
'Item' => array(
(int) 0 => array(
'id' => '25741',
'order_id' => '67817',
'num' => '2',
'item_type_id' => '8',
'name' => '3-5 titles active',
'deleted' => false,
'ItemType' => array(), // <= how to remove this empty model
'Item' => array(
(int) 0 => array(
'letter' => 'B'
)
)
),
(int) 1 => array(
'id' => '25742',
'order_id' => '67817',
'num' => '3',
'item_type_id' => '2',
'name' => '1,000 pro active',
'deleted' => false,
'ItemType' => array(
'id' => '2',
'name' => 'Part Instalment',
'show_type' => true,
'deleted' => false
),
'Item' => array(
(int) 0 => array(
'letter' => 'C'
)
)
)
)
)
This could not be done using Countaible behaviour, but iwth the joins method, set the recursive to -1
$brief = $this->Order->find('first', array(
'recursive' => -1,
'fields' => array(
'Order.*'
),
'conditions' => array(
'Order.order_id' => $orderId,
),
'joins' => array(
array(
'table' => 'items',
'alias' => 'Item',
'type' => 'inner',
'conditions' => array(
'Order.id = Item.order_id'
)
),
array(
'table' => 'item_types',
'alias' => 'ItemType',
'type' => 'inner',
'conditions' => array(
'ItemType.id = Item.item_type_id'
)
),
)
));
You have to check if the table names are correct and also the foreign keys names.
Another solution would be to go through your results, and unset the empty ones
foreach($brief as $k => $v){
foreach($v['Item'] as $kk => $vv){
if(empty($vv['ItemType'])){
unset($brief[$k]['Item'][$kk];
}
}
}
debug($brief);

Nested Result while using CakePhp Containable

I am not sure about the question title but tried to explain more below. Thanks in advance for all who helps me.
I sampled the tables to simplify my questions.
DB Tables
News: news_id, title
Comments: comment_id, news_id, content, created (timestamp)
Users are posting comments to the news. In a search with the input of "date interval 01.10.2015 - 05.11.2015", the result should include: the news which has been commented between this interval. And comment_count should be shown next to the title on the screen. This comment_count is not whole comment_count, only the count between this dates.
To do that, I ran the query first in comments. Then count comments. Then group by news_id.
$Data = $this->Comment->find('all', array(
'fields' => array(
'News.news_id','News.title'
),
'conditions' => array(
"Comment.created >=" => date('Y-m-d', strtotime("-1 days")) //yesterday's commented news
),
'contain' => array(
'News' => array(
'Comment => array(
'fields' => array('COUNT(Comment.id) as comment_count'),
)
)
),
'group' => array('News.title')
));
The result is below with too much nesting:
Array
(
[0] => Array
(
[News] => Array
(
[id] => 27
[title] => sample news title
[News] => Array
(
[id] => 27
[title] => sample news title
[Comment] => Array
(
[0] => Array
(
[News_id] => 27
[Comment] => Array
(
[0] => Array
(
[Comment_count] => 1
)
)
)
)
)
)
)
)
In conclusion, to reach comment_count, I have to write the nestings manually. However I would like to reach it in the same level with first [title] node.
$JsonData [] = array(
"id" => $Item["News"]["id"],
"title" => $Item["News"]["title"],
"comment_count" => $Item["News"]["News"]["Comment"][0]["Comment"][0]["comment_count"]
);
How can I reduce the life-long nesting?
Thanks in advance.
First of all, you should name your table id like id not news_id. For your question, I'm not sure from what controller you are displaying news, IMO this should be done in NewsController. Here is how you can retrieve your news with comments count from previous day:
$Data = $this->News->find('all', array(
'fields' => array(
'News.id',
'News.title',
'COUNT(Comment.id) as comment_count'
),
'group' => array('News.id'),
'joins' => array(
array(
'table' => 'comments',
'alias' => 'Comment',
'conditions' => array(
'News.id = Comment.news_id',
'Comment.created >=' => date('Y-m-d', strtotime("-1 days"))
)
)
)
));
This should give you resulting array like this:
array(
(int) 0 => array(
'News' => array(
'id' => '1',
'title' => 'New news'
),
(int) 0 => array(
'comment_count' => '2'
)
),
(int) 1 => array(
'News' => array(
'id' => '2',
'title' => 'Seconds news'
),
(int) 0 => array(
'comment_count' => '1'
)
),
)
Then you can get your comment count(assuming through foreach loop):
... "comment_count" => $Data[0]['comment_count'] ...
You should look counterCache it might be useful to you.

How to assign mysql functions on join fields in Cakephp

I want to get last_deadline and the count of instalments of all instalments but obviously this query will show me 1 order and the last_deadline.
$orders = $this->find('all', array(
'fields' => array(
'Order.order_id', 'Order.summary', 'Order.fee',
'BriefInstalment.id',
'MAX(`BriefInstalment`.`deadline`) AS last_deadline'
),
'conditions' => $conditions,
'joins' => array(
array(
'table' => $this->getTableName('default', 'brief_instalments'),
'alias' => 'BriefInstalment',
'type' => 'RIGHT',
'conditions' => array(
'Order.order_id = BriefInstalment.order_id',
'BriefInstalment.deleted' => 0,
),
'order' => 'BriefInstalment.deadline ASC',
)
),
'order' => 'BriefInstalment.deadline ASC'
));
I have tried 'contain' and doesn't work.
'contain' => array(
'BriefInstalment' => array(
'fields' => 'BriefInstalment.id',
'fields' => array(
'BriefInstalment.id',
'MAX(`BriefInstalment`.`deadline`) AS last_deadline', 'COUNT(`BriefInstalment`.`id`) AS total_instalments'
),
'conditions' => array(
'BriefInstalment.deleted' => 0
)
)
),
By the way I don't want to use loop to get last_intalments and cout brief_instalments. e.g.
// Determine deadlines
foreach ($orders as $i => $order) {
$deadline = $this->BriefInstalment->getLastDeadline($order['Order']['order_id']);
$orders[$i] += array(
...
'last-deadline' => $deadline,
'total-instalments' => count($order['BriefInstalment'])
);
}
The reason is it decrease the speed of loading.
Any help plz
Here is how to create custom query
$conditionsSubQuery['"User2"."status"'] = 'B';
$db = $this->User->getDataSource();
$subQuery = $db->buildStatement(
array(
'fields' => array('"User2"."id"'),
'table' => $db->fullTableName($this->User),
'alias' => 'User2',
'limit' => null,
'offset' => null,
'joins' => array(),
'conditions' => $conditionsSubQuery,
'order' => null,
'group' => null
),
$this->User
);
$subQuery = ' "User"."id" NOT IN (' . $subQuery . ') ';
$subQueryExpression = $db->expression($subQuery);
$conditions[] = $subQueryExpression;
$this->User->find('all', compact('conditions'));
Reference:
http://book.cakephp.org/2.0/en/models/retrieving-your-data.html#sub-queries

Saving many datas HasMany trough

I want to save 2+ datas in a model hasMany trough. But this is not saving.
$data[] = array('User' => array('id' => 5), 'Solicitation' => array('id' => $this->Solicitation->id));
$data[] = array('User' => array('id' => 6), 'Solicitation' => array('id' => $this->Solicitation->id));
debug($data);
$this->SolicitationUser->saveAll($data);
Result of debug($data)
array(
(int) 0 => array(
'User' => array(
'id' => (int) 5
),
'Solicitation' => array(
'id' => '70'
)
),
(int) 1 => array(
'User' => array(
'id' => (int) 6
),
'Solicitation' => array(
'id' => '70'
)
)
)
It's hard to tell what you're going after, since there's little description, but I assume you'd want it more like this to save two rows in your HasMany Through table:
array(
(int) 0 => array(
'user_id' => (int) 5
'solicitation_id' => '70'
),
(int) 1 => array(
'user_id' => (int) 6
'solicitation_id' => '70'
)
)

CakePHP 2.1 Custom Pagination

I am using cakephp 2.1 and defined 3 tables as follows.
`industries(id, name);`
`movies(id, name, industry_id),`
`trailers(id, name, movie_id);`
I want to paginate the trailers for particular industry. So the code I have written is below:
$this->paginate = array(
'Industry' => array(
'contain' => array(
'Movie' => array(
'order' => 'Movie.release DESC',
'Trailer' => array(
'conditions' => array(
'Trailer.id !=' => $trailer_id
)
)
)
),
'conditions' => array(
'Industry.id' => $id
)
)
);
If I would specify 'limit' for 'Trailer', I get correct number of results but some null array for a movie which doesn't contain any trailers. Please help me to get the number of specified limit of trailers for particular industry. The work will be most appreciated.
It looks to me like you've got your contain key in the wrong place. It should be defined before the models.
<?php
$this->paginate = array(
'contain' => array(
'Industry' => array(
'Movie' => array(
'order' => 'Movie.release DESC',
'Trailer' => array(
'conditions' => array(
'Trailer.id !=' => $trailer_id
)
)
)
)
),
'conditions' => array(
'Industry.id' => $id
)
);

Resources