I like the convenience methods for data manipulation queries $conn->insert() and $conn->update() in doctrine 2 DBAL because insert/update values can be held an passed as associative array. But how can i pass a NULL value, a MySQL function or other expressions as value?
E.g:
/* $conn is a \Doctrine\DBAL\Connection object */
$conn->update('person', array('phone' => 'NULL'), array('id' => 1));
$conn->update('person', array('lastlogin' => 'NOW()'), array('id' => 1));
$conn->update('person', array('visit' => 'visit + 1'), array('id' => 1));
These function calls would create prepared statements like
UPDATE person SET phone = ? WHERE id = ?
and thus the values would be treated as strings.
Is there a way to make this work using this technique?
There is an optional $types argument, which defaults to an empty array:
public function update($tableExpression, array $data, array $identifier, array $types = array())
The $types array can contain PDO type constants which will change how the corresponding $data values are treated.
So I'd try:
$conn->update(
'person', // $tableExpression
array('phone' => null), // $data
array('id' => 1), // $identifier
array('phone' => \PDO::PARAM_NULL) // $types
);
Related
Is there any solution in PHP to make an array destructuring assignment with associative keys can extract optional values/default values (like the example below)?
Because when I tried, I always get undefined index 'age' (it's because it is not set).
See example:
// implementation
function myFunction(array $params = ['name' => 'user-name', 'age' => 15]){
['name' => $name, 'age' => $age ] = $params;
echo $name.' '.$age;
}
// Execution:
// normal case
myFunction(['name' => 'user', 'age' => 100]);
// Output: user 100
// Wanted case to be released
myFunction(['name' => 'user']);
// Output: user 15 <== i want to get this default age value if i don't set it in the given associative array params.
What about defining an array with default values?
function myFunc(array $params, array $defaults = [ 'name' => 'Marcel', 'age' => 40 ]): string
{
$data = array_merge($defaults, $params);
[
'name' => $name,
'age' => $age
] = $data;
return $name . $age;
}
The array merge overwrites the default values with the given param values. So the return will always be a complete mixture of both values. If you send a name only, the name will be mixed up with the default age. If you give an age only, the age will be mixed up with the default value.
myFunc([ 'name' => 'sohaieb', 'age' => 15 ]); // => sohaieb15
myFunc([ 'name' => 'yadda' ]); // => yadda40
The second parameter $defaults can be changed, if the defaults change in the future. A possible scenario: You set the default values in a config. With this solution the function can take the default settings from the config and don 't has to be touched in the future.
Well, since you pass in a parameter, it will always take that instead of the default one. However, you can use the union operator + to merge it with default values if not present in the originally supplied value during function call.
<?php
function myFunction(array $params = []){
$params = $params + ['name' => 'user-name', 'age' => 15];
['name' => $name, 'age' => $age ] = $params;
echo $name.' '.$age;
}
myFunction(['name' => 'user']);
Because $params is an array, so it overrides the default values even if you pass in an empty array.
One way to do this is to check the value inside your function body.
function myFunction(array $params){
$name = $params['name'] ?? 'user-name';
$age = $params['age'] ?? 15;
echo $name.' '.$age;
}
// Execution:
// normal case
myFunction(['name' => 'user', 'age' => 100]);
// Output: user 100
// Wanted case to be released
myFunction(['name' => 'user']);
Reference
https://wiki.php.net/rfc/isset_ternary
I have a query that populates an array from the database. In some cases, this query returns a great amount of data, (let's say for purpose of an example, 100.000 records). Each row of the database has at least 6 or 7 columns.
$results = [
['id' => 1, 'name' => 'name', 'status' => true, 'date' => '10-01-2012'],
['id' => 2, 'name' => 'name 2', 'status' => false 'date' => '10-01-2013'],
...
]
I need to perform a substitution of some of the data inside the $results array, based on another one that give me some information about how i would change the values in the rows.
$model = [
'status' => ['function' => 'formatStatus', params => ['status']],
'date' => ['function' => 'formatDate', params => ['date']]
]
Now that i have all the data and what do i do with it i have the following routine.
foreach ($results as &$itemResult) {
$oldValues = $itemResult;
foreach ($itemResult as $attribute => &$value) {
if (isset($model[$attribute]['function'])) {
$function = $model[$attribute]['function'];
$params = $model[$attribute]['params'];
$paramsSize = count($params);
for ($i = 0; $i < $paramsSize; $i++) {
$newParams[] = $oldValues[$params[$i]];
}
$itemResult[$attribute] = call_user_func_array([$this, $function], $newParams);
$newParams = null;
}
}
}
So, for each attribute for each row of my data array, i run check for the existence of a function and params information. When the attribute in question needs to be replaced, i call the function via call_user_func_array and replace the value with the function return value.
Also notice that i am replacing the current array, not creating another, by passing the reference &$itemResult inside the loop, so in the end, i have the same array from the beginning but with all columns that needed to be replaced with its new values.
The thing is, for little arrays, this method is quite good. But for big ones, it becomes a pain.
Could you guys provide me some alternative to the problem?
Should i use another data structure instead of the PHP array?
This question already has answers here:
PHP function with variable as default value for a parameter
(7 answers)
Closed 1 year ago.
I have a PHP function with a array within. I put the array inside so the parameters would be option and these would be the defaults. Example
/**
* Creates New API Key
*
* #return Response
*/
public function create(
$data = [
"user-id" => Auth::id(),
"level" => '1',
"ignore-limits" => '0',
]){
...
}
However I keep getting the error
syntax error, unexpected '(', expecting ']'
So I assume that you cant pass a array like this when constructing a function. What would be a better way to do this or a fix?
You can only use scalar types for the default values of function arguments.
You can also read this in the manual: http://php.net/manual/en/functions.arguments.php#functions.arguments.default
And a quote from there:
The default value must be a constant expression, not (for example) a variable, a class member or a function call.
EDIT:
But if you still need this value as default value in the array you could do something like this:
Just use a placeholder which you can replace with str_replace() if the default array is used. This also has the advantage if you need the return value of the function in the default array multiple times you just need to use the same placeholder and both are going to be replaced.
public function create(
$data = [
"user-id" => "::PLACEHOLDER1::",
//^^^^^^^^^^^^^^^^ See here just use a placeholder
"level" => '1',
"ignore-limits" => '0',
]){
$data = str_replace("::PLACEHOLDER1::", Auth::id(), $data);
//^^^^^^^^^^^ If you didn't passed an argument and the default array with the placeholder is used it get's replaced
//$data = str_replace("::PLACEHOLDER2::", Auth::id(), $data); <- AS many placeholder as you need; Just make sure they are unique
//...
}
Another idea you could do is set a default array which you can check and then assign the real array like this:
public function create($data = []){
if(count($data) == 0) {
$data = [
"user-id" => Auth::id(),
"level" => '1',
"ignore-limits" => '0',
];
}
//...
}
The issue here is the:
Auth::id()
This calls a method which is illegal to do in this context
I would solve it like this:
public function create(
$data = [
"user-id" => -1,
"level" => '1',
"ignore-limits" => '0',
]){
if($data['user-id'] === -1) {
$data['user-id'] = Auth::id()
}
...
}
More universal solution with array_mearge. This way you can rewrite any parameter without having to check each of them individually.
function create($somthing, $settings = [])
{
$default = [
'date' => date("Y-m-d H:i:s"),
'bold' => false,
'italic' => false,
];
$settings = array_merge($default, $settings);
...
}
I've hit this a few times and never found the best way to tackle it. It's easiest to illustrate with a concrete example. Sample data:
product_id display_name display_order
---------- ------------ -------------
"samgal3" "Samsung Galaxy 3" 0
"motorazrh" "Motorola Razr HD" 1
"iphone5" "Apple iphone 5" 2
etc
The actual arrays are often small (<20 items), though not always, and have guaranteed unique keys/values. Each item has a unique sort key (order it's listed in, for html table/enumeration), a unique internal key (for item lookup), and a human-readable display name.
Typically I hit this issue when a list of options is used on a form. The same data is used both to populate dropdown boxes on forms, and also to validate the submitted $GET/POST data. When generating the form, it needs to be enumerated/listed in 'sort' order to create the SELECT box options in order. When a form is submitted it needs to be searchable by 'product_id' (to validate "...&action=view&product_id=elephant..." is a product in the list).
If I use 'sort'=>array(other data) as the key, then displaying by 'sort' is easy but searching within $data[*]['product_id'] is hard *(ie identify $KEY if it exists, having $data[$KEY]['product_id'] == 'htcvox')*. If I use 'product_id'=>array(other data) as the key then searching whether 'samgal3' is in the array and finding its data is easy but there's no simple way to walk/enumerate the array by 'sort' to create the form.
I guess I can do a custom search/sort where the search/sort key for any member $i in $data is $i['product_id'] or $i['sort'] but it's clumsy and I've never done it before. Simplicity counts as the code will be open source.
I'm expecting to code the data as an array of arrays, like this:
$data = array(
0 => array('product_id'=>'samgal3', 'display_name' => 'Samsung Galaxy 3'),
1 => array('product_id'=>'motorazrh', 'display_name' => 'Motorola Razr HD'),
...
or
$data = array(
'samgal3' => array('sort'=>0, 'display_name' => 'Samsung Galaxy 3'),
'motorazrh' => array('sort'=>1, 'display_name' => 'Motorola Razr HD'),
...
Put the same problem differently, given an 2D array of arrays: $data = array(array1, array2, array3, ....); where all of array1, array2, array3, ... contain a key/field with a fixed name, is there an easy way to search/sort the nested arrays on $ARRAY[**]['named_field']?
What you want is essentially multiple indexes into a single array, just like you would have multiple indexes on a single table in a relational database. (Sorting is a separate but related concern.)
Let's start with this basic data structure, a simple set of arrays with no particular importance attached to the keys:
$data = array(
array('display_order'=> 0, 'product_id'=>'samgal3', 'display_name' => 'Samsung Galaxy 3'),
array('display_order'=> 1, 'product_id'=>'motorazrh', 'display_name' => 'Motorola Razr HD'),
array('display_order'=> 2, 'product_id'=>'a', 'display_name' => 'a'),
array('display_order'=> 3, 'product_id'=>'c', 'display_name' => 'c'),
array('display_order'=> 4, 'product_id'=>'d', 'display_name' => 'd'),
array('display_order'=> 5, 'product_id'=>'b', 'display_name' => 'b'),
array('display_order'=> 6, 'product_id'=>'q', 'display_name' => 'q'),
array('display_order'=> 7, 'product_id'=>'f', 'display_name' => 'f'),
);
You can create an explicit index easily and then use it to get $data items:
$product_id_idx = array_flip(array_map(function($item){return $item['product_id'];}, $data));
$samgal3_array = $data[$product_id_idx['samgal3']]; // same as $data[0]
For sorting you can use the oft-forgotten array_multisort. Look at example 3 in the documentation. The trick is to create arrays to sort on and include the full data set as the last argument. For example:
array_multisort(array_keys($product_id_idx), SORT_ASC, SORT_STRING, $data);
$data is now sorted by product key. The original numeric array keys of $data are lost, however, which means our $product_id_idx is not usable anymore. Thus it's best to sort a copy of the data array if you want to keep using your indexes.
We can combine both these approaches into a single class to preserve our sanity:
class MultiIndex {
protected $array;
protected $indexes = array();
protected $indexdefs = array();
function __construct($array, $indexdefs)
{
$this->array = $array;
$this->indexdefs = $indexdefs;
foreach ($indexdefs as $name => $column) {
$this->indexes[$name] = $this->makeIndex($column);
}
}
function makeIndex($column)
{
$index = array();
foreach ($this->array as $k => $v) {
$index[$v[$column]] = $k;
}
return $index;
}
function get($key, $index=null)
{
$datapk = ($index===null) ? $key : $this->indexes[$index][$key];
return $this->array[$datapk];
}
function getIndex($index)
{
return $this->indexes[$index];
}
function getData()
{
return $this->array;
}
function indexedBy($index)
{
$indexed = array();
$indexedcolumn = $this->indexdef[$index];
foreach ($this->indexes[$index] as $indexk => $arrayk) {
$newarray = $this->array[$arrayk];
unset($newarray[$indexedcolumn]);
$indexed[$indexk] = $newarray;
}
return $indexed;
}
function sortedBy(/*multisort args*/)
/* with strings converted to arrays corresponding to index of same name */
{
$args = func_get_args();
foreach ($args as $n => $arg) {
if (is_string($arg)) {
$args[$n] = array_keys($this->indexes[$arg]);
}
}
$sorted = $this->array;
$args[] = $sorted;
call_user_func_array('array_multisort', $args);
return $sorted;
}
}
Example of use:
$dataidx = new MultiIndex($data, array('id'=>'product_id', 'disp'=>'display_order'));
var_export($dataidx->sortedBy('disp', SORT_STRING, SORT_ASC));
var_export($dataidx->indexedBy('id'));
var_export($dataidx->get('samgal3', 'id'));
This should be a very basic base to build upon, and should be fine for small arrays. For simplicity's sake MultiIndex's data is immutable and keys are always array indexes. Some obvious ways to enhance this are:
Make the $indexdefs accept a callable which returns a key for an item, instead of just a string/int naming an array key. This allows you to create indexes on any shape of data and even to create indexes which don't correspond to the data directly. (E.g., index by number of characters in the display name, or by the date+time of an array that keeps date and time separate, etc.)
Remove the requirement that an index key have only a single value. (Right now all indexes you make are assumed to be unique.)
Allow you to declare a SORT_* datatype for an index and have it automatically included in the arguments to array_multisort in MultiIndex::sortedBy().
Include sparse indexes: if you have very common or very rare values, or just have a very large dataset and you want to save memory, you can make it so only certain values are indexed. If an item isn't found in an index, fall back to a full scan of unindexed items in the data.
Add sensible interface implementations.
Allow the MultiIndex to have multiple backends, so you can have multiple indexes on any array-like structure (e.g. a dbm key-value store, a cloud store like DynamoDB, memcached, etc), and manipulate them all with the same object interface.
Make MultiIndex hold mutable data and update indexes incrementally and automatically as the data changes.
I'll keep this code on a gist for easy forking.
use http://www.php.net/usort to generate a custom user defined sort.
Example:
<?php
//added a few more values
$data = array(
0 => array('product_id'=>'samgal3', 'display_name' => 'Samsung Galaxy 3'),
1 => array('product_id'=>'motorazrh', 'display_name' => 'Motorola Razr HD'),
2 => array('product_id'=>'a', 'display_name' => 'a'),
3 => array('product_id'=>'c', 'display_name' => 'c'),
4 => array('product_id'=>'d', 'display_name' => 'd'),
5 => array('product_id'=>'b', 'display_name' => 'b'),
6 => array('product_id'=>'q', 'display_name' => 'q'),
7 => array('product_id'=>'f', 'display_name' => 'f'),
);
function cmp($a,$b){
return strcasecmp($a['display_name'],$b['display_name']);
}
usort($data,'cmp');
var_export($data);
http://codepad.viper-7.com/3mY8nU
What do
$categories[$id] = array('name' => $name, 'children' => array());
and
$categories[$parentId]['children'][] = array('id' => $id, 'name' => $name);
mean?
Thanks a lot.
How should i format the output so i can learn the results that was returned?
You can format your code into tables by looping on the array using for or foreach. Read the docs for each if you don't have a grasp on looping.
2.What does
$categories[$id] = array('name' => $name, 'children' => array());
and
$categories[$parentId]['children'][] = array('id' => $id, 'name' => $name);
The first line assigns an associative array to another element of the $categories array. For instance if you wanted the name of the category with ID of 6 it would look like this:
$categories[6]['name']
The second line does something similar, except when you are working with an array in PHP, you can use the [] operator to automatically add another element to the array with the next available index.
What is the uses of .= ?
This is the concatenation assignment operator. The following two statements are equal:
$string1 .= $string2
$string1 = $string1 . $string2
These all have to do with nesting arrays.
first example:
$categories[$id] = array('name' => $name, 'children' => array());
$categories is an array, and you are setting the key id to contain another array, which contains name and another array. you could accomplish something similar with this:
$categories = array(
$id => array(
'name' => $name,
'children' => array()
)
)
The second one is setting the children array from the first example. when you have arrays inside of arrays, you can use multiple indexes. It is then setting an ID and Name in that array. here is a another way to look at example #2:
$categories = array(
$parentID => array(
'children' => array(
'id' = $id,
'name' => $name
)
)
)
note: my two ways of rewriting are functionally identical to what you posted, I'm just hoping this makes it easier to visualize what's going on.