CakePHP: List comments associated with a post - php

In my application I have two tables (I have more but I'm only discussing two here):
**Posts**
id
title
datetime
content
**Comments**
id
content
post_id
user_id
As you can see their is a relationship using the post_id as a foreign key.
When viewing the Post I have the following code to display comments:
<?php if ( ! empty($post['Comment']) ): ?>
<ul>
<?php foreach ($post['Comment'] as $comment): ?>
<li>
<?php echo $comment['title']; ?>
</li>
<?php endforeach; ?>
</ul>
<?php else: ?>
<p>No comments...</p>
<?php endif; ?>
Here is the controller method for viewing a post:
function view ( $id = null, $slug = null )
{
$post = $this->Post->find('first',array('contain'=>array('User'=>array('Comment','Profile')),'conditions'=>array('Post.id'=>Tiny::reverseTiny($id))));
if (!$post)
{
throw new NotFoundException('404');
}
else if($post['Post']['status'] == '0') // 0=draft 1=open 2=open
{
if($post['Post']['user_id'] == $this->Auth->user('id'))
{
$this->Session->setFlash('Your post has NOT been published yet');
}
else
{
throw new NotFoundException('404');
}
}
if (Inflector::slug($post['Post']['title']) != $slug || $slug = null)
{
$this->redirect(array('id'=>Tiny::toTiny($post['Post']['id']),'slug'=>Inflector::slug($post['Post']['title'])));
}
$this->set(compact('post'));
}
and this is the comment model:
class Comment extends AppModel
{
public $name = 'Comment';
public $belongsTo = array('User','Post');
public $actsAs = array('Containable');
}
and a typical url for a post would be: /posts/Sample_post-1ns
However I ALWAYS get the message that there are no comments... But I have looked in the database and their is... and I have double checked that all the associations are correct. So I can only presume that the code above is wrong!
Also is their a better way of calling the comments? As I want to add the ability to filter and paginate them too.
Cheers

Seems you need something like this:
Comment model:
public $belongsTo = array('User', 'Post');
Post model:
public $belongsTo = array('User');
public $hasMany = array('Comment');
User model:
public $hasMany = array('Post', 'Comment');
And contain statement of Post:
'contain' => array('Comment', 'User' => array('Comment', 'Profile'))

From your schema it looks like the comment model is related to the post model, not to the user, so:
$post = $this->Post->find('first',array('contain'=>array('User'=>array('Comment','Profile')),'conditions'=>array('Post.id'=>Tiny::reverseTiny($id))));
Should be:
$post = $this->Post->find('first',array('contain'=>array('Comment','User'=>array('Profile')),'conditions'=>array('Post.id'=>Tiny::reverseTiny($id))));
I.e. the Comment should be outside of the User contain array.
Providing that the correct hasMany and belongsTo relationships are set up on the Post/User and Comment model respectively, the first example would produce an array like this:
array(
'Post'=>array(...),
'User'=>array(
'id'=>1,
//Other user data
'Comment'=>array(
array(...), //Comment 1
array(...), //Comment 2
)
)
);
And the second example would produce:
array(
'Post'=>array(...),
'User'=>array('id'=>1 //Other user data)
'Comment'=>array(
array(...), //Comment 1
array(...), //Comment 2
)
);
Hopefully, that shows that the difference is in the position of the "Comment" key in the array - it is either under the User key, or at the top level depending on how you do the contain.

Related

CakePhp multiple table data fetch on single page

I am new in cakePHP try to create a blog site where user add blog after category select, I have one categories table : fields: category_id, name and posts table : fields : id, category_id , title, body.
I want to fetch all categories to a dropdown list. When user add new post they have to select category first then he is able to post anything..
My PostsController:
<?php
class PostsController extends AppController{
public $helpers = array('Html', 'Form', 'Session');
public $components = array('Session','Paginator');
public function index(){
$this->Paginator->setting = array(
'limit' =>'10',
'order' => array('Post.id' =>'Desc')
/*'conditions' => array(
'Post.user_id' => AuthComponent::user(id)
)*/
);
$arrPosts = $this->Paginator->paginate('Post');
$this->set('posts', $arrPosts);
}
public function view($id = null){
if(!$id){
throw new NotFoundException(__("Error Processing Request ID"));
}
$post =$this->Post->findById($id);
if(!$post){
throw new NotFoundException(__("Error Processing Request POST"));
}
$this->set('post',$post);
}
public function add(){
// HERE I want to fetch all categoryies from categories table and want to send to view
if($this->request->is('post')){
$this->Post->create();
if($this->Post->save($this->request->data)){
$this->Session->setFlash(__('Blog Posted Sucessfully'));
return $this->redirect(array('action' => 'index'));
}else{
$this->Session->setFlash(__('Unable to Post Blog '));
}
}
}
}
?>
I want to show my category in add form:
Please help me ...
In your controller action you need to use $this->set() to set a View variable. As long as you've got your associations setup correctly in your Post model you should then be able to use:-
$this->set('categories', $this->Post->Category->find('list'));
Cake's FormHelper should automatically know that the Post.category_id form field wants to be a select input with $categories as the options.
One further point, it would be better to set the view variables after processing the form as you won't need them if it saves correctly and so can reduce the number of database queries by 1.
If you use find('all') to retrieve the categories you will need to convert it into the format used by find('list') which can easily be done using Hash::combine():-
$categories = $this->Post->Category->find('all');
$this->set(
'categories',
Hash::combine($categories, '{n}.Category.id', '{n}.Category.name')
);
The only real value in doing this is if you need $categories for something else.
public function add(){
/*Here get the category list & set the view (ctp file)*/
$this->set('categories', $this->Post->Category->find('list'));
if($this->request->is('post')){
$this->Post->create();
if($this->Post->save($this->request->data)){
$this->Session->setFlash(__('Blog Posted Sucessfully'));
return $this->redirect(array('action' => 'index'));
}else{
$this->Session->setFlash(__('Unable to Post Blog '));
}
}
}
//Set The dropdown in ctp file
$this->Form->input('category_id', array('type'=>'select', 'options'=>'categories'));

relation using BELONGS_TO with user table

I'm trying to join my comment table with my user table like this comment.userId=user.id
unfortunately when i print_r($this->user); i get nothing. what am i doing wrong here?
in my comment model
public function relations()
{
return array(
'user' => array(self::BELONGS_TO, $this->module->user, 'userId'),
);
}
public function getLastName()
{
print_r($this->user);
die;
return is_null($this->user) ? '' : $this->user->{$this->module->lastNameAttribute};
}
where
$this->module->user = 'User'; //User is the model name
and
$this->module->lastNameAttribute = 'last_name';
in my view
$comments = $model->getCommentDataProvider();
$comments->setPagination(false);
$this->widget('zii.widgets.CListView', array(
'dataProvider'=>$comments,
'itemView'=>'application.modules.comment.views.comment._view', //view file location
'emptyText' => '<div class="alert alert-info">No comments yet.</div>',
'summaryText' => '<h4>'.Yii::t('commentTitle','{n} comment|{n} comments',$comments->totalItemCount).'</h4>'
));
I see a small typo, maybe you mistaken while making a post:
comment.userid=user.id
here it's userid and in relation you referenced it with userId
check it out please
EDIT - after question edit
I'm not familiar with CommentableBehavior but it seems to me that you need to eager load User model with each Comment:
$comments = Yii::createComponent($this->module->commentModelClass)->with('user')->findAll($this->getCommentCriteria());
I added with('user') in getComments() method

How to use the user ID as a foreign key?

I have two tables, 'users' and 'posts', looking like this:
users:
- id
- username
- password
...
posts:
- id
- user_id (foreign key referencing users.id)
- text
Basically, a user has multiple posts (blog-type posts). Now, I'm trying to create a new post as a logged in user, but I can't get it to work. Here's what I've done:
// 'User' model
class User extends AppModel
{
public $name = 'User';
public $hasMany = array('Post');
...
// 'Post' model
class Post extends AppModel
{
public $name = 'Post';
public $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id'
)
);
// In PostsController
public function create()
{
if($this->request->is('post'))
{
$this->Post->create();
if($this->Post->save($this->request->data)
{
// Success
}
}
}
// In the post view
<?php echo $this->Session->flash('auth'); ?>
<?php echo $this->Form->create('Post', array('action' => 'create')); ?>
<fieldset>
<legend>
<?php echo __("Write a post"); ?>
</legend>
</fieldset>
<?php echo $this->Form->end(__('Post')); ?>
If I write a post and click 'Post', I get an integrity constraint violation:
Error: SQLSTATE[23000]: Integrity constraint violation:
1452 Cannot add or update a child row: a foreign key
constraint fails (`yams`.`posts`, CONSTRAINT `user_id`
FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
ON DELETE NO ACTION ON UPDATE NO ACTION)
Am I missing something here? It looks like the user id is not saved to the model.
EDIT:
I forgot to mention, the database error also prints out the SQL query which is clearly wrong:
INSERT INTO `yams`.`posts` (`text`) VALUES ('this is a test post.')
There's no ID whatsoever...
You need to do this:
// In PostsController
public function create()
{
if($this->request->is('post'))
{
$this->request->data['Post']['user_id'] = $this->Auth->user('id');
$this->Post->create();
if($this->Post->save($this->request->data)
{
// Success
}
}
}
I am just copying the book here, i have not used cakePHP at all!
According to the book: http://book.cakephp.org/2.0/en/models/associations-linking-models-together.html then a 'hasMany' relationship should look similar to:
class User extends AppModel {
public $hasMany = array(
'Recipe' => array(
'className' => 'Recipe',
'conditions' => array('Recipe.approved' => '1'),
'order' => 'Recipe.created DESC'
)
);
}
You have:
public $hasMany = array('Post');
Should there be mention of a classname in yours?
i.e.
public $hasMany = array(
'Post' => array(
'className' => 'Post'
)
);
With this then the ORM can work out how the classes relate and what to fill in at run time.

Show the user's username in post view w/ related comment

I am a newbie in CakePHP 1.3... I want to display or show the USER's username instead of USER's id, who post the COMMENTS in the POST view... Anyone can help me please?
Here is the model association I made:
POSTS 'has many' COMMENTS
COMMENTS 'belongs to' USERS
POST->COMMENT->USER
I already read the Containable Behavior of CakePHP 1.3, but still I can't understand it well... Please help me what codes to put in the post_controller's view & view.ctp that can show the related's related table in the POST view.
And How to call the USER's data in the POST view.
I'm still confused.
Thanks in Advance, Azure
Assumptions
You have three tables as below
(1) Posts
* id
* title
(2) Comments
* id
* post_id
* user_id
(3) Users
* id
* name
View Function in PostsController.php file
public function view($id) {
if (!$id) {
throw new NotFoundException(__('Invalid post'));
}
$this->Post->recursive=2;
$post = $this->Post->findById($id);
if (!$post) {
throw new NotFoundException(__('Invalid post'));
}
$this->set('post', $post);
}
Content of view.ctp file in app/View/Posts/ folder
<!-- File: /app/View/Posts/view.ctp -->
<h1><?php echo 'Post ID : '.h($post['Post']['id']); ?></h1>
<h1><?php echo 'Post Title : '.h($post['Post']['title']); ?></h1>
<?php
echo 'Comments By Users : ';
if(!empty($post['Comment'])){
foreach ($post['Comment'] as $key=>$value){?>
<p>User Name : <?php echo $value['User']['name'];?></p>
<?php }
}
else {
echo '<br/>';
echo 'No Comments Yet';
} ?>
Model File : User.php
<?php
class User extends AppModel {
public $hasMany = array(
'Comment' => array(
'className' => 'Comment',
)
);
}
?>
Model File : Comment.php
<?php
class Comment extends AppModel {
public $belongsTo = array(
'User' => array(
'className' => 'User',
'foreignKey' => 'user_id'
)
);
}
?>
Model File : Post.php
<?php
class Post extends AppModel {
public $hasMany = array(
'Comment' => array(
'className' => 'Comment',
)
);
}
?>
I assume you are saving user id in comments table and user names are in users table who posts the comment, then use the below solution
In Controller method:
$userdata=$this->User->find('all',array('fields'=>array('id','username'),'recursive'=>-1));
$userid=Set::extract('/User/id', $userdata);
$username=Set::extract('/User/username', $userdata);
$data=array_combine($username,$userid);
$this->set('name',$data);
In View:
$cid=$var['Comment']['user_id'];
$username=array_search($cid, $name);
echo $username;

Saving data on GET request in CakePHP

I am building a simple mechanism where a user can like a post by clicking on a link. I'm using GET rather than POST as I want to allow the method to fire via the URL.
That been said how do I save data using GET? As the request data doesn't exist in this scenario... My model looks like:
class Like extends AppModel
{
public $name = 'Like';
public $belongsTo = array('User','Post');
}
and the method for adding looks like:
public function add( $id )
{
$post = $this->Post->find('first', array(
'conditions' => array('Post.id'=>Tiny::reverseTiny($id))
));
if (!$post)
{
throw new NotFoundException('404');
}
if($post['Post']['user_id'] == $this->Auth->user('id'))
{
$this->Session->setFlash('You can\'t like your own post... That\'s just silly!');
}
if ($this->Like->create())
{
$liked = $this->Like->find('first', array(
'conditions' => array('Like.id'=>Tiny::reverseTiny($id), 'Like.user_id'=>$this->Auth->user('id') )
));
if(!$liked){
$this->Like->saveField('user_id', $this->Auth->user('id'));
$this->Like->saveField('post_id', $post['Post']['id']);
$this->redirect(array('controller'=>'posts','action'=>'view','id'=>Tiny::toTiny($post['Post']['id']),'slug'=>$post['Post']['slug']));
} else {
$this->Session->setFlash('You already like this post!');
}
else
{
$this->Session->setFlash('Server broke!');
}
}
Can anyone help?
<?php echo $this->Html->link('1', array('controller'=>'followers','action'=>'add','id'=>Tiny::toTiny($post['Post']['id'])),
array('title'=>'Follow','class'=>'follow')); ?>
This part all works fine. It's saving a new row in the DB on GET that I'm struggling with.
Hi you just need to make a link to your controller action and pass you variable in the url.
to be clear the link on the post to like is in your post view :
$this->Html->link('like this post', array('controller' => 'like', 'action' => 'add', $postId))
It should render a link like this :
www.yourWebSite/likes/add/1 to like the postId 1,
variables after your action (add) are interpreted as variable for your controller action
if your fonction add had been
public function add($postId, $wathever){
}
the url should look like www.yourWebSite/likes/add/1/blabla
where 1 is the first var for the add action and blabla the second one and so on.
this is the equivalent of a non rewriting url : ?postId=1&whatever=blabla
EDIT :
if(!$liked){
//simulate the post behaviour
$this->request->data['Like']['user_id'] = $this->Auth->user('id');
$this->request->data['Like']['post_id'] = $post['Post']['id'];
//save the data
if ($this->Like->save($this->request->data)) {
$this->Session->setFlash(__('Thanks for your support !'));
$this->redirect(array('controller'=>'posts','action'=>'view','id'=>Tiny::toTiny($post['Post']['id']),'slug'=>$post['Post']['slug']));
} else {
$this->Session->setFlash('Server broke!');
}
}
How about using save with id=0 instead of create?
<?php
$like = array(
"Like" => array
(
"id" => 0,
"user_id" => $this->Auth->user("id"),
"post_id" => $post['Post']['id']
)
);
$result = $this->Like->save($like);
if(!$result){$this->Session->setFlash('Server broke!');}
$this->redirect(array('controller'=>'posts','action'=>'view','id'=>Tiny::toTiny($post['Post']['id']),'slug'=>$post['Post']['slug']));
?>

Categories