How to implement a PHP condition using pure SQL? - php

I have this tables structure:
// Posts
+----+------------+-----------------------+----------------+-------------+
| id | title | content | money_amount | author_id |
+----+------------+-----------------------+----------------+-------------+
| 1 | title 1 | content 1 | NULL | 12345 |
| 2 | title 2 | content 2 | 25 | 42355 |
| 3 | title 3 | content 3 | 5 | 53462 |
| 4 | title 4 | content 4 | NULL | 36346 |
| 5 | title 5 | content 5 | 15 | 13322 |
+----+------------+-----------------------+----------------+-------------+
// ^^ NULL means this post is free
// Money
+---------+--------------+
| post_id | user_id_paid |
+---------+--------------+
| 2 | 42355 | // He is author of post
| 2 | 34632 | // This row means besides author, this user 34632 can see this post too. Because he paid the money of this post.
| 3 | 53462 | // He is author of post
| 5 | 13322 | // He is author of post
| 3 | 73425 | // This row means besides author, this user 34632 can see this post too. Because he paid the money of this post.
+---------|--------------+
Note1: All post_id(s) in the Money table are belong to those posts which are non-free.
Note2: Always there is a row belong to author of post (which is non-free) in the Money table.
Note3: Money table is just to determines who can see such a post.
Now this user $_SESSION['current_user'] = '23421' wants to see this post id = 2. Here is my code:
$stm = $this->dbh->prepare(SELECT * FROM Posts WHERE id = '2');
$stm->execute();
$result = $stm->fetch(PDO::FETCH_ASSOC);
if ( $result[money] == '') { // money_amount is NULL in the Posts table
this post is free and everybody can see it
} else {
$stm = $this->dbh->prepare(SELECT count(1) FROM Money WHERE post_id = '2' and user_id = $_SESSION['current_user']);
$num_rows = $stm->fetchColumn();
if($num_rows){
$paid = true; // This means current user paid the cost of post and he can see it.
} else {
$paid = false; // this means current user didn't pay the cost of post and he cannot see it.
}
}
I want to know, can I implement those two query in one query and do that condition using MySQL instead of PHP ?

Here is solution using IF and EXISTS functions(MySql):
...
$stmt = $conn->prepare("
SELECT IF(p.money_amount,1,0) as notfree,
EXISTS(SELECT * FROM `Money` WHERE `post_id` = ? AND`user_id_paid` = ?) as paid
FROM `Posts` p WHERE p.id = ? ");
$stmt->execute([2, $_SESSION['current_user'], 2]);
$result = $stmt->fetch(\PDO::FETCH_ASSOC);
if (!$result['notfree']) { // post is free
// this post is free and everybody can see it
} else {
$paid = ($result['paid'])? true : false;
}

You can use a join, and the query below uses LEFT JOIN.
SELECT * FROM Money
LEFT JOIN Posts ON Money.post_id = Posts.id
WHERE ((Posts.money_amount IS NOT NULL AND Money.user_id_paid = :userId)
OR Posts.money_amount IS NULL) AND Posts.id = :postId
Note that :userId is a placeholder for PDO parameterized query, where you should bind the parameter to the placeholder before execution. Like:
$postId = 2;
$stmt->bindParam('userId', $_SESSION['current_user']);
$stmt->bindParam('postId', $postId);
Also note that when binding the placeholder name doesn't need the colon. Using a RIGHT JOIN means you SELECT from the Posts table and join the Money table.

Related

Concatenating conditionally queries in PHP, while trying to get rows from multiple tables

I have a really huge problem with my Social Network Plugin.
In my PHP script, I am concatenating SQL Where Queries, using conditional statements (if/else), because with the same function I'm getting the posts for the search bar and for the profile itselves.
I can't change, for this reason, the main statement.
I'm developing a Mention system, like Facebook's: When an user tags someone on its own profile, the post should be shown on the tagged user's profile too.
To achieve this, I'm editing the function that gets the posts, but can't figure out how to write the query.
Let's look the scenario:
I have two tables, one for posts, the other for the mentions linked to the posts' table.
Table: posts (user_id is the publisher, in_user_id is the profile where the post is published)
+---------+----------+------------+------------+------------+
| post_id | user_id | in_user_id | in_group | updated_at |
+---------+----------+------------+------------+------------+
| 1 | 12 | 12 | 0 | some_text |
| 2 | 1 | 1 | 0 | some_text2 |
| 3 | 2 | 2 | 0 | some_text3 |
| 4 | 5 | 12 | 0 | some_text4 |
| 5 | 5 | 5 | 0 | some_text4 |
| 6 | 5 | 5 | 0 | some_text4 |
+---------+----------+------------+------------+------------+
Table: posts_mentions (post_id is the post that contains the mention, user_id is the mentioned user)
+---------+----------+
| post_id | user_id |
+---------+----------+
| 2 | 12 |
| 3 | 12 |
| 6 | 1 |
+---------+----------+
What I need:
I need, when viewing the profile of user_id 12, to get the post_id of the posts published on 12's profile (posts.post_id 1 and 4), but also the posts where user_id 12 is tagged (posts.post_id 2 and 3), which are listed on posts_mentions.
What I tried:
The actual code is like this (this is an example):
$where="";
if($user_id->isFriend())
{
$where .= "WHERE (in_user_id = $user_id AND in_group = '0')";
}
...
$posts = $db->query(sprintf
("SELECT
*
FROM (SELECT posts.post_id
FROM posts ".$where.") posts
ORDER BY posts.post_id DESC");
(I used subqueries because otherwise I couldn't use conditional-concatenated queries)
I tried to add another sub-query in the $where statement, but it didn't work at all.
I'm really getting crazy. How can I resolve this problem?
You can add a subquery to get the post_ids from the post_mentions table. Something like:
$where .= "WHERE (in_user_id = $user_id AND in_group = '0')
OR post_id IN (
SELECT post_id FROM posts_mentions
WHERE user_id = $user_id
)
";

How can I check two conditions before inserting?

I have three tables:
// Posts
+----+----------+---------------+-----------+
| id | title | content | id_author |
+----+----------+---------------+-----------+
| 1 | title1 | content1 | 1234 |
| 2 | title2 | content2 | 5678 |
+----+----------+---------------+-----------+
// Users
+----+--------+--------+
| id | name | active |
+----+--------+--------+
| 1 | jack | 1 |
| 2 | peter | 0 |
| 3 | John | 1 |
+----+--------+--------+
// Votes
+----+---------+---------+
| id | id_post | id_user |
+----+---------+---------+
| 1 | 32 | 1234 |
| 2 | 634 | 5678 |
| 3 | 352 | 1234 |
+----+---------+---------+
Now I need to check two conditions before inserting a new vote into Votes table:
The id of author and what I have passed are the same? Posts.id_user = :author (I know I can do that by a FK, but I don't want)
The account of current user is active? Users.active = 1
Also here is my query:
INSERT INTO Votes (id_post,id_user)
SELECT ?,?
FROM Posts p
WHERE p.id_user = :author limit 1;
How can I add second condition Users.active = 1 to my query?
EDIT: Actually I'm trying to don't let people be able to vote who are inactive (active = 0). For example if SO bans men, then I cannot vote to post anymore, because I (current user) am banned. So I'm pretty sure $_SESSION['id'] should be used in the query.
INSERT INTO Votes (id_post,id_user)
SELECT p.id,u.id
FROM Posts p, Users u
WHERE p.id_user = :author
AND u.id = :user
AND u.active = 1 limit 1;
then you set parameter user equal to the current user id.
EDIT: I suppose id_user in table Votes must be the voter's id, not the author of the post (correct?), so I fixed the query eliminating the JOIN.
Use and with where
INSERT INTO Votes (id_post,id_user)
SELECT ?,?
FROM Posts p, Users u
WHERE p.id_user = :author and u.active = 1 limit 1;
INSERT INTO Votes (id_post,id_user)
SELECT p.id, u.id
FROM Posts p
INNER JOIN Users u ON 1 = 1
WHERE p.id_user = :author
AND u.id = :current_user_id
AND u.active = 1
LIMIT 1;

How to make a manual 'like' function (like facebook) using MySQL

I am using MySQL and cPANEL for making my website. I'm having a problem in figuring out how to make manual likes function (Facebook like button, or YouTube thumbs up button).
Am I going in the right way to make a "like" button in my website?
VIDEOTABLE:
+----+----------+-----------+
| ID | VIDEO URL| LIKES |
+----+----------+-----------+
| 1 | example 1| 5774 |
| 2 | example 2| 9654 |
| 3 | example 3| 1254 |
| 4 | example 4| 7656 |
| 5 | example 5| 6757 |
| 6 | example 6| 5675 |
| 7 | example 7| 4565 |
+----+----------+-----------+
ID is my serial
VIDEO_URL is my page link or the video link
likes is the users liking this
So, to like the video when USER will clicks the like button without login it will redirect and show the login panel. Then, user will login into it via FACEBOOK or TWITTER so the site can get the USER_ID
Ones the user logins there likes option their will be this code (NOT SURE)
SQL> UPDATE VIDEOTABLE
SET ID = '3', LIKES = ????;
How can i increase +1 value in the table? And then when the USER likes it, the site refreshes and shows the latest value (by increasing by +1).
My Second Problem = How can i restrict the user by giving only one chance to increase the like) and to undo Rollback Query.
You should be able to use the following:
UPDATE VIDEOTABLE SET LIKES=LIKES+1 WHERE ID=3;
However, it may be wise to set a separate lookup table that indicates whether a particular user has already 'liked' something, so that they may not 'like' it more than once.
That table would look like this:
tblLikesLookup
+----+--------+--------+
| ID | videoID| userID |
+----+--------+--------+
| 1 | 1 | 10 |
| 2 | 3 | 7 |
| 3 | 2 | 10 |
| 4 | 8 | 8 |
| 5 | 6 | 8 |
+----+--------+--------+
You could even use this table in place of your VIDEOTABLE to store your likes. To get the number of likes for any given video,
SELECT COUNT(*) FROM tblLikesLookup WHERE videoID=[videoID];
or for all videos,
SELECT COUNT(*) FROM tblLikesLookup GROUP BY videoID;
This may eventually become slow as the volume to your site increases, in which case using a separate counter like the one you have above will come in handy.
if i understand you right,
you can simple increase any number like that:
update VIDEOTABLE set LIKES = LIKES + 1 where ID = ...
so as the same to reduce likes
update VIDEOTABLE set LIKES = LIKES - 1 where ID = ...
it is not important what the user id is. You need video ID
EDIT:
if you want to prevent multiple likes, you need a new table too.
TABLE: USER_TO_LIKES
id | user_id | video_id
so you must firstly check if user liked it before:
$c = mysql_num_rows(mysql_query("select id from USER_TO_LIKES where video_id = 11 and user_id = XX"));
if($c == 0){
mysql_query("update VIDEOTABLE set LIKES = LIKES + 1 where ID = 11 ");
mysql_query("insert into USER_TO_LIKES set video_id = 11, user_id = XX");
}
If unlike:
$c = mysql_num_rows(mysql_query("select id from USER_TO_LIKES where video_id = 11 and user_id = xx"));
if($c > 0)
{
mysql_query("update VIDEOTABLE set LIKES = LIKES - 1 where ID = 11");
mysql_query("delete from USER_TO_LIKES where video_id = 11 and user_id = XX");
}
thats it.
EDIT2: see above
You Will Need the video id And The User That has clicked And Another Table(Called Liked_People) For People Who Has Already Liked Something like that:
+-------+---------+
|UserId |VideoId |
+-------+---------+
|1 |5739 |
+-------+---------+
Now Wen The User Click On Like Send An XMLHTTP request to that PHP function:
function Like($usrid , $vidid){
//usrid = userid And vidid = video id
$Comm1 = $Con->prepare("SELECT * FROM Liked_People WHERE UserId = ? And VideoId = ?");
$Comm1->execute(Array($usrid , $vidid);
if($Comm1->rowCount() == 0){
$Comm2 = $Con->prepare("INSERT INTO Liked_People (UserId , VideoId) VALUES (?,?)");
$Comm2->execute(Array($usrid , $vidid));
$Comm3 = $Con->prepare("SELECT Likes FROM VideoTable WHERE Id = ?");
$result = $Comm3->Execute(Array($vidid));
$final = $result + 1;
$Comm4 = $Con->prepare("UPDATE VideoTable SET Likes=? WHERE ID = ?");
$Comm4->Execute(Array($final , $vidid));
}else { return false;}
}
And That is but if you want to get A list of the usernames that has liked lets say that you have the People Table Like That:
+-------+--------------+
|Id |Username |
+-------+--------------+
|1 |Ahmad Mehmet |
+-------+--------------+
And This is the Code:
function get_likers($vidid){
if($vidid == null or $vidid == "" or $vidid == 0){
return false;
}
$Comm1 = $Con->prepare(SELECT UserId FROM Liked_People WHERE VideoId = ?);
$Comm1->Execute(Array($vidid));
$arr = $Comm1->fetchAll();
$finale = Array();
$i = 0;
foreach($arr as $row){
$Comm = $Con->Prepare("SElECT Username WHERE Id = ?");
$finale[$i] = $Conm->Execute(Array($row[0]));
$i++;
}
return $finale
}
This function is just for backups and Not essential.
function Work_Array($Arr){
echo '<table border="2">';
echo '<tr><th>Users That Has Liked</th></tr>';
foreach($Arr as $el){
echo"<tr><td>";
echo $el;
echo "</td></tr>";
}
echo "</table>";
}
Called Like That:
work_array(get_likers($vidid));
Questions?:D

confusion about result structure from joining tables when each row has multiple correspondence on the second table

lets say i have a simple poll aplication
and each user only can choose one option from poll
my poll table
+-----------+---------------------+
| poll_id | question |
+-----------+---------------------+
| 1 | q1? |
+-----------+---------------------+
| 2 | q2? |
+-----------+---------------------+
| 3 | q3? |
+-----------+---------------------+
my options table
+-----------+---------------------+---------+---------+
| option_id | poll_id | option | counter |
+-----------+---------------------+---------+---------+
| 1 | 1 | yes | 254 |
+-----------+---------------------+---------+---------+
| 2 | 1 | no |392333337|
+-----------+---------------------+---------+---------+
| 3 | 2 | yes | 1000 |
+-----------+---------------------+---------+---------+
right now i do something like this to show all the polls i one page
$polls = $db->query("select * from polls ");
foreach ($polls as $poll)
{
echo $poll->question;
$options = $db->query("select * from options where poll_id = '$poll->id' ");
foreach($options as $op )
{
echo '<checkbox />'.$op->option;
}
}
now if i want to do this using joine and one query
$query = "select p.* , o.* from polls p JOIN options o ON p.poll_id = o.poll_id ";
what data structure is going to be used to represent the result ? perhaps a multidimensional array ? i'm not sure how can i printout the result
like in this example result is going to be
resul_array (
poll_id poll_question option option
1 q1 ys no
2 q2 no
}
if i get the result as an array row one would have columns with similar name ? i'm confused
In my opinion, you just need to add "order by" in your query, and store result in an array, and then process the logic on it.
Something like:
$polls = $db->query("select p.* , o.* from polls p JOIN options o ON p.poll_id = o.poll_id order by p.poll_id");
$currentPoll = $polls[0];
foreach ($polls as $poll)
{
if($poll->poll_id != $currentPoll->poll_id) {
echo $poll->question;
$currentPoll = $poll;
}
echo '<checkbox />'.$poll->option;
}
HTH.

Trouble structuring MySQL query with one-to-many over multiple tables

I have a blog-style app that allows users to tag each post with topics. I keep this data in three separate tables: posts (for the actual blog posts), topics (for the various tags), and posts_topics (a table that stores the relationships between the two).
In order to keep the MVC structure (I'm using Codeigniter) as clean as possible, I'd like to run one MySQL query that grabs all the post data and associated topic data and returns it in one array or object. So far, I'm not having any luck.
The table structure is like this:
Posts
+--------+---------------+------------+-----------+--------------+---------------+
|post_id | post_user_id | post_title | post_body | post_created | post_modified |
+--------+---------------+------------+-----------+--------------+---------------+
| 1 | 1 | Post 1 | Body 1 | 00-00-00 | 00-00-00 |
| 2 | 1 | Post 1 | Body 1 | 00-00-00 | 00-00-00 |
+--------+---------------+------------+-----------+--------------+---------------+
// this table governs relationships between posts and topics
Posts_topics
+--------------+---------------------+-------------------------+-----------------+
|post_topic_id | post_topic_post_id | post_topic_topic_id | post_topic_created |
+--------------+---------------------+-------------------------+-----------------+
| 1 | 1 | 1 | 00-00-00 |
| 2 | 1 | 2 | 00-00-00 |
| 3 | 2 | 2 | 00-00-00 |
| 4 | 2 | 3 | 00-00-00 |
+--------------+---------------------+-------------------------+-----------------+
Topics
+---------+-------------+-----------+----------------+
|topic_id | topic_name | topic_num | topic_modified |
+---------+-------------+-----------+----------------+
| 1 | Politics | 1 | 00-00-00 |
| 2 | Religion | 2 | 00-00-00 |
| 3 | Sports | 1 | 00-00-00 |
+---------+-------------+-----------+----------------+
I have tried this simple query with n success:
select * from posts as p inner join posts_topics as pt on pt.post_topic_post_id = post_id join topics as t on t.topic_id = pt.post_topic_topic id
I've also tried using GROUP_CONCAT, but that gives me two problems: 1) I need all the fields from Topics, not just the names, and 2) I have a glitch in my MySQL so all GROUP_CONCAT data is returned as a BLOB (see here).
I'm also open to hearing any suggestions where I run two queries and try to build out an array for each result; I tried that with the code below but failed (this code also includes joining the user table, which would be great to keep that as well):
$this->db->select('u.username,u.id,s.*');
$this->db->from('posts as p');
$this->db->join('users as u', 'u.id = s.post_user_id');
$this->db->order_by('post_modified', 'desc');
$query = $this->db->get();
if($query->num_rows() > 0)
{
$posts = $query->result_array();
foreach($posts as $p)
{
$this->db->from('posts_topics as p');
$this->db->join('topics as t','t.topic_id = p.post_topic_topic_id');
$this->db->where('p.post_topic_post_id',$p['post_id']);
$this->db->order_by('t.topic_name','asc');
$query = $this->db->get();
if($query->num_rows() > 0)
{
foreach($query->result_array() as $t)
{
$p['topics'][$t['topic_name']] = $t;
}
}
}
return $posts;
}
Any help greatly appreciated.
This query should do the trick. Just change the * to the field list you desire so you are not pulling excess data every time you run the query.
Select
*
FROM
posts,
post_topics,
topics
WHERE
post_topic_topic_id = topic_id AND
post_topic_post_id = post_id
ORDER BY
post_id, topic_id;
Select
*
FROM
posts,
post_topics,
topics,
users
WHERE
post_topic_topic_id = topic_id AND
post_topic_post_id = post_id AND
post_user_id = user_id
ORDER BY
post_id, topic_id;
Holy Cow, You Can do it! See it helps to help. Never knew, try this
Select
post_id,
GROUP_CONCAT(DISTINCT topic_name) as names
FROM
posts,
post_topics,
topics
WHERE
post_topic_topic_id = topic_id AND
post_topic_post_id = post_id
GROUP BY
post_id;
You get
1, 'politics,relligion'
2, 'sports,relligion'

Categories