Check if usergroup exists - php

I have 2 variables which both hold comma separated lists. One is a permissions variable and the other is the usergroup list of the person wanting to access a page.
1 = user
2 = editor
3 = moderator
4 = admin
<?php
$query = mysql_query("SELECT * FROM users WHERE id='{$user['id']}'");
$user = mysql_fetch_assoc($query);
$query2 = mysql_query("SELECT * FROM menu WHERE active='1'");
$page = mysql_fetch_assoc($query2);
echo $user['usergroup']; // 1
echo $page['usergroup']; // 2,3,4
?>
I need help to find a way to compare both variables to check if they have the right usergroup to access a page
if $user['usergroup'] contains a number from $page['usergroup'] do X otherwise do Y
Thanks :)

$user = "1,3,5";
$page = "1,2,5";
//explode and trim for a case you have spaces '1, 2,3 ,4'
$u = array_map('trim', explode(",", $user));
$p = array_map('trim', explode(",", $page));
$intersect = array_intersect( $u, $p );
if ( count($intersect) )
{
//ok
print_r( $intersect );
}
else
{
//error
}
Allowed permissions:
Array
(
[0] => 1
[2] => 5
)

Try this:
$query = mysql_query("SELECT * FROM users WHERE id='{$user['id']}'");
$user = mysql_fetch_assoc($query);
$query2 = mysql_query("SELECT * FROM menu WHERE active='1'");
$page = mysql_fetch_assoc($query2);
$pageGroups = explode(',', $page['usergroup']);
if (array_search($user['usergroup'], $pageGroups) !== false) {
//TODO: User have access
}

Although I wouldn't store permissions as a commaseparated string, something like this might be what you are looking for:
$pageaccess = explode(',', $page['usergroup']);
if(in_array($user['usergroup'], $pageaccess)) {
// Has permissions. Code goes here.
}
edit: renamed $permissions to $pageaccess

While all the answers you got should work I'd like to add a comment to your approach. It's never a good idea to store comma seperated lists into your database. It offends the concept of database normalization.
Instead of storing comma seperated lists you should reconsider your database design. A possible approach could be:
users (user_id, username)
permissions(permission_id, permission)
user_permissions(up_id, permission_id, user_id)
That is much more efficient since you don't need to process the data that come from your database any further. You can just query your database and ask if the user has a certain permission.
SELECT `up_id` FROM `user_permissions` WHERE `user_id` = '{$uid}' AND `permission_id` = {5}
Taking this a step further you can assemble a little access control list:
users (user_id, username)
groups (group_id, name)
user_groups(ug_id, group_id, user_id)
permissions(permission_id, permission)
group_permissions(gp_id, group_id, permission_id)

Related

SELECT a value multiple time in SQL

I have a code in PHP where I want to display multiple times values, and so, even if these values are the same between them. My code is simple :
$sql = "SELECT photo from table WHERE username IN ('1','2','2') ORDER BY id DESC ";
$res = array();
$result = mysqli_query($con,$sql);
while($row = mysqli_fetch_array($result)){
array_push($res, $row['photo']);
}
echo json_encode($res);
But this code only display (in json) an array of two values (because the values of photo of the username 2 are the same).
What I want to achieve is to make an array with the exact same number of values of the number of username I defined WHERE username IN ('1','2','2') (so here, 3 values).
I hope you understood me, thanks for helping me !
I think what you're after is to list even the duplicates in the end result. As your SQL will only retrieve the unique items, the idea would be to include the username in the SQL result set. Then use the original list of user names ($userNames) and add in the photo for each of them.
I've used mysqli_fetch_all() to simplify the process of fetching all of the data, then used array_column() to make the username the key for the photos.
$userNames = array(1,2,2);
$sql = "SELECT username, photo
from table
WHERE username IN ('".implode("','", $userNames)."')
ORDER BY id DESC ";
$res = array();
$result = mysqli_query($con,$sql);
$photos = mysqli_fetch_all($result, MYSQLI_ASSOC);
$photos = array_column($photos, "photo", "username");
foreach ( $userNames as $user ) {
if ( isset($photos[$user])) {
$res[] = $photos[$user];
}
else {
$res[] = '';
}
}
echo json_encode($res);
You would use left join:
select t.photo
from (select '1' as username union all select '2' union all select '3'
) u left join
table t
on t.username = u.username
order by t.id desc;
Note this will return rows, even when the user name does not exist. If you want to filter those rows, remove the left so you are doing an inner join.

How to while loop distinct data?

Let's Say I have Data...
ID... 1, 2, 3, 4, 5, 6
FirstName... Donald, Tabatha, Jeremy, Samuel, Donald, Tabatha
LastName... Faulknor, Kolasa, Jones, Jackson, Faulknor, Kolasa
In this example, I would want to display
Donald Faulknor, Tabatha Kolasa, Jeremy Jones, Samuel Jackson
I can either display all data (repeating the duplicate entries) or, if I use SELECT DISTINCT then I will only display the first instance, which in this case would be Donald Faulknor. Here's my code...
$sql55 = "SELECT * FROM messages WHERE lowerID = :lowerIDb || higherID = :higherIDb";
$stmt55 = $pdo->prepare($sql55);
$stmt55->bindValue(':lowerIDb',$user_id);
$stmt55->bindValue(':higherIDb',$user_id);
$stmt55->execute();
while($row55 = $stmt55->fetch(PDO::FETCH_ASSOC)) {
$lowerID55 = $row55['lowerID'];
$higherID55 = $row55['higherID'];
if($lowerID55 == $user_id) { $user_id55 = $higherID55; } elseif($higherID55 == $user_id) { $user_id55 = $lowerID55; }
$sql56 = "SELECT DISTINCT user_id FROM users WHERE user_id = :user_id55";
$stmt56 = $pdo->prepare($sql56);
$stmt56->bindValue(':user_id55',$user_id55);
$stmt56->execute();
while($row56 = $stmt56->fetch(PDO::FETCH_ASSOC)) {
$id56 = $row56['user_id'];
$sql58 = "SELECT * FROM users WHERE user_id = :user_id58";
$stmt58 = $pdo->prepare($sql58);
$stmt58->bindValue(':user_id58',$id56);
$stmt58->execute();
while($row58 = $stmt58->fetch(PDO::FETCH_ASSOC)) {
$firstname56 = $row58['firstname'];
$lastname56 = $row58['lastname'];
$profilePhoto56 = $row58['profilePhoto'];
$sql57 = "SELECT * from photos WHERE id = :profilePhotoa";
$stmt57 = $pdo->prepare($sql57);
$stmt57->bindValue(':profilePhotoa',$profilePhoto56);
$stmt57->execute();
while($row57 = $stmt57->fetch(PDO::FETCH_ASSOC)) {
$profilePhotoPath57 = $row57['path'];
echo '';
echo ' '.$firstname56.' '.$lastname56.'';
}
}
}}
To better understand the above code... I have messages to and from users, of course, there will be many to and from the same users. I'm trying to get their name (the recipient) to show once in a side bar (indicating messages from/to that person. Let's say for example, I have 20 messages to and from one person. I can either get the name to repeat twenty times (not using DISTINCT) or I can only get one person's name to show (using DISTINCT). So if there's 8 people to send this user an email, only one name will show.
Much help is greatly appreciated. For questions or more elaboration, please feel free to ask questions. Thank You! :)

Wordpress/PHP/MySql sort results

I have a query that loads values from the database based upon a province a user lives:
$query = "SELECT * FROM $wpdb->usermeta WHERE meta_key='provincie' AND ( ".$provincie_check.")";
This loads all provinces that a user holds.
Based upon this I load the users:
for($p=0; $p <count($personen); $p++){
$persoon = $personen[$p];
if ($p % 2 == 0){
$oddeven = 'even';
}else{
$oddeven = 'odd';
}
$id = $persoon->user_id;
$user_info = get_userdata($id);
$p_fname = get_user_meta($id, 'first_name', true);
$p_fname = array($p_fname);
sort($p_fname);
}
For every user I create a table to view them. I want them sorted upon ther first name. So I thought I create an array of all first names and sort that.
But no luck.
How can I sort the for-loop to view my users sorted by their first name?
Why you don`t extract them directly with SQL?
$query = "SELECT user_id, meta_value as name
FROM $wpd->usermeta
WHERE meta_key
LIKE 'first_name'
AND user_id
IN (SELECT user_id
FROM $wpdb->usermeta
WHERE meta_key='provincie'
AND ( ".$provincie_check."))
ORDER BY name";

How to optimise large mySQL within a foreach loop in PHP?

I have a function that gets user details and returns an associative array consisting of arrays of each user with its related data. My function works except that it doesn't work as well when it has to fetch large number of rows from mySQL.
function function_name($DB, $id)
{
//Prepare, bind and execute statements
//Returns one value or an array
}
function main_function($DB, $id_list)
{
foreach($id_list as $user_id)
{
//Calls function_name
$data = function_name($DB, $user_id);
}
//Returns a nested associative array
}
I have been told that I should move the bind param statement outside of the foreach loop in my case but I have tried and I keep getting error "MySQL has went away" message. How can I optimise querying from mysql when I could potentially be querying 10,0000 id at one time?
Please refer to the code snippet below for detailed explanation.
function getUserEvent($DB_3308, $user_id)
{
$user_event = array ();
$sql_get_user_event = "SELECT * FROM user_event WHERE user_id = ?";
$statement_user_event = $DB_PUMA_3306->link->prepare ( $sql_get_user_event);
$statement_user_event ->bind_param ( "s", $user_id );
$statement_user_event ->execute ();
if ($rs_user_event = $statement_user_event->get_result ())
{
while ( $row = $rs_user_event->fetch_assoc () )
{
$user_event [] = $row;
}
}
return $user_event;
}
function getUserDetails($DB_3306, $DB_3308, $user_list)
{
$user_details = array ();
foreach ( $user_list as $user_id )
{
$temp = array ();
$user_personal = null;
$user_event = null;
$user_personal = getUserContact ( $DB_3306, $user_id );
$user_event = getUserEvent( $DB_3308, $userid );
$temp ['user_id'] = $user_id;
$temp ['full_name'] = $user_personal ['full_name'];
$temp ['tel_no'] = $user_personal ['tel_no'];
$temp ['email'] = $user_personal ['email'];
$temp ['events'] = $user_event ;
$user_details [] = $temp;
}
return $user_details;
}
Why can't you get some 50 or 100 userID in array before fetching that from database and fetch it in bulk to reduce more query load?
$implodedUserIDs = implode(',', $userIDs);
$query = "SELECT * FROM user_event WHERE user_id IN ($implodedUserIDs)";
It will reduce some load. Also you can give some sleep in every load. Just try to optimize your code as much as possible. :)
You appear to be looping around (potentially) 10000 users and for each one performing at least 2 queries. Each query has a small over head to parse it, etc, and hence with a large number of queries this can rapidly add up.
I would suggest that if possible you merge the 2 queries together, doing a join to get both the users contact details and the user event details.
I would also suggest that you perform this single query once in total for all user ids rather than once per user id. Normally this would be easy to do using IN with a list of user ids, but with 10000 this is not really viable. As such generate a temp table containing your list of user ids.
Very roughly (and making assumptions on your database class and on your actual data) something like this:-
function getUserDetails($DB_3306, $DB_3308, $user_list)
{
$sql = 'CREATE TEMPORARY TABLE user_list_tmp
(
user_id INT
)';
$DB_3306->execute($sql);
$user_list_split = array_chunk($user_list, 250);
foreach($user_list_split as $user_list_split_chunk);
{
$sql = 'INSERT INTO user_list_tmp (user_id) VALUES ('.implode('),(', $user_list_split_chunk).')';
$DB_3306->execute($sql);
}
$sql = "SELECT a.user_id, b.full_name, b.tel_no, b.email, c.event_id
FROM user_list_tmp a
INNER JOIN user_contact b
ON a.user_id = b.user_id
LEFT OUTER JOIN user_event c
ON a.user_id = c.userid
WHERE user_id = ?
ORDER BY a.user_id, c.event_id";
$statement_user_event = $DB_3306->link->prepare ( $sql);
$statement_user_event ->execute ();
$user_details = array();
if ($rs_details = $statement_user_event->get_result ())
{
while ( $row = $rs_details->fetch_assoc () )
{
$user_details[$row['user_id']]['user_id'] = $row['user_id'];
$user_details[$row['user_id']]['full_name'] = $row['full_name'];
$user_details[$row['user_id']]['tel_no'] = $row['tel_no'];
$user_details[$row['user_id']]['email'] = $row['email'];
$user_details[$row['user_id']]['events'][] = $row['event_id'];
}
}
return $user_details;
}
This takes you array of passed user ids, chunks it into arrays of 250 and inserts them into a temp table (I tend to insert in batches of 250 as a reasonable balance between a readable and fast insert statement and performing a min number of separate statements - you may chose to split it into larger or smaller chunks).
It then performs a single query that joins the temp table against the user_contact table and left joins it against the user_event table. Each user will return multiple rows, one for each even (but still one row if no events). It is putting these into an array, and I have cheated a bit here by using the user_id as the key of the array. So for the first row for the user id it will save the details for the user, and on any subsequent rows for the user (for further events) the user details will just over write themselves. The events details are just put into the next array member of the events array for that user.

Merging data from two different data sets (Facebook & MySQL)

I'm wondering if this is the best way to tackle this issue. I am merging a Facebook users friends data, (from facebook - returns a multi array) with the votes from the users in that list that voted (from MySQL).
This is how I accomplished this. I'm a junior developer and looking for help on making my code as optimized as possible.
public function getFriendVotes(){
global $facebook;
// Get The users friends that use this app from facebook
$friends = $facebook->api_client->fql_query(
"SELECT uid, first_name, last_name
FROM user
WHERE uid IN (SELECT uid2 FROM friend WHERE uid1=$this->user)"
);
// Create an array of just the ids
foreach($friends as $friend){
$userids[] = $friend['uid'];
}
// Create a string of these ids
$idstring = implode(",", $userids);
// Get the votes from only the users in that list that voted
$result = $this->db->query(
"SELECT vote, userid FROM user_votes WHERE userid IN ($idstring)"
);
// Create a new result set (multi array). Include the data from the first
// Facebook query, but include only those who voted and append their votes
// to the data
$row = $result->fetch_assoc();
foreach($friends as $friend){
if($row['userid'] == $friend['uid']){
$return[$count] = $friend;
$return[$count]['vote'] = $row['vote'];
$row = $result->fetch_assoc();
$count++;
}
}
return $return;
}
I asume that fql_query does support mysql syntax and it would be more efficient to use LEFT JOIN instead creatig extra query, here is my version of your code:
public function getFriendVotes(){
global $facebook;
// Get The users friends that use this app from facebook
$friends = $facebook->api_client->fql_query("
SELECT DISTINCT u.uid,u.first_name,u.last_name
FROM user AS u
LEFT JOIN friend AS f ON uid=uid2
WHERE f.uid1='{$this->user}'
");
$arrayUsers = array();
// Create an array of just the ids
foreach($friends as $v){
$arrayUsers[$friend['uid']] = $v;
}
unset($friends);
// Create a string of these ids
$idstring = implode(",", array_keys($arrayUsers));
// Get the votes from only the users in that list that voted
$result = $this->db->query(
"SELECT vote, userid FROM user_votes WHERE userid IN ({$idstring})"
);
$result = array();
// Create a new result set (multi array). Include the data from the first
// Facebook query, but include only those who voted and append their votes
// to the data
while($v = $result->fetch_assoc())
{
if(isset($arrayUsers[$v['userid']])
{
$arrayUsers[$v['userid']] = $v['vote'];
$result[] = $arrayUsers[$v['userid']];
unset($arrayUsers[$v['userid']], $v);
}
}
return $return;
}
I can't tell you how your code would perform without measuring and testing. I would look for other issues with your code, that would make it a bit more readable/maintanable. For example:
Create smaller methods.
Inside the main method , I see some chunks of code that are well commented. Why not create a method instead of making a huge comment in the main method?
For example:
// Get The users friends that use this app from facebook
$friends = $facebook->api_client->fql_query(
"SELECT uid, first_name, last_name
FROM user
WHERE uid IN (SELECT uid2 FROM friend WHERE uid1=$this->user"
);
return $friends;
Would make an interesting
functin get_users_friends_from_facebook($facebook){
// Get The users friends that use this app from facebook
$friends = $facebook->api_client->fql_query(
"SELECT uid, first_name, last_name
FROM user
WHERE uid IN (SELECT uid2 FROM friend WHERE uid1=$this->user"
);
return $friends;
}
In the same manner,
// Get the votes from only the users in that list that voted
$result = $this->db->query(
"SELECT vote, userid FROM user_votes WHERE userid IN ($idstring)"
);
Is a good candidate to
function get_votes_from_voters(){
// Get the votes from only the users in that list that voted
$votes = $this->db->query(
"SELECT vote, userid FROM user_votes WHERE userid IN ($idstring)"
);
}
Give variables meaningful names to the context.
$return isn't a good name. Why don't you name it $users_votes for example?
Try to keep the naming convention of your plataform.
Check the apis you're using. Are they using camelCase? Are they using underscores? Try to keep with your libraries and plataform conventions. Check this topic for a good reference.
And welcome to SO. Your code is fine. Try to read some OO principles, you could even cut more lines of your code. All the simple advices I wrote here are avaiable in a great book named Code Complete.
I took points from all your comments and rewrote this method as below. Thanks for all the great input.
public function getAppUserFriends(){
global $facebook;
return $facebook->api_client->fql_query(
"SELECT uid, first_name, last_name
FROM user
WHERE uid IN (SELECT uid2 FROM friend WHERE uid1=$this->user)
AND is_app_user;"
);
}
public function getFriendVotes(){
// Get the users friends that use this app
$friends = $this->getAppUserFriends();
// Create an array with the ids as the key
foreach($friends as $v){
$arrayFriends[$v['uid']] = $v;
}
// Create a string of these ids
$idString = implode(",", array_keys($arrayFriends));
// Get the votes from only the users in that list that voted
$result = $this->db->query(
"SELECT vote, userid
FROM user_votes
WHERE pollid=$this->poll
AND userid IN ($idString)"
);
// Pluck out user data from facebook array where the user has voted
// and add the vote to that array
while($row = $result->fetch_assoc()){
$friendsVotes[$row['userid']] = $arrayFriends[$row['userid']];
$friendsVotes[$row['userid']]['vote'] = $row['vote'];
}
return $friendsVotes;
}
Are you having performance troubles in this method? Because unless you are, there's no need to optimize it.
Code first, profile the code, and then optimize where it does the most good.
$friends = $facebook->api_client->fql_query(
"SELECT uid, first_name, last_name
FROM user
WHERE uid IN (SELECT uid2 FROM friend WHERE uid1=$this->user"
);
could probably be shortened to
$userids = $facebook->api_client->fql_query(
"SELECT uid
FROM user
WHERE uid IN (SELECT uid2 FROM friend WHERE uid1=$this->user)"
);
because the uid is the only thing you seem to be using from fb
It was a little hard for me to tell what you are trying to do, but you might consider looking at PHP's array_intersect (and its cousins).
A = {1:'fred', 2:'bob'}
B = {1: 2, 3: 0}
C = array_intersect( array_keys(A), array_keys(B) )
D = {}
foreach (C as c) {
D[c] = (A[c], B[c])
}
The syntax is off there but I hope it leads you in the right direction.

Categories