PHP PDOStatement::fetchAll and grouping by key - php

There is a comment on php.org at fetchAll page.
You might find yourself wanting to use FETCH_GROUP and FETCH_ASSOC at
the same time, to get your table's primary key as the array key:
// $stmt is some query like "SELECT rowid, username, comment"
$results = $stmt->fetchAll(PDO::FETCH_GROUP|PDO::FETCH_ASSOC);
// It does work, but not as you might expect:
$results = array(
1234 => array(0 => array('username' => 'abc', 'comment' => '[...]')),
1235 => array(0 => array('username' => 'def', 'comment' => '[...]')), );
but you can at least strip the useless numbered array out easily:
$results = array_map('reset', $results);
The code functions as expected and everything is accomplished with just line:
array_map('reset', $results);
After reading documentation array_map and reset function I don't really understand how the result is produced when both are combined in a single line.
Is it a safe solution? Would you recommend this kind of one-line or is it a side effect and should not be used, i.e. should I write a plain old loop to produce the same result?
Single line solution combining two standard functions is a very attractive solution to me. I just want to make sure there are no surprises.

The trick here is that reset in this very case is equal (suggest we are iterating through $results in array_map) to a $results[$i][0]. reset, besides internal pointer rewind, returns the first element of the passed array, as simple as that.
As a side note I would suggest achieve this behavior with PDO::FETCH_CLASS implementing ArrayAccess Interface.

Yes, it is safe. array_map() replaces an array member with callback function's result and reset() returns first member of array.
So,
array(0 => array('username' => 'abc', 'comment' => '[...]'))
is replaced with
array('username' => 'abc', 'comment' => '[...]')
as reset() returns this very line out of the former one.
But honestly, this is a kind of problem which never existed to me.
I don't understand what is so attractive in the idea of making one-liners using API functions only, producing whatever mind-breaking statements to abide one rule - "no user-defined functions at any cost".
I can make whatever function of my own, and it will be shorter than any of these code twists when called, yet have plain and readable syntax in body, without scratching your head both at writing and reading, without asking yourself a question "is it safe?".
So, a real one-liners would be my safe and convenient database access library's getInd family functions (which allows any field as a key, not primary index only):
$data = $db->getInd("id", "SELECT * FROM table WHERE category=?i", $cat);
$dic = $db->getIndCol("name", "SELECT name, id FROM cities");
you may notice that these are real one-liners, doing all the dirty and repetitive job inside, including query execution, parameter binding and stuff.

Related

PHP key => value array to method arguments (order?)

I think this is quite interesting!!! :).
What I've got?
In the application that I'm using on some level in some objects (doesn't really matter) I get an array, for example:
$array = array(
'argument_label' => 'value_label',
'argument_name' => 'value_name',
'argument_id' => 'value_id'
)
I don't have any impact on how and when this array is created. Next, I've got a method:
public function arrayArgument($array) {
$label = isset($array['argument_label']) ? $array['argument_label'] : 'default_label';
$name = isset($array['argument_name']) ? $array['argument_name'] : 'default_name';
$id = isset($array['argument_id']) ? $array['argument_id'] : 'default_id';
// Do something
return 'something';
}
I really hate it. There is no way of proper documentation for the method arguments (as PHPDocumentator work not so well with arrays), and those issets are driving me crazy. Additionally it is a nightmare for someone who will work with this code in the future, when I will already be a "retired nerd".
What I want?
I want to have a function like that:
public function notArrayArgument(
$id='default_id',
$label='default_label',
$name='default_name'
) {
// Do something
return 'something';
}
What I can do?
When I get array, I can change some code, and make my own method run. So I need some kind of solution to get from here:
$array = array(
'argument_label' => 'value_label',
'argument_name' => 'value_name',
'argument_id' => 'value_id'
)
To here:
notArrayArgument('value_id', 'value_label', 'value_name');
Or here:
notArrayArgument($array['argument_id'], $array['argument_label'], $array['argument_name']);
What are the problems?
This is not template like. The number of variables is always different, the names are always different, and sometimes some of them are passed, sometimes not.
It should work really fast...
Calling the method arguments in the right order. Array can be sorted, not sorted or random sorted, while the arguments inside method are always in the same order. The array should be reordered to match the method arguments order, and after that the method should be called.
What I came with?
I've got an idea using reflectionClass. I can check the names of method arguments, get them in order, reorder the array and try to call this method. But this is quite resource eating solution, as reflectionClass is not so fast I think.
Solution using extract? This would be great. But after extract, I need to use exact variables names in code. And I don't know them as every time they are different, and I need an universal approach.
NEW (thx to comment): call_user_func_array(). It is great, but it only works with indexed arrays, so this will be the last but not least step of the possible solution. As the order of arguments is still unknown...
Does this problem have a nice semantic, pragmatic solution?
I read my question once more, and I hope it is clear to understand. If not, please post a comment and I will do my best to describe the problem better.
Kudos for thinking about the maintainer, but I'd argue simplicity is just as important as nice semantics and pragmatism. Consider this: if you had to ask how to write such a pattern, what are the chances that it will be obvious to the reader? I'd much rather come across code where I can just think "Yep, that's clear" than "Oh cool that's a really intricate and clever way of setting array defaults".
With this in mind, it seems to me that an array is being used in a situation more suited to a class. You have an id, a label and a name, which surely represents an entity - exactly what classes are for! Classes make it easy to set defaults and provide PHPDoc on each of their properties. You could have a constructor that simply takes one of your existing arrays and array_merge()s it with an array of defaults. For the reverse conversion, conveniently, casting an object to an array in PHP results in an associative array of its properties.
Try to use classes as George Brighton mentioned.
If you can't for some legacy or library constraint, you will have to use reflection. Don't worry too much about the performance of reflection classes, a lot of frameworks use them to do the request routing.
You can use a function like:
function arrayArgument($object, $method, $array)
{
$arguments = [];
$reflectionMethod = new ReflectionMethod(get_class($object), $method);
foreach ($reflectionMethod->getParameters() as $parameter)
{
$arguments[] = isset($array[$parameter->name]) ? $array[$parameter->name] : $parameter->getDefaultValue();
}
call_user_func_array(array($object, $method), $arguments);
}
So you can do
$instance = new MyClass();
arrayArgument($instance, 'notArrayArgument', ['name' => 'myname']);

Incorrectly used array_multisort() giving correct result?

I'm currently touching some legacy code in an attempt to clean it and have come across something of a puzzle. It's not even remotely mission critical that I resolve it, so consider this a sporting question at most, but it's still perplexing me. Colour me curious.
This code:
// dummy data for the sake of testing / abstract visualisation;
// x is thrown in as an experiment:
$workTables = array(
array('x' => 5, 'Name' => 'foo3', 'Update_time' => '2013-04-04 04:40',),
array('x' => 4, 'Name' => 'foo4', 'Update_time' => '2013-04-01 04:40',),
array('x' => 3, 'Name' => 'foo2', 'Update_time' => '2013-04-04 09:40',),
array('x' => 2, 'Name' => 'foo1', 'Update_time' => '2013-04-12 04:40',),
array('x' => 1, 'Name' => 'foo5', 'Update_time' => '2012-12-04 04:40',),
);
// original legacy code:
if (!empty($workTables)) {
$sort = array();
foreach ($workTables as $key => $value) {
$sort[$key]["Update_time"] = $value["Update_time"];
}
array_multisort($workTables, SORT_ASC, SORT_STRING, $sort);
}
...while throwing twelve notices ("Notice: Array to string conversion ..."), works as expected, by ordering all elements in $workTables according to Update_time.
(The 'x' column is part of my attempt to rule out that it has anything to do with Name, which in the original code is a fixed prefix followed by a timestamp (e.g. work_table_1367940392, work_table_1367940395, ...), making sorting by Name equivalent to sorting by Update_time.)
Obviously, since I don't want to program by coincidence, at the very least this is going to be replaced with:
// new code:
if (!empty($workTables)) {
$sort = array();
foreach ($workTables as $key => $value) {
$sort[$key] = $value["Update_time"];
}
array_multisort($sort, SORT_ASC, SORT_STRING, $workTables);
}
...which conforms to the description of array_multisort(), also does what we want, and doesn't throw Notices in our face.
But what I'm really interested in is why the old code works (notices notwithstanding).
A partial reason seems to be the behaviour of asort() and co., which have the (partly) undocumented feature that they can work with multi-dimensional arrays by acting according to the contents of the array, working through the structure from "left to right" (at least in PHP 5.4.7 (cli))... but... I'm stuck trying to comprehend what's making $workTables and $sort 'interchangable'.
I've tried to look at PHP's C source code to figure it out, but I'm stuck at trying to understand what happens here:
/* Do the actual sort magic - bada-bim, bada-boom. */
zend_qsort(indirect, array_size, sizeof(Bucket **), php_multisort_compare TSRMLS_CC);
...since my knowledge of C has rusted terribly and zend_qsort() itself is just flatly out of my league.
Any takers?
Keep in mind I'm not desperate for an answer, so don't sink too much time into this, but maybe someone else likes a puzzle, too? :)
Personally, I've invested some time into this purely because I prefer understanding code thoroughly, especially when I'm trying to clean it up - even if it's code that works only by coincidence. I've just reached a dead-end when it comes to further comprehension, so stackoverflow seemed like the best chance for further enlightenment.
So, if you have an idea about what's going on behind the scenes (I suspect it's something trivial I've overlooked; that tends to be my problem after going around in circles for a while), I'd love to hear it! :)
From what I understand, an array is being created and stored in $sort[$key]. Then, once you call array_multisort(), it happens to convert the array back into a string and sort using that string as the value that defines the order -- just as the warning says. Then, this string is used as the order-defining value.
How is this conversion array->string performed? Just doing echo $anArrayValueHere; is not helpful; it prints out Array. Similar trick, echo "" . $anArrayValueHere . ""; also doesn't yield anything useful. (But neither does it yield a notice!)
It's quite possible that serialize() is used. Presuming that, let's see what would get used as a order-defining value:
#!/usr/bin/php5
<?php
$sort[0]["Update_time"] = '2012-12-04 04:40';
echo serialize($sort[0]);
then
$ ./php tmp.php
a:1:{s:11:"Update_time";s:16:"2012-12-04 04:40";}
It looks like the key would be sufficiently definitive to yield something usable to define sort order just as well as just the Update_time value would.
I'm not sure that serialize() is used, but if it is, I think this is the explanation.

PHP array argument

Is there a way to make the arguments of a function act as an array? I'm finding this difficult to explain.
Here's kind of an example.. When you declare an array, you can define the keys => values like so:
$array = array(
"key" => "value",
"other_key" => "other_value"
);
And if I make a function that for an example outputs these onto the document, I could have:
function write($ar)
{
foreach($ar as $key => $value)
echo "$key: $value<br />";
}
write($array); // parse previously mentioned array
What I want to be able to do is omit the need to parse an array like above or below examples..
write(array(
"key" => "value",
"other_key" => "other_value"
));
I know I can use func_get_args() to list any amount of arguments, but is there a similar function that lets you parse key => value pairs rather than just a list of values?
Hope I described this in a way that makes sense, what I essentially want to end up with is something like:
write(
"key" => "value",
"other_key" => "other_value"
);
A function cannot take the argument as an array structure, so your best bet would be to use the method you specified in your second to last example:
write(array(
"key" => "value",
"other_key" => "other_value"
));
You could then have a default array within your function (if desired) so you could merge the two together so you always have a decent set of data.
EDIT
Unless you want to go crazy and pass it through as a string:
write('
"key" => "value",
"other_key" => "other_value"
');
And then parse that out on the other side... but IMO I wouldn't bother, potentially opening yourself up to issues here.
I know I can use func_get_args() to list any amount of arguments, but is there a similar function that lets you parse key => value pairs rather than just a list of values?
No, that's not possible in PHP. However you can fork PHP and implement such a syntax or make a code preprocessor ;)
well, I assume that you can send an object instead of an array and declare like that.
//the class
class CustomType{
public $key1;
public $key2;
}
//the function
function write(CustomType $customType){
}
//call the funcion write
$a = new CustomType();
$a->key1 = 1;
$a->key2 = 2;
write($a);
the above will force the user to send a custom object.
it is better than arrays, but then again, the user can ignore and not set $key2.
BTW, if you choose this method, you might as well do it in JAVA style with getters and setters. like here: http://www.abbeyworkshop.com/howto/java/salesTaxBean/index.html
that is as good as it gets since php is typeless, but i think it is much better then arrays
Edit
One of the bad usage in PHP is using arrays as params.
Almost every framework does that and you can find your self diving into sources in order to understand the array structure.
When you declare a class, you get it as autocomplete in your editor, and if not you just need to open the class to understand its structure.
I believe this is better programming style.

Declaring static arrays in php

I am new to php. I was wondering how I could declare a static array in php. Here is what I would do in C. How is the corresponding php code for it?
char a[][] = { (1,1), (1,2), (1,3), (2,1), (2,2), (2,3), (3,1), (3,2), (3,3) };
From what I read it has to be something like this -
$a = array( 1 => array(1,1), 2 => array(1,2), ... );
Is this correct? If so it sucks :) I hope I am wrong.
Thanks,
- Pav
You've already found the way to do it natively.
Another option would be to declare your data as JSON (a very concise and human-friendly format). This could be either in a separate file bundled with your app, or directly in your code in a string. Then parse the JSON at runtime. Since PHP isn't exactly known for speed, this may or may not make your noticeably app slower to start.
you have it already figured out in your question.
One thing I would add is that you do not need to explicitly define the keys if you are intending to use a zero based array, this is assumed and can be done like so...
$a = array(array(1,1),array(1,2), ... );
You can also use what is called associative arrays which use string keys and you define them the same way you do in your example, except use strings instead of numbers...
$ass_array = array( 'array_1' => array(1,1), 'array_2' => array(1,2), ... );
you would then call your associative array like this...
$ass_array['array_1'];
Also, if you want to append single items to an array (for example in a loop to load an array)...
$ass_array[] = $item;
Further to jondavidjohn's anwser, you could just write a quick script to grab your list of values and generate the array statement for you.
No need to care how verbose the syntax is then. If the task is long and repetitious enough to care, don't do it by hand. :)

How do I stop MySQL from duplicating every column's entry in returned arrays?

My MySQL queries are returning arrays with duplicate entries: numbered keys and labeled keys with the same data inside. This may be standard, but it seems like a waste, and something that could cause problems if I'm printing values. I mean, not a huge problem, obviously. But I'm just curious if I can stop that. It seems unnecessary. For example:
Array(
[0] => "Ted",
[first_name] => "Ted",
[1] => "Schmidlap",
[last_name] => "Schmidlap"
)
And so on.
I'm pretty new to a lot of this, so this may be a simple question, but Googling doesn't seem to have any answers for me. Anyone know the reason this happens? I'm using PHP's PDO now, but I was doing it straight through the MySQL functions before and the same thing was happening, so I assume it's a byproduct of MySQL interaction.
I can iterate through and unset the numeric ones, because I don't need them, but they're not really in the way right now, so that's just an extra step. Still, is there a way to simply not have them fetched in the first place?
Presumably this is happening after you use mysql_fetch_array (or similar).
You need to add in a flag to specify what array type you want returned or PHP assumes both.
i.e. mysql_fetch_array($result_set, MYSQL_ASSOC|MYSQL_NUM|MYSQL_BOTH)
That depends on the function you are using.
Some functions return both types, others return only one of them.
If you are using PDOStatement->fetch, notice the optional $fetch_style argument it takes.
Your issue is with the mysql_fetch_array function.
If you want only the numbers, use:
$row = mysql_fetch_array($result, MYSQL_NUM)
If you want only the text indexes, use:
$row = mysql_fetch_array($result, MYSQL_ASSOC)
I usually use MySQLi but for PDO you can find more information here: http://us3.php.net/manual/en/pdostatement.fetch.php
Which seems to mean that you should be using this for text indexes:
$row = $statement->fetch(PDO::FETCH_ASSOC);
And this for numeric indexes:
$row = $statement->fetch(PDO::FETCH_NUM);
Also, just to note this since it isn't listed here, if you want an associative array, you can use mysql_fetch_assoc(), or if you want an enumerated (numbered) array use mysql_fetch_row() instead of mysql_fetch_array(). I use this mostly because without it I would often forget the flag, so I got into the habit of just using the specific function.

Categories