Looping through a Snowflake with PHP (Discord.php) - php

This is my first post, I do a lot of reading here, so hopefully I avoid embarrassing myself. I've done a lot of searching on the topic, with little results, given that I am new to PHP it doesn't help either, and documentation is sparse on the topic.
The Discord API for PHP is limited to certain pieces of data which you can request, what I am attempting to do is: fetch the members, check what roles they have, and if they have it, count them.
Currently with the API you can count all members within a guild, however you cannot count all members within a guild with a specific role. My end conclusion is to loop through the snowflake and handle the comparisons myself.
This code returns the snowflake (up to 1000) for a guild:
<?php
$json_options = [
"http" => [
"method" => "GET",
"header" => "Authorization: Bot TOKENREDACTED"
]
];
$json_context = stream_context_create($json_options);
$json_get = file_get_contents('https://discordapp.com/api/guilds/GUILDIDREDACTED/members?limit=1000', false, $json_context);
$json_decode = json_decode($json_get, true);
print_r($json_decode);
?>
And the snowflake I am trying to loop through looks like this:
Array
(
[0] => Array
(
[nick] => nickname
[user] => Array
(
[username] => username
[discriminator] => 7697
[id] => 123456789012345
[avatar] => 32ad726b873445fff9145e47144a9465
)
[roles] => Array
(
[0] => 123456789012345678
[1] => 123456789012345678
)
[mute] =>
[deaf] =>
[joined_at] => 2018-05-18T07:22:49.562000+00:00
)
[1] => Array (annnd repeat for the next member)
As you can see the snowflake is quite complicated in terms of arrays.
What I am trying to do here is loop through each array entry ([0],[1],[2] etc.) then to the roles. If the [user] has the role ID 123456789012345678 (for example) then add that member to a count to print, if there's no match then it'll simply ignore that and move onto the next one. But I'm not really sure where to start with this. Any help or direction is appreciated, thank you.

You can use array-filter and get only the element you need with in-array and then count them using simple count method. Consider the following:
$arr = array_filter($json_decode, function($e) {return in_array("123456789012345678", $e['roles']);});
echo count($arr);
If the your "RoleId" is dynamic you can do:
$myRole = "123456789012345678";
$arr = array_filter($json_decode, function($e) use ($myRole) {return in_array($myRole, $e['roles']);});
If you also want to display the username you can do:
foreach($arr as $e) {echo $e['user']['username'];}

This is probably really, really bad practice, especially for a first post. #wesley murch thank you very much for the direction, I got it now. Also, feel free to reply so I can mark you as the answer. Here is the working code which I would like to share with everyone, and a following explanation of what the code does:
<?php
$json_options = [
"http" => [
"method" => "GET",
"header" => "Authorization: Bot Your-Discord-Bot-Token-Here"
]
];
$json_context = stream_context_create($json_options);
$json_get = file_get_contents('https://discordapp.com/api/guilds/your-guild-id-here/members?limit=1000', false, $json_context);
print_r(substr_count ( $json_get ,'Role-Id-To-Look-For'));
?>
This code will query the API for the snowflake, it then converts that snowflake to a string, and counts the occurrences of which that role is contained in the snowflake. In this case, I have 12 people with this role, I can confirm that it successfully returned a value of 12. In this usage case it wasn't necessarily a matter of parsing the array, I was able to just check the snowflake as a string for the ID I was looking for. This usage case is limited in the fact that it won't necessarily return any details of the members which have the role, so I wouldn't say it completely covers every single usage case.

Related

PHP Count Number of Times A String Appears As A Value Inside An Array

So I looked around here for a bit but cant seem to get this right, so I am forced to make a new post. I gave it an effort! Don't hurt me.
Sample Array:
$ndata = Array
(
[ARMOR_HEAD] => Andariel's Visage
[ARMOR_TORSO] => Blackthorne's Surcoat
[ARMOR_FEET] => Illusory Boots
[ARMOR_HANDS] => Gauntlets of Akkhan
[ARMOR_SHOULDERS] => Pauldrons of Akkhan
[ARMOR_LEGS] => Blackthorne's Jousting Mail
[ARMOR_BRACERS] => Nemesis Bracers
[ARMOR_MAINHAND] => Gyrfalcon's Foote
[ARMOR_OFFHAND] => Jekangbord
[ARMOR_WAIST] => Blackthorne's Notched Belt
[ARMOR_RRING] => Ring of Royal Grandeur
[ARMOR_LRING] => Stone of Jordan
[ARMOR_NECK] => Kymbo's Gold
)
$count = count(preg_grep('Akkhan', $ndata));
print_r($count);
So this is only returns 1 instead of 2. I also tried array_search(), but that simply returns a the first found with its key. Or in_array but that is just boolean I guess.. Is there a better way to go about this?
The first parameter to preg_grep is not a string, it is a regular expression pattern represented as a string. Try this:
preg_grep('/Akkhan/i', $ndata)
For more information, see the documentation page for preg_grep.

Turn string into multi dimensional array with keys and values

I am coding a PHP function, and I would like to ask a question. I have a problem with exploding a string in a particular way. I have tried to explain as well as I can down below.
What is this?
Well.. I am working on a solution to decrease the number of tables on my website. I can turn the table for admin rights into one field in the user table. However, I will then need to explode the text field into an array when loading the website. The code is looking like:
<?php
$string = "server1=(ban=(perm=false,normal=true),edit=true,delete=false),adminlog=true,server2=(ban=(perm=false,normal=true),edit=false,delete=true)";
function parseRights( $s="" ){
$context = array();
// code here
}
print_r(parseRights($string));
?>
Basically, I would want the result to be:
Array
(
[server1] => Array
(
[ban] => Array
(
[perm] => false
[normal] => true
)
[edit] => true
[delete] => false
)
[adminlog] => true
[server2] => Array
(
[ban] => Array
(
[perm] => false
[normal] => false
)
[edit] => false
[delete] => true
)
)
True and false should be PHP true and false. If written out like that, I know it will show 1 where true are, and nothing where 0 are.. but it's just to show you what I would like the array to look like after run through the function. I would like it to be able to create an "infinite" array, with each new parentheses creating a new array. Of cource I would gladly accept other ways to distinguish the correct rights if the function would work in the same manner.
Just to help you along a little. Better would be to have related tables that store each functional piece, such as a ban table and permission table both related to the servers table or something similar. If you're not going to store this properly in the database, at least save some trouble:
$array = array('server1'=>array('ban'=>array('perm'=>false,'normal'=>true),'edit'=>true,'delete'=>false));
$string = json_encode($array);
echo $string;
/*
{"server1":{"ban":{"perm":false,"normal":true},"edit":true,"delete":false}}
*/
$new_array = json_decode($string, true);
var_export($new_array);
/*
array
(
'server1' =>
array (
'ban' =>
array (
'perm' => false,
'normal' => true,
),
'edit' => true,
'delete' => false,
),
)
*/
See how the JSON string looks eerily similar to your string? You could also use serialize() but JSON is standardized and portable.
Also, var_dump() and var_export() will show that true and false are actually stored, print_r() just doesn't display the proper type.

CakePHP overriding identical "field" in search conditions

I've come across an odd problem using CakePHP 1.3 to find information. Let's use this dbschema as an example:
id is int(11) PRIMARY auto_increment
amount is float(10,2) NULL
status is ENUM(Completed, Removed, Pending)
id amount status
1 100.00 Completed
2 100.00 Removed
3 100.00 Completed
4 100.00 Completed
5 100.00 Pending
When using Cake's find to retrieve data from this table, I use this query:
$this->Testtable->find('all', array(
'conditions' => array(
'status LIKE ' => 'Removed',
'status LIKE ' => 'Pending',
'status LIKE ' => 'Completed'
)
))
Looking at this query, I would assume that Cake would return all rows that match all of those conditions (which is totally acceptable in SQL), however it only uses the last condition and returns WHERE status LIKE 'Completed'.
I ran a test doing this, which returned all rows correctly (I know it's a more "correct" way to do the query anyway):
'conditions' => array(
'status' => array('Removed', 'Pending', 'Completed')
)
Take the opposite for an example, I want to return all rows that aren't Removed or Pending:
'conditions' => array(
'status !=' => 'Removed',
'status !=' => 'Pending'
)
This query returns all rows with Completed and Removed, as it only listens to the last statement. I assume that this is happening because instead of concatenating these search conditions into the query, Cake is overwriting the conditions based on the "field" being status !=. I can prove this theory by adding a space after the != in either of those conditions, creating the desired result of only Confirmed records.
Can anybody tell me why Cake would do this? As this is a legitimate thing to do in SQL, I see no reason that Cake wouldn't allow you to do it. Does anybody know if this issue is fixed up in newer versions of Cake?
I suppose that this comes down to the fact that at the end of the day, I am reassigning the array value based on that key, and it's not actually CakePHP's fault at all. I had a look into Cake's model.php and found this:
$query = array_merge(compact('conditions', 'fields', 'order', 'recursive'), array('limit' => 1));
I ran a test:
$array = array(
'conditions' => array(
'test' => 'yes',
'test' => 'no'
)
);
$var = 'hello';
$c = compact('array', 'var');
print_r($c);
As mentioned above, compact is only receiving the value no from the test key. I assumed that the use of compact to merge variables/arrays into the query would recursively merge similar keys from the conditions array into the last specified, but it turns out that yes isn't even making it that far as it's being redefined on the spot.
I suppose this is a limitation of PHP rather than Cake, but it is still something that didn't occur to me and should be done differently somehow (if it hasn't already) in future.
Edit
I ran a couple more tests. I wrapped identical conditions in their own arrays, then compared them the way Cake's find functions would. Using compact (which Cake does) the arrays containing identical keys remain intact, however using array_merge, the first key is overwritten by the second. I guess in this case it's a very, very good thing that Cake uses compact instead of array_merge to merge its query criteria.
$array = array(
array('test' => 'yes'),
array('test' => 'no')
);
$m = array_merge($array[0], $array[1]);
$c = compact('array');
print_r($c);
print_r($m);
Result:
Array
(
[array] => Array
(
[0] => Array
(
[test] => yes
)
[1] => Array
(
[test] => no
)
)
)
Array
(
[test] => no
)
While this is obviously a simple problem in the way you fundamentally write PHP code, it wasn't inherently obvious while writing in Cake syntax that conditions would overwrite each other...
Basic PHP: Don't use the same array key twice
'conditions' => array(
'status LIKE ' => 'Removed',
'status LIKE ' => 'Pending',
'status LIKE ' => 'Completed'
)
should be
'conditions' => array(
'status LIKE' => array('Removed', 'Pending', 'Completed'),
)
Same for any other array key.
Note that some quick debugging of the array reveals this.
Please also see the tons of other stackoverflow questions with the same issue or other areas where basic research could have pointed you in this direction. Taking a look there first can help to resolve the issue in less time.

DyanmoDB: not getting the answer I'm expecting using batch_get_item

I am trying to do a batch_get_item to request multiple items from a table. I am following the PHP example in the DynamoDB documentation, but I am not getting the results I'm expecting.
Following is the code:
$batch_array = array ();
$batch_array[]= array ('HashKeyElement' =>
array( AmazonDynamoDB::TYPE_STRING => 'V1L3M5O5L1W8R5B6D2Q1S8V0B3R8M7A6R0X0'));
$options = array (
'RequestItems' => array(
'profile_dev' => array (
'Keys' => $batch_array
)
)
);
$result = $this->db->batch_get_item($options);
Instead of getting the data, I am getting a very long response, and I'm including the relevant information from the tail end of it:
[x-aws-body] => {"RequestItems":{"profile_dev":{"Keys":[{"HashKeyElement":{"S":"V1L3M5O5L1W8R5B6D2Q1S8V0B3R8M7A6R0X0"}}]}}} ) [body] => CFSimpleXML Object ( [__type] => com.amazon.coral.validate#ValidationException [message] => One or more parameter values were invalid: The provided key size does not match with that of the schema ) [status] => 400 ) )
The hashKey for this table is a string. It has a rangeKey, but I am using the hashKey so I can get all the rows matching the hashKey. What am I missing?
The DynamoDB documentation (and SDK samples) have colossal bugs in them. The documentation, and actual SDK code, make use only of the hashKeyElement, but in fact if a table has both a hashKey AND a rangeKey, both must be used.
When I used both the hashKey and the rangeKey, the call worked.
Get (or batch get) requires you to completely define the key of all items you are getting. If you want to retrieve all rows with the same hashKey using a single call, it seems like you're looking for Query.
You don't need to use BatchGet, you should be using Query. Here is an example using the PHP SDK to get all items with the HASH key 'YourHashKey' on table 'YourTable'
// Instantiate the class
$dynamodb = new AmazonDynamoDB();
$response = $dynamodb->query(array(
'TableName' => 'YourTable',
'HashKeyValue' => array( AmazonDynamoDB::TYPE_STRING => 'YourHashKey' ),
));
Reference: http://docs.amazonwebservices.com/amazondynamodb/latest/developerguide/LowLevelPHPQuerying.html

How to work with join data in codeigniter?

I am having a play around with codeigniter and trying to get my head around the active record system and such like.
I have set up a couple of tables and am attempting to run a join on them, as such:
function GetOrganisationsAndBuildingDetails()
{
$this->db->select('organisations.organisation_name,
organisations.organisation_id,
buildings.building_name,
buildings.address1');
$this->db->from('organisations')->join('buildings', 'buildings.organisation_id = organisations.organisation_id');
$query = $this->db->get();
return $query->result();
}
In my database i have one organisation with two related buildings. The above query returns two objects (one for each building) - however, the organisation is duplicated.
stdClass Object (
[organisation_name] => This is an example org
[organisation_id] => 1
[building_name] => test building
[address1] => 123456 )
stdClass Object (
[organisation_name] => This is an example org
[organisation_id] => 1
[building_name] => teeeest building
[address1] => 123456 )
I suppose I was expecting something along the lines of one return object with a series of nested objects for related buildings. Is this possible?
If not, is their a recommend way of arranging the return data so I can easily loop through it in the view? (foreach org, foreach building etc etc).
Apologies if I'm being a little dense here. Im coming from .net and (linq to SQL in particular) where this stuff is a little different)
The query will inevitably return duplicate data as you say, you have to organize them after you get the result like this
$buildings = array();
foreach ( $result_object as $organization ) {
$building_data = array(
'building_name' => $organization->building_name,
'address' => $organization->address,
);
$buildings[$organization->organization_name][] = $building_data;
}
this way organizations will be "compacted" in the first key of the multidimensional array, and one level deeper you will have info about the buildings. Hope this helps.

Categories