The Problem
I am trying to create a website, where users can post posts and then their friends can see these posts. Similar to facebook, twitter etc.
I got to the point where users can be friends and they can post things. However, I am stuck restricting the shown posts to just the user's friends' posts. At the moment every user can see every post.
I am using PDO in a MVC architecture.
The database structure
TABLE `friends` (
`friendship_id` int(11) NOT NULL AUTO_INCREMENT,
`user_one` int(11) NOT NULL,
`user_two` int(11) NOT NULL,
PRIMARY KEY (`friendship_id`),
FOREIGN KEY (user_one) REFERENCES users(user_id),
FOREIGN KEY (user_two) REFERENCES users(user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
In the table above, depending on who sent the friend request to who, 'user_one' can be either myself or my friend or 'user_two' can be myself or my friend.
TABLE `posts` (
`post_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`post_text` text NOT NULL,
`user_id` int(11) unsigned NOT NULL,
PRIMARY KEY (`post_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
The Model
/**
* Get all posts from the suer's friends
* #return array an array with several objects (the posts)
*/
public static function getAllPosts()
{
$my_id = Session::get('user_id');
$database = DatabaseFactory::getFactory()->getConnection();
// get all of the user's friends
$friendsQuery = $database->prepare("
SELECT user_one FROM friends WHERE user_two = '$my_id';
SELECT user_two FROM friends WHERE user_one = '$my_id';
");
$friendsQuery->execute();
$friends = $friendsQuery->fetchAll();
$friendsQuery->closeCursor();
$query = $database->prepare("SELECT * FROM posts WHERE user_id = '$my_id' OR user_id = '$friends'");
$query->execute(array());
return $query->fetchAll();
}
In the above code my problem lies in the $friends variable. If I replace it manually with the user_id of my friend it works as intended.
E.g.:
"SELECT * FROM posts WHERE user_id = '$my_id' OR user_id = '1'";
The Controller
/**
* Gets all posts (of the user and his friends).
*/
public function index()
{
$this->View->render('post/index', array(
'posts' => PostModel::getAllPosts()
));
}
The View
<?php if ($this->posts) { ?>
<?php foreach($this->posts as $key => $value) { ?>
<p><?= htmlentities($value->post_text); ?></p>
<?php } ?>
<?php } ?>
Summary
I am not able to create a sql query, which just selects posts, which were posted by myself or my friends and does not select any other posts.
I would be very thankful, if somebody could help me out with this!
UPDATE
My model now looks like this:
/**
* Get all posts from the suer's friends
* #return array an array with several objects (the posts)
*/
public static function getAllPosts()
{
$my_id = Session::get('user_id');
$database = DatabaseFactory::getFactory()->getConnection();
// get all of the user's friends
$friendsQuery = $database->prepare("
SELECT user_one FROM friends WHERE user_two = '$my_id';
SELECT user_two FROM friends WHERE user_one = '$my_id';
");
$friendsQuery->execute();
$friends = $friendsQuery->fetchAll();
print_r($friends);
$friendsQuery->closeCursor();
foreach($friends as $friend)
{
$friend_ids[$friend->key] = $friend->val;
}
print_r($friend_ids);
$friendsSQL = implode(',',$friend_ids);
$query = $database->prepare("SELECT * FROM posts WHERE user_id = '$my_id' OR user_id IN('$friendsSQL')");
$query->execute();
return $query->fetchAll();
}
This is my output:
Array ( [0] => stdClass Object ( [user_one] => 1 ) )
Notice: Undefined property: stdClass::$key
Notice: Undefined property: stdClass::$val
Array ( [] => )
You can add all friends_id into an array and then use this code.
//$friends is an array.
$friendsSQL = implode(',',$friends);
$query = $database->prepare("SELECT * FROM posts WHERE user_id = '$my_id' OR user_id IN($friendsSQL)");
You should note that $friendsQuery->fetchAll() will return an Array. Also, if PDO::FETCH_ASSOC is supplied as an additional argument [fetchAll(PDO::FETCH_ASSOC)], it would be an associative array.
The structure of the array could look like this :
Array ( [int] datasetNumber =>
Array (
[string] "friendship_id" => [int] 1,
[string] "user_one" => [int] 1,
[string] "user_two" => [int] 1
)
)
I gave some more Information about returning data from PDO Statements in this post.
your $friends variable is being returned by a fetchAll() function so will probably be a list of results. Your then using that list as a string in the query which won't work. You need to extract the ID of the friend from the $friends list.
if you're using PDO then use the variable handling built into PDO ie:
$query = $database->prepare("SELECT * FROM posts WHERE user_id = :variable_ID OR user_id = '1'");
$query->bindValue( ':variable_ID', $my_id, PDO::PARAM_STR ] );
$query->execute();
Related
I am creating a comment and reply system in PHP using the tree concept.
I have created a comment table in my database -
CREATE TABLE `comments` (
`cm_id` int(11) NOT NULL,
`cm_message` text NOT NULL,
`postid` int(11) DEFAULT NULL,
`user_id` int(11) NOT NULL,
`created` datetime NOT NULL,
`parentid` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
As you can see, I have contained parent id and comment id with post id.
My tree function
function fetchCategoryTree($parentid = 0,$user_tree_array = '') {
global $db;
global $postid;
if (!is_array($user_tree_array))
$user_tree_array = array();
$stmt = $db->prepare('SELECT
comments.cm_message, comments.cm_id,comments.parentid,comments.created,users.username, users.image
FROM comments
INNER JOIN users ON comments.user_id = users.user_id
WHERE comments.postid=:postid AND parentid =:parentid ORDER by comments.cm_id DESC');
$stmt->execute(array('parentid' => $parentid,
'postid' => $postid
));
if($stmt->rowCount() > 0) {
while ($row = $stmt->fetchObject()) {
$user_tree_array[] = array("cm_id" => $row->cm_id,"image" => $row->image,"parentid" => $row->parentid,"username" => $row->username,"created" => $row->created, "cm_message" =>$row->cm_message);
$user_tree_array = fetchCategoryTree($row->cm_id, $user_tree_array);
}
}
return $user_tree_array;
}
Calling a function
$categoryList = fetchCategoryTree();
It's working but,
As you can see I have made a global postid in the function.
if I give the postid inside
$categoryList = fetchCategoryTree($postid);
and change the function look like -
function fetchCategoryTree($postid) {
global $db;
$parentid = 0;
$user_tree_array = '';
}
then it doesn't work.
I want to convert it to OOPS class and method but it's not working like this.
The main points are here -
I am inserting comments and replies in the same table.
I am fetching comments and replies by postid using while loop on the same page -
like -
post 1
comments for post 1
with reply
post 2
comments for post 2
with reply
post n
comments for n post
with reply n
What are the best ways to do that in OOPS?
class and method
How can I use postid at the calling time without any errors?
I made this more understandable what i want here, so:
I want to SELECT the users here that has the ,for example , "coder" in his "proffesion" field, in Mysql table. Because as you see here by this code it will pick a random user except the user who is logged in. And i need to make another condition after AND ( see the code line 2 ) so it FINDS and puts out only user with the "coder" written in proffesion field. What info do you need more to help to solve the problem?
ps. it should pick from column - PROFESSION
public function whoToFollow1($user_id, $profileID){
$stmt = $this->pdo->prepare("SELECT * FROM `users` WHERE `user_id` != :user_id AND (what stetement need to be here) ORDER BY rand() LIMIT 1");
$stmt->execute(array("user_id" => $user_id));
$users = $stmt->fetchAll(PDO::FETCH_OBJ);
echo '<div class="wrap"><div class="inner"><div class="title"></div>';
foreach ($users as $user) {
You just need to add the condition you described to the query i.e.
$profession = "coder";
$stmt = $this->pdo->prepare("SELECT * FROM `users` WHERE `user_id` != :user_id AND profession = :profession ORDER BY rand() LIMIT 1");
$stmt->execute(array("user_id" => $user_id, "profession" => $profession));
// I am assuming you have profession table with id and profession name/type
SELECT *
FROM `users`
INNER JOIN profession on users.profession_id = profession.id
WHERE `user_id` = :user_id
AND profession.id = :profession_id
$stmt = $this->pdo->prepare("SELECT * FROM `users` WHERE `user_id` != :user_id AND `profession`=:job ORDER BY rand() LIMIT 1");
$stmt->execute(array("user_id" => $user_id, "job" => $job));
Note: $job will be used to represent the current job you wish to target.
I am trying to create a simple forum in a MVC architecture.
This is my database setup (the relevant part):
Table: forum_categories
`forum_categories` (
`cat_id` INT(8) NOT NULL AUTO_INCREMENT,
`cat_title` VARCHAR(255) NOT NULL,
`cat_desc` TEXT NOT NULL,
PRIMARY KEY (`cat_id`),
UNIQUE KEY (`cat_title`)
Table: forum_topics
`forum_topics` (
`topic_id` INT(8) NOT NULL AUTO_INCREMENT,
`cat_id` INT(8) NOT NULL COMMENT 'foreign key with forum_categories table',
`user_id` INT(11) NOT NULL COMMENT 'foreign key with users table',
`topic_title` VARCHAR(255) NOT NULL,
`topic_desc` TEXT NOT NULL,
`topic_date` DATETIME DEFAULT NULL,
PRIMARY KEY (`topic_id`),
FOREIGN KEY (`cat_id`) REFERENCES forum_categories (`cat_id`) ON DELETE CASCADE ON UPDATE CASCADE
Example of the functionality, I would like to achieve:
Category 1 has cat_id = 1
Category 2 has cat_id = 2
Topic 1 has cat_id = 1
Topic 2 has cat_id = 2
Now when category 1 is selected I just want topic 1 to show.
If category2 is selected I just want topic 2 to show.
This prepared SQL statement achieves that:
PREPARE stmnt FROM
'SELECT *
FROM forum_categories fc
JOIN forum_topics ft ON fc.cat_id = ft.cat_id
WHERE fc.cat_id = ?
ORDER BY ft.topic_date DESC';
SET #a = 1;
EXECUTE stmnt USING #a;
My Problem: I would like to move this functionality into my PHP MVC structure.
Here is my attempt, which does not work (it shows all topics in all categories).
Controller
/**
* Show all the topics in the chosen category
*/
public function showForumTopics()
{
$topic_model = $this->loadModel('Forum');
$this->view->forum_topics = $topic_model->getForumTopics();
$this->view->render('forum/viewTopics');
}
Model
/**
* Gets an array that contains all the forum topics in the database.
* Each array element is an object, containing a specific topic's data.
* #return array All the forum topics
*/
public function getForumTopics($cat_id)
{
$sql = 'SELECT * FROM forum_categories fc JOIN forum_topics ft ON fc.cat_id = ft.cat_id WHERE fc.cat_id = :cat_id ORDER BY ft.topic_date DESC';
$query = $this->db->prepare($sql);
$query->execute(array(':cat_id' => $cat_id));
return $query->fetchAll();
}
View
if ($this->forum_topics) {
foreach($this->forum_topics as $key => $value) {
echo '<p><strong>Title:</strong>' . $value->topic_title . '</p>';
echo '<p><strong>Description:</strong> ' . $value->topic_desc . '</p>';
echo '<p><strong>Author:</strong> ' . $value->topic_author . '</p>';
echo '<p><strong>Date:</strong> ' . $value->topic_date . '</p>';
}
} else {
echo 'No forum topics.';
}
Help would be highly appreciated! Thank you!!
For example, your page http://example.com/?cat_id=2
Your code should be like this
Controller
public function showForumTopics()
{
$default_category = 1;
$topic_model = $this->loadModel('Forum');
$cat_id = isset($_GET['cat_id']) ? $_GET['cat_id'] : $default_category;
$this->view->forum_topics = $topic_model->getForumTopics($cat_id);
$this->view->render('forum/viewTopics');
}
Model
public function getForumTopics($cat_id) {
$sql = 'SELECT * FROM forum_categories fc JOIN forum_topics ft ON fc.cat_id = ft.cat_id WHERE fc.cat_id = :cat_id ORDER BY ft.topic_date DESC';
$query = $this->db->prepare($sql);
$query->execute(array(':cat_id' => $cat_id));
return $query->fetchAll(); }
View
if ($this->forum_topics) {
foreach($this->forum_topics as $key => $value) {
echo '<p><strong>Title:</strong>' . $value->topic_title . '</p>';
echo '<p><strong>Description:</strong> ' . $value->topic_desc . '</p>';
echo '<p><strong>Author:</strong> ' . $value->topic_author . '</p>';
echo '<p><strong>Date:</strong> ' . $value->topic_date . '</p>';
}
} else {
echo 'No forum topics.';
}
Your problem is that your backend requires and ID to pull the specific category (and via the join, the correct topic). In your DB Query, you are looking for it here: WHERE fc.cat_id = ?
Your getForumTopics($cat_id) function also requires the ID to pass into that prepared statement. Problem is you aren't passing any ID into that function when you call it:
$this->view->forum_topics = $topic_model->getForumTopics();
So without anything coming through, your function is now broken and should be throwing an error. You have two options at this point:
Provide an ID from the page, through the controller to your function (hint, you'll have to add it to the URL like #kringeltorte suggested so the page knows what to load!)
Make a backup that lists all topics when no specific category is chosen. You'd do that with something like this in your function definition:
// Specifying the backup option null here, for when there is no ID passed
public function getForumTopics($cat_id = null) {
// Simple check for an ID
if ($id) {
// Run the code you had before
$sql = 'SELECT * FROM forum_categories fc JOIN forum_topics ft ON fc.cat_id = ft.cat_id WHERE fc.cat_id = :cat_id ORDER BY ft.topic_date DESC';
$query = $this->db->prepare($sql);
$query->execute(array(':cat_id' => $cat_id));
return $query->fetchAll(); }
} else {
// Otherwise, do the same thing as above but without a WHERE clause
}
}
Ok, so I am creating a web app with php and mysqli.
I have a table friends which is a simple set up:
f_id int(11)
uid int(11)
fids TEXT
now its basically like a row for each user with the fids consisting of a lot of numerical values (other userids) separated by commas like: 1,2,3
so I use this function to get each user's friends:
function getFriends($db, $userid)
{
$q = $db->query("SELECT fids FROM friends WHERE uid='$userid'");
$ar = $q->fetch_assoc();
$friends = $ar['fids'];
$fr = explode(",", $friends);
return $fr;
}
but each posts comments that appear to each of their friends. my problem comes from trying to sort these comments by the time they were posted.
lets say my comments table is:
c_id int(11)
uid int(11)
c_text TEXT
c_time int(11)
I want to be able to get the comments posted by each 'friend' put them all into an array together, then sort them from their c_time value, then all the values from that particular row in the comments table.
The problem comes from my how I've set up my friends table.
I'm using:
$fr = getFriends($db, $userid);
$updates = array();
$i = 0;
foreach( $fr as $friend)
{
// Get Updates from friends and from self
$q = $db->query("SELECT up.*, u.* FROM updates up
LEFT JOIN users u ON u.id = '$friend'
WHERE (up.userid = '$userid') ORDER BY up.up_id DESC");
while($ar = $q->fetch_array(MYSQLI_BOTH))
{
$updates[$i] = $ar;
$i++;
}
}
$sortArray = array();
foreach($updates as $update){
foreach($update as $key=>$value){
if(!isset($sortArray[$key])){
$sortArray[$key] = array();
}
$sortArray[$key][] = $value;
}
}
$orderby = "up_id";
array_multisort($sortArray[$orderby],SORT_DESC,$updates);
$updates_limit = array_slice($updates, 0, 20);
to get the comments from each friend, sorting it by time, then slicing it to the first 20.
However when I var_dump($updates_limit) it takes the last row in the comments table, and then makes it look like each friend posted the same comment.
Can anyone see the problem or a better way of addressing this issue?
I'd completely refactor the friends table to look something more like this: (Also, use english - Characters are cheap :c))
CREATE TABLE friends (
user_id int FOREIGN KEY REFERENCES user(id)
, friend_id int FOREIGN KEY REFERENCES user(id)
, PRIMARY KEY (user_id, friend_id)
);
Then you can take essentially the same comment table:
CREATE TABLE comment (
comment_id int PRIMARY KEY
, user_id int FOREIGN KEY REFERENCES user(id)
, comment_text text
, comment_time datetime
);
And your "query for friend's comments" becomes:
SELECT comment_id, comment.user_id, comment_text, comment_time
FROM friends
INNER JOIN comment
ON comment.user_id = friends.friend_id
WHERE friends.user_id = ? #Target it
ORDER BY comment_time DESC
LIMIT 0, 20;
You can even speed this up by adding a few indexes - like comment(user_id).
I'm trying to count how many times a certain article has been graded for example how many times have users_articles_id 3 been graded by my members.
I'm also trying to count the points for a certain article for example users_articles_id 3 is related to the ratings database by its ratings_id the rating points should be a total of 13.
I was wonder if I was doing this right because to me it looks all wrong? I was hoping if some one can help me fix this? And where should my code go exactly?
I'm using PHP and MySQL?
Here is my MySQL tables
CREATE TABLE articles_grades (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
ratings_id INT UNSIGNED NOT NULL,
users_articles_id INT UNSIGNED NOT NULL,
user_id INT UNSIGNED NOT NULL,
date_created DATETIME NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE ratings (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
points FLOAT UNSIGNED NOT NULL DEFAULT 0,
PRIMARY KEY (id)
);
Database Input
articles_ratings
id ratings_id users_articles_id user_id date_created
1 3 2 32 2010-01-13 02:22:51
2 1 3 3 2010-01-13 02:23:58
3 2 3 45 2010-01-13 02:24:45
ratings
id points
1 10
2 3
3 5
Here is the PHP code I'm trying to fix.
// function to retrieve rating
function getRating(){
$sql1 = "SELECT COUNT(*)
FROM articles_ratings
WHERE users_articles_id = '$page'";
$result = mysql_query($sql1);
$total_ratings = mysql_fetch_array($result);
$sql2 = "SELECT COUNT(*)
FROM ratings
JOIN ratings ON ratings.id = articles_ratings.ratings_id
WHERE articles_ratings.users_articles_id = '$page'";
$result = mysql_query($sql2);
$total_rating_points = mysql_fetch_array($result);
if(!empty($total_rating_points) && !empty($total_ratings)){
// set the width of star for the star rating
$rating = (round($total_rating_points / $total_ratings,1)) * 10;
echo $rating;
} else {
$rating = 100;
echo $rating;
}
}
Well I think there are several issues here.
1) You are defining a table called articles_grades, not articles_ratings, as in your code.
2) Why do articles_grades and ratings need to be in separate tables? There is a one-to-one correspondence between those tables.
3) You need to do sum(points) in your second query.
4) You can combine both queries into a single query.
Here's how I would do it if you don't change the schema:
<?php
mysql_connect('localhost','root','fake123123');
mysql_select_db('test');
$result = mysql_query('SELECT users_articles_id,count(*),sum(r.points)
FROM articles_grades ag,ratings r
WHERE ag.ratings_id = r.id
GROUP BY users_articles_id');
if (!$result)
die('invalid');
else{
echo '<table><tr><th>Article Id</th><th>#Ratings</th><th>#Points</th></tr>';
$a = mysql_fetch_row($result);
while($a){
echo '<tr><td>'.implode('</td><td>',$a).'</td></tr>';
$a = mysql_fetch_row($result);
}
echo '</table>';
}
?>
You can run this as a CGI script. It should return a table of the results.
Let me know if this helps.