cakephp threaded comments - cakephp

I a bit new to cakephp and I'm wondering why I have this kind of problem.
Basically, I am doing a threaded comments in cakephp. But my problem is that every time I try to "comment" on a "comment", it is displaying differently.
Here is a screenshot:
I want it to be reversed like all the child comments should be posted on the last row. Currently, when I add a new comment, it is displayed on the top rather than on the bottom. I want it to become like how facebook does their commenting.
Here is my code for this:
$comments = $this->UserDiscussionComment->find('threaded', array('conditions' => array('UserDiscussionComment.user_discussion_id' => $slug), 'order' => array('UserDiscussionComment.id DESC', 'UserDiscussionComment.created ASC')));
And here is a sample records in the database:
I want to change the order of the child comments. I tried "ASC" and "DESC" but its not working.
Thanks in advance.

You won't be able to order the children in a different order according to the documentation for find('threaded'). What I would do is after your find call, simply reverse the array:
$comments = $this->UserDiscussionComment->find('threaded', array(
'conditions' => array('UserDiscussionComment.user_discussion_id' => $slug),
'order' => array('UserDiscussionComment.id DESC')
));
for ($i = 0; $i < sizeof($comments); $i++) {
$comments[$i]['children'] = array_reverse($comments[$i]['children']);
}
Untested but it should do the trick (also I assume you can only comment 1 level deep like your screenshot shows).
Edit
I wanted to share a different approach I used in the past for the same thing you're trying to accomplish. Basically I set my Comment model up like so:
class Comment extends AppModel {
public $belongsTo = array(
'ParentComment' => array(
'className' => 'Comment',
'foreignKey' => 'parent_id'
),
'User'
);
public $hasMany = array(
'ChildComment' => array(
'className' => 'Comment',
'foreignKey' => 'parent_id'
)
);
}
Then, when I want to do a find, I can order the parent & child comments differently (note that I'm using the Containable behavior):
$comments = $this->Comment->find('all', array(
'order' => 'Comment.id DESC',
'contain' => array(
'ChildComment' => array(
'order' => 'ChildComment.id ASC'
)
)
));

array('UserDiscussionComment.id DESC', 'UserDiscussionComment.created ASC')
I think the error is there.
As UserDiscussionComment.id are all distinct, there is nothing to order for UserDiscussionComment.created ASC.
Try:
array('UserDiscussionComment.parent_id DESC', 'UserDiscussionComment.created ASC')

Related

Associate different column name to one Model + cakephp

how to get name of (UserTransactionType.name) with Transaction.who_pay_fee_1,2,3 fields.
'user_transaction_type_id' works well but how to get the rest of fields work :(
//Transaction Model
public $belongsTo = array(
'UserTransactionType' => array(
'className' => 'UserTransactionType',
'foreignKey' => 'user_transaction_type_id',
'conditions' => '',
'fields' => '',
'order' => ''
),
//UserTransactionType Model
public $hasMany = array(
'Transaction' => array(
'className' => 'Transaction',
'foreignKey' => 'user_transaction_type_id',
'dependent' => false,
))
This is the sample code for your controller:
$this->UserTransactionType->find('all',array(
'fields' => array('name'),
'contain' => array('Transaction')
)
);
If Models are associated you can specify in 'contain' which of them you want to get in the result.
If you want to have only some fields of related Model, you can determine them after 'Transaction' in 'contain' just like in the regular find() query:
'contain' => array('Transaction' => array('fields' => array('field_1',
'field_2') ))
But in your case, you don't need to specify fields, because by default you get all fields.
So no matter if you define or not fields "who_pay_fee_1,2,3" because if you use 'contain' by default you will get foreing_key - user_transaction_type_id.
I hope it's helpful
For people they like CakePhp :)
in Controller ->
get the list of 'UserTransactionType'
in View ->
after looping trough all the transactions; in Transaction Status column simply load the 'UserTransactionType'array and assign the number of array to $userTransactionTypes.
$userTransactionTypes[$transaction['Who_pay_fee_1']];
To be honest it was straight forward but needed a bit concentration :)

Containable behavior doesn't return deeper model association and selecting fields

I want to limit the fields returned by a deeper association using containable.
My associations:
Game hasMany Review
The paginate and containable code:
$this->paginate = array(
'conditions' => $conditions,
'fields' => array(
'Game.id', 'Game.name',
'Publisher.id', 'Publisher.name'
),
'contain' => array(
'Game' => array(
'Review' => array(
'fields' => array('Review.id', 'ROUND(AVG(Review.score),1)')
)
),
)
);
$games = $this->paginate('Game');
Currently, all of the fields in the Review table are returned. 'ROUND(AVG(Review.score),1)' is never returned. How can I specify what fields I want returned from the Review association?
SQL dumps for two search results using #theJetzah's answer. The first is a search with one game as a result and the second is a search returning three games.
SELECT `Review`.`id`, `Review`.`review_text`, `Review`.`score`, `Review`.`user_id`, `Review`.`game_id`, `Review`.`created`, `Review`.`platform_id`, (ROUND(AVG(`Review`.`score`),1)) AS `Review__average_score` FROM `videogamedb`.`reviews` AS `Review` WHERE `Review`.`game_id` = (55)
SELECT `Review`.`id`, `Review`.`review_text`, `Review`.`score`, `Review`.`user_id`, `Review`.`game_id`, `Review`.`created`, `Review`.`platform_id`, (ROUND(AVG(`Review`.`score`),1)) AS `Review__average_score` FROM `videogamedb`.`reviews` AS `Review` WHERE `Review`.`game_id` IN (55, 56, 57)
Not a full answer, but an attempt to get it working :)
Approach1 (UPDATE: Containable doesn't support 'group by')
First of all, try to add the 'Game' model to the $uses array of your Controller, if it is not included yet, and re-organise the pagination array (as previously suggested by Sam), so that you'll be pagination the Game model itself.
Then, It may help to create a virtual field for the calculated score, but the results of 'Review' need to be grouped, otherwise you'll not be able to calculate the average score.
I'm not able to test this, but it may worth trying
something like this;
public $uses = array(
'Game',
// other models
);
public function myfunction()
{
$this->Game->Review->virtualFields['average_score'] = 'ROUND(AVG(Review.score),1)';
$this->paginate = array(
'Game' => array(
'fields' => array(
'Game.id',
'Game.name',
'Publisher.id',
'Publisher.name'
),
'contain' => array(
'Review' => array(
'fields' => array(
'Review.game_id,
'Review.average_score',
),
'group' => array(
'Review.game_id,
),
)
)
)
);
// Conditions can be passed to paginate,
// that way you can specify 'paginate' at
// one place and don't have to modify it
// to include the conditions
$games = $this->paginate('Game', $conditions);
}
Alternative approach: Using joins and a database-view
Apparently, the Containable behavior doesn't like group-by clauses; See this ticket for more information: Containable behavior does not implement 'group' option
CakePHP allows you to manually specify a join: Joining Tables
To simplify things and to prevent having to add a 'group by' for all fields, create a simple database-view in your database;
CREATE VIEW review_scores AS
SELECT
game_id,
ROUND(AVG(score),1) AS average_score,
COUNT(id) AS total_reviews
FROM
reviews
GROUP BY
game_id;
If you're unfamiliar with this; a database 'view' is basically a 'stored query', which can be accessed as if it was a regular table. See Create View
Then, use a 'manual' join, using the newly created database-view as the source-table. In your case, this will look something like this;
$this->paginate = array(
'Game' => array(
'fields' => array(
'Game.id',
'Game.name',
'Publisher.id',
'Publisher.name',
'ReviewScore.average_score',
'ReviewScore.total_reviews',
),
'joins' => array(
array(
'table' => 'review_scores',
'alias' => 'ReviewScore',
'type' => 'LEFT',
'conditions' => array(
'ReviewScores.game_id = Game.id',
)
)
)
)
);
Hope this helps
I think your array is a configured a little wrong, try:
$this->paginate = array(
'Game' => array(
'conditions' => $conditions,
'fields' => array(
'Game.id', 'Game.name',
'Publisher.id', 'Publisher.name'
),
'contain' => array(
'Review' => array(
'fields' => array('Review.id', 'ROUND(AVG(Review.score),1)')
)
)
)
);
$games = $this->paginate('Game');
As an aside, from personal experience, specifying the fields in a query doesn't always speed it up (certainly for small number of fields), assuming this is the motive for doing so. It does reduce memory occupancy but this is only relative to original size of the record and the number of records returned.

CakePHP - find Model using association other than id

I'm having no luck trying to bend CakePHP to do what I need.
I have 2 Models, Listing & ListingService.
ListingService's fields are as follow: id, title, listing_id, service_type_id
(So in a way it is like a has and belongs to many relationship with Listing)
When I do a find right now. (Using belongsTo in Listing.php)
public $belongsTo = array(
'ListingService' => array(
'className' => 'ListingService',
'foreignKey' => 'id'
)
);
$this->paginate = array('type' => 'all', 'recursive' => 0,
'fields' => array('Listing.id', 'Listing.title',
'ListingService.service_type_id','ListingService.title'),
'limit' => 10
);
This is the query that is showing
SELECT `Listing`.`id`, `Listing`.`title`, `ListingService`.`service_type_id`, `ListingService`.`title` FROM `listings` AS `Listing` LEFT JOIN `listing_services` AS `ListingService` ON (`Listing`.`id` = `ListingService`.`id`) LIMIT 10
This is almost what I need, except that I would like the LEFT JOIN to be ON (Listing.id = ListingService.listing_id) instead of ON (Listing.id = ListingService.id)
If I use "hasMany" it doesn't even do the LEFT JOIN.
I don't have a ListingService.php model currently.
Please enlight.
Thank you,
Tee
Found a solution.
I need to first do this.
$this->Listing->ListingService->primaryKey = 'listing_id';
Is this a hack? If yes, is there a better way to do it?
I think your belongsTo should be like this
public $belongsTo = array(
'ListingService' => array(
'className' => 'ListingService',
'foreignKey' => 'listing_id'
)
);

Cakephp Paginate Distinct record

So this is what I am trying to do.
My table say(Courses) has multiple entries with same id.
When I get the data from paginate it shows all the records. So if I have 3 records with Id 5 it will show record number 5 three times.
Now What I want is that it should show the record only once.
I searched online but can't find anything.
If anyone has come across such problem and found a solution to it please let me know.
Thanks,
I came across your problem, as I had a similar problem. David Z's solution did not work for me, but I did find that the group variable in $paginate worked for me.
So using your code sample above, this is how I'd think it should work.
$paginate = array(
'Courses' => array(
'limit' => 20,
'fields' => array('Courses.id'),
'conditions' => $cond,
'group' => array('Courses.id'),
'order' => array('Courses.id' => 'asc')
)
);
To hopefully shed some more light on the solution that worked for me, I have Systems that belong to Companies. I wanted to get a list of the unique companies, for the systems I have. This is the exact code I used, that worked for me
$this->paginate = array ('fields' => array ('Company.*'),
'order' => array('Company.name' => 'ASC'),
'group' => array('Company.id'));
$this->set('companies', $this->paginate($this->Company->System));
Hope this has helped
Looking at the CakePHP cookbook, the documentation for pagination shows that you can override the $paginate member. Behind the scenes, this similar to passing in the parameters for your model's find('all'). Maybe try setting parameter to explicitly return the filds that you are interested with the distinct keyword to narrow down the values you need?
class RecipesController extends AppController {
var $paginate = array(
'fields' => array('Model.field1', 'DISTINCT Model.field2')
);
}
So here is how my paginate variable looks like:
var $paginate = array(
'Courses' => array(
'limit' => 20,
'page' => 1,
'order' => array(
'Courses.id' => 'asc')
),
);
The condition variable looks something like this:
$cond = array("Courses.id LIKE "=>$this->data['id_search'],
"Courses.length LIKE "=>$this->data['length_search'],
"Courses.marks LIKE "=>$this->data['marks']
);
And this is how I am calling paginate.
$data = $this->paginate('CdmaRfReport',$cond);
I tried doing
$paginate = array(
'Courses' => array(
'limit' => 20,
'fields' => array('DISTINCT Courses.id'),
'page' => 1,
'conditions' => $cond,
'group' => array('id'),
'order' => array(
'Courses.id' => 'asc')
)
);
It doesn't seem to help.
I also tried
$cond = array("DISTINCT Courses.id "=>$this->data['id_search'],
"Courses.length LIKE "=>$this->data['length_search'],
"Courses.marks LIKE "=>$this->data['marks']
);
Even this errors out
I might be something wrong. But I am not able to figure it out.
Any suggestions please let me know.

Only retrieve records with at least one association

This is pretty straightforward, but I guess I've never bumped into it before. I have a Page model that hasMany Comment. I'd like to pull all of the pages that have at least 1 comment, but eliminate pages with none. As I look at it, I realize that I'm not sure how to do that. I guess I could use ad hoc joins, but I'd rather use Containable, if that's possible. I've tried testing not null in the Comment conditions and one or 2 other things that were unlikely to work, but it seems like this should be possible.
What I get now, of course, is all pages and some of those page records have an empty Comment member. Be nice to skip passing around all of the extra cruft if I can do so.
My find call:
$pages = $this->Folder->Page->find(
'all',
array(
'contain' => array(
'Comment' => array(
'order' => array( 'Comment.modified DESC' ),
),
'Folder' => array(
'fields' => array( 'Folder.id' ),
),
),
'conditions' => array(
'Folder.group_id' => $id,
),
)
);
Thanks.
You have several approaches available other than ad-hoc joins:
Denormalize your dataset and add a has_comment flag to your pages table. Add 'Page.has_comment' => 1 to your conditions.
Run your query against Comment, with DISTINCT page_id.
$comments = $this->Folder->Page->Comment->find('all', array(
'conditions' => array('DISTINCT Comment.page_id'
'contain' => array(
'Page' => array(
'Folder'
)
)
);
First grab a distinct set of page ids from the comments table.
$page_ids = $this->Folder->Page->Comment->find('list', array(
'fields' => array('id', 'page_id'),
'conditions' => array('DISTINCT Comment.page_id'),
);
$pages = $this->Folder->Page->find('all', array(
'conditions' => array('Page.id' => $page_ids),
'contain' => array(
...
)
);

Resources