What would be the most readable way to build a URL query string from a { 'param': 'value' } map in XSLT/XPath 3.0?
The following function will work:
declare function local:build-uri($base-uri as xs:string, $params as map(xs:string, xs:string)) as xs:string {
if (map:size($params) ne 0) then
let $param-string := string-join(
map:keys($params)[. ne ""] ! (encode-for-uri(.) || "=" || encode-for-uri($params?(.))),
"&"
)
return $base-uri || "?" || $param-string
else
$base-uri
};
For example:
declare namespace map = "http://www.w3.org/2005/xpath-functions/map";
declare variable $params := map {
"one": "1",
"two": "2",
"three": "3",
"four": "4"
};
local:build-uri("http://www.example.com", map{}),
local:build-uri("http://www.example.com", $params),
local:build-uri("", $params),
()
returns:
http://www.example.com
http://www.example.com?four=4&one=1&two=2&three=3
?four=4&one=1&two=2&three=3
Edit: To support multi-value parameters (while keeping the function body compatible with XPath), something like this should work:
declare function local:build-uri(
$base-uri as xs:string,
$params as map(xs:string, xs:string*),
$use-array-for-multivalue-params as xs:boolean (: param[]=value for PHP, etc. :)
) as xs:string {
if (map:size($params) ne 0) then
let $param-strings :=
for $param in map:keys($params)[. ne '']
return $params?($param) ! string-join((
encode-for-uri($param),
if ($use-array-for-multivalue-params and count($params?($param)) gt 1) then "[]" else "",
"=",
encode-for-uri(.)
), "")
return $base-uri || "?" || string-join($param-strings, "&")
else
$base-uri
};
Not short, nor necessarily easy to understand.
BUT
it handles null values (with csv you get key= the other to omit the key entirely)
it handles xs:anyAtomicType (xs:dateTime, xs:decimal, xs:boolean, ...)
adds a third, common way to serialize the query string parameters with multiple values separating the with a comma
xquery version "3.1";
declare namespace qs="http://line-o.de/ns/qs";
(:~
: Append nothing to names of parameters with multiple values
: ?single=v1&multi=v2&multi=v3
:)
declare function qs:serialize-query-string($parameters as map(xs:string, xs:anyAtomicType*)) as xs:string? {
qs:serialize(
$parameters,
qs:serialize-parameter(?, ?, ()))
};
(:~
: Append [] to names of parameters with multiple values
: ?single=v1&multi[]=v2&multi[]=v3
:)
declare function qs:serialize-query-string-array($parameters as map(xs:string, xs:anyAtomicType*)) as xs:string? {
qs:serialize(
$parameters,
qs:serialize-parameter(?, ?, '[]'))
};
(:~
: Commma separated values for parameters with multiple values
: ?single=v1&multi=v2,v3
:)
declare function qs:serialize-query-string-csv($parameters as map(xs:string, xs:anyAtomicType*)) as xs:string? {
qs:serialize(
$parameters,
qs:serialize-parameter-csv#2)
};
declare function qs:serialize(
$parameters as map(xs:string, xs:anyAtomicType*),
$serializer as function(xs:string, xs:anyAtomicType*) as xs:string*
) as xs:string? {
if (map:size($parameters) eq 0)
then ()
else
$parameters
=> map:for-each($serializer)
=> string-join('&')
=> qs:prepend-questionmark()
};
declare function qs:serialize-parameter (
$raw-parameter-name as xs:string,
$values as xs:anyAtomicType*,
$appendix as xs:string?
) as xs:string* {
let $parameter-name := concat(
encode-for-uri($raw-parameter-name),
if (exists($values) and count($values)) then $appendix else ()
)
return
for-each($values,
qs:serialize-parameter-value($parameter-name, ?))
};
declare function qs:serialize-parameter-csv ($raw-parameter-name as xs:string, $values as xs:anyAtomicType*) as xs:string* {
concat(
encode-for-uri($raw-parameter-name),
'=',
$values
=> for-each(function ($value) { encode-for-uri(xs:string($value)) })
=> string-join(',')
)
};
declare function qs:serialize-parameter-value (
$parameter as xs:string, $value as xs:anyAtomicType
) as xs:string {
``[`{$parameter}`=`{encode-for-uri($value)}`]``
};
declare function qs:prepend-questionmark ($query-string as xs:string) {
concat('?', $query-string)
};
qs:serialize-query-string(map{}),
qs:serialize-query-string-array(map{}),
qs:serialize-query-string-csv(map{}),
qs:serialize-query-string(map{ "a": ("b0","b1"), "b": "$=##'" }),
qs:serialize-query-string-array(map{ "a": (xs:date("1970-01-01"),"b1"), "b": "$=##'" }),
qs:serialize-query-string-csv(map{ "a": ("b0",3.14), "b": "$=##'" }),
qs:serialize-query-string(map{ "a": ("b0","b1"), "c": () }),
qs:serialize-query-string-array(map{ "a": ("b0","b1"), "c": () }),
qs:serialize-query-string-csv(map{ "a": ("b0","b1"), "c": () })
Here is a gist with the above split into a module and tests:
https://gist.github.com/line-o/e492401494a4e003bb01b7a2f884b027
EDIT: less code duplication
let
$encode-parameters-for-uri:= function($parameters as map(*)) as xs:string? {
let
(: serialize each map entry :)
$encoded-parameters:= map:for-each(
$parameters,
function ($key, $values) {
(: serialize the sequence of values for this key :)
for $value in $values return
encode-for-uri($key) || '=' || encode-for-uri($value)
}
),
(: join the URI parameters with ampersands :)
$parameters-string:= string-join(
$encoded-parameters,
codepoints-to-string(38)
)
return
(: prepend '?' if parameters exist :)
if ($parameters-string) then
'?' || $parameters-string
else
()
}
return
$encode-parameters-for-uri(
map{
'size': 'large',
'flavour': ('chocolate', 'strawberry')
}
)
result: ?flavour=chocolate&flavour=strawberry&size=large
A more concise version, also differing in that it converts an empty map into a zero-length string rather than an empty sequence of strings:
let
$encode-parameters-for-uri:= function($parameters as map(*)) as xs:string {
if (map:size($parameters)) then
'?' || string-join(
map:for-each(
$parameters,
function ($key, $values) {
for $value in $values return
encode-for-uri($key) || '=' || encode-for-uri($value)
}
),
codepoints-to-string(38)
)
else
''
}
return
$encode-parameters-for-uri(
map{
'foo': ('bar', 'baz'), 'direction': 'north'
}
)
result ?direction=north&foo=bar&foo=baz
Related
So i have this sorting of multi-dimensional array:
function score($somearray) {
$scores = 0;
if (($somearray[4] == '') AND (($somearray[5] == '') OR ($somearray[5] == '00:00:00')) AND (($somearray[6] == '') OR ($somearray[6] == '00:00:00'))) { // days are not set and time is not set
$scores = 1;
}
else if (($somearray[4] != '') AND (($somearray[5] == '') OR ($somearray[5] == '00:00:00')) AND (($somearray[6] == '') OR ($somearray[6] == '00:00:00'))) { // days are set and time is not set
$scores = 2;
}
else if (($somearray[4] == '') AND ((($somearray[5] != '') AND ($somearray[5] != '00:00:00')) OR (($somearray[6] != '') AND ($somearray[6] != '00:00:00')))) { // days are not set and time is set
$scores = 3;
}
else if (($somearray[4] != '') AND ((($somearray[5] != '') AND ($somearray[5] != '00:00:00')) OR (($somearray[6] != '') AND ($somearray[6] != '00:00:00')))) { // days are set and time is set
$scores = 4;
}
return $scores;
}
function cmp2(array $a, array $b)
{
return (score($b) - score($a));
}
$recordsarr = array();
$recordsarr[0] = ['1','5','2022-04-05','2022-04-15', '', '10:30:00', '11:30:00', '1', '800'];
$recordsarr[1] = ['1','5','2022-04-01','2022-04-20', '5', '10:30:00', '11:30:00', '1', '700'];
$recordsarr[2] = ['1','5','2022-04-05','2022-04-15', '4', '', '', '1', '800'];
$recordsarr[3] = ['1','0','2022-04-07','2022-04-15', '', '', '', '1', '800'];
$recordsarr[4] = ['1','5','2022-04-06','2022-04-16', '', '', '', '0', '900'];
$recordsarr[5] = ['1','5','2022-04-07','2022-04-12', '2', '', '', '0', '600'];
$majorpriorityarray = array();
if (count($recordsarr) > 0) {
usort($recordsarr, 'cmp2');
$majorpriorityarray = $recordsarr[0];
}
echo "major priority array is<br>";
var_dump($majorpriorityarray);
It works fine in php5.6, but does not work in php7, showing this error:
usort() expects parameter 2 to be a valid callback, function 'cmp2'
not found or invalid function name
What i have to change in my code for working this sorting stuff in php7? Thanks
UPD: Just tried:
usort($recordsarr, function($a, $b) {
cmp2($a, $b);
});
Also tried:
function cmp2(array $a, array $b)
{
return score($b) <=> score($a);
}
Both these variants does not show an error, but are not sorting - do not affect the initial order.
Try to use
usort($recordsarr, array( $this, "cmp2" ));
I'm checking JSON input data with codeigniters native FormValidation library.
I've set up the validation rules like this:
$this->form_validation->set_rules(
'active', 'myCheckbox',
'required|is_bool'
);
here is the JSON data
{
"data": {
"type": "items",
"attributes": {
"category": "5",
"description" : "my description",
"active": false
}
}
}
Problem: The rule "required" interprets the input as not present as soon as the JSON value of active is false, so that the validation only succeed if value of active is true
Question: Is there a way around this, besides changing value of active into a numeric value 1or 0 and therefore change lots of other correspondending code?
EDIT: trying isset instead of required behaves different, but also not satisfying
$this->form_validation->set_rules(
'active', 'myCheckbox',
'isset|is_bool'
);
EDIT No.2: trying in_list[false,true] instead of is_bool behaves right, but the JSON does not look right anymore, because the boolean value must be sent as a string
$this->form_validation->set_rules(
'active', 'myCheckbox',
'required|in_list[false,true]'
);
here is the JSON data
{
"data": {
"type": "items",
"attributes": {
"category": "5",
"description" : "my description",
"active": "false"
}
}
}
I had the same probleam.
I fixed it creating my own "required" and my own "is_bool".
I created a "is_required_not_null" and "is_boolean" functions and put then in my helper, so I can use it where I need.
Note that I used Identical operator(===) to make sure of variables types and values.
See below...
Helper File:
/yourApplication/helpers/yourHelperFileName_helper.php
function is_boolean($value)
{
try {
if ($value === true || $value === false)
return true;
else
return false;
} catch (\Throwable $th) {
return false;
}
}
function is_required_not_null($value)
{
try {
if ($value === null)
return false;
if ($value === true || $value === false)
return true;
if (is_array($value) && count($value) == 0) {
return false;
} else {
if (trim($value) == "")
return false;
}
return true;
} catch (\Throwable $th) {
return false;
}
}
Usage sample:
$data = $this->input->post(); //yours inputs ...
$rules = [
[
'field' => 'fieldName',
'rules' => 'is_required_not_null|is_boolean',
'label' => 'Field Name',
'errors' => [
'is_required_not_null' => 'Field %s is required.',
'is_boolean' => 'Field %s must be boolean.',
],
],
]; //yours rules ...
$this->load->library('form_validation');
$this->form_validation->set_data($data);
$this->form_validation->set_rules($rules);
if ($this->form_validation->run()) {
//Valid Code ...
}
else{
//Not Valid Code ...
}
Source docs:
https://codeigniter.com/userguide3/libraries/form_validation.html#validating-an-array-other-than-post
https://codeigniter.com/userguide3/libraries/form_validation.html#setting-rules-using-an-array
https://codeigniter.com/userguide3/libraries/form_validation.html#callable-use-anything-as-a-rule
https://codeigniter.com/userguide3/libraries/form_validation.html#setting-validation-rules
public function index()
{
$yek = $this->input->post("KEY");
$cek = $this->m_user->get('KEY' => $yek)->num_rows();//line 14
$response = array();
if ($cek > 1)
{
$list = $this->m_masalah->jenis();
}
foreach ($list as $key ) {
$arr = array();
$arr['ID_MASALAH'] = $key['ID_MASALAH'];
$arr['MASALAH'] = $key['MASALAH'];
$arr['JENIS'] = $key['JENIS'];
array_push($response, $arr);
}
echo $json_response = json_encode($response);
}
this is my erorr
Parse error: syntax error, unexpected '=>'
(T_DOUBLE_ARROW), expecting ',' or ')' in
E:\xampp\htdocs\sengketa_dpbt\application\controllers\Masalah.php on line 14
The => symbol is used for assigning array elements, but you didn't declare it as an array here. Just make this change:
$cek = $this->m_user->get(array('KEY' => $yek))->num_rows();
I have the following request in Laravel:
$name = Place::where("status", "1", function ($query) use ($request) {
if (($from = $request->get("from"))) {
$query->where('name', 'like', $from . '%');
}
if (($to = $request->get("to"))) {
$query->where('name', 'like', $to . '%');
}
})->orderBy('name', 'desc')->get();
I get result like as:
{
"id": 2,
"name": "Lena"
},
{
"id": 1,
"name": "Baran"
}
How can I set keys for these two result rows? I want to do:
name_one = {
"id": 2,
"name": "Lena"
},
name_two = {
"id": 1,
"name": "Baran"
}
You can try this code
public function getCollectinos()
{
$collection = collect([
['id' => '1', 'product' => 'Chair'],
['id' => '2', 'product' => 'Desk'],
['id' => '3', 'product' => 'Bookcase'],
]);
$grouped = $collection->groupBy(function ($item, $key) {
return 'name_'.$this->number_to_word($item['id']);
});
$array = $grouped->toArray();
dd($array);
}
public function number_to_word( $num = '' )
{
$num = ( string ) ( ( int ) $num );
if( ( int ) ( $num ) && ctype_digit( $num ) )
{
$words = array( );
$num = str_replace( array( ',' , ' ' ) , '' , trim( $num ) );
$list1 = array('','one','two','three','four','five','six','seven',
'eight','nine','ten','eleven','twelve','thirteen','fourteen',
'fifteen','sixteen','seventeen','eighteen','nineteen');
$list2 = array('','ten','twenty','thirty','forty','fifty','sixty',
'seventy','eighty','ninety','hundred');
$list3 = array('','thousand','million','billion','trillion',
'quadrillion','quintillion','sextillion','septillion',
'octillion','nonillion','decillion','undecillion',
'duodecillion','tredecillion','quattuordecillion',
'quindecillion','sexdecillion','septendecillion',
'octodecillion','novemdecillion','vigintillion');
$num_length = strlen( $num );
$levels = ( int ) ( ( $num_length + 2 ) / 3 );
$max_length = $levels * 3;
$num = substr( '00'.$num , -$max_length );
$num_levels = str_split( $num , 3 );
foreach( $num_levels as $num_part )
{
$levels--;
$hundreds = ( int ) ( $num_part / 100 );
$hundreds = ( $hundreds ? ' ' . $list1[$hundreds] . ' Hundred' . ( $hundreds == 1 ? '' : 's' ) . ' ' : '' );
$tens = ( int ) ( $num_part % 100 );
$singles = '';
if( $tens < 20 )
{
$tens = ( $tens ? ' ' . $list1[$tens] . ' ' : '' );
}
else
{
$tens = ( int ) ( $tens / 10 );
$tens = ' ' . $list2[$tens] . ' ';
$singles = ( int ) ( $num_part % 10 );
$singles = ' ' . $list1[$singles] . ' ';
}
$words[] = $hundreds . $tens . $singles . ( ( $levels && ( int ) ( $num_part ) ) ? ' ' . $list3[$levels] . ' ' : '' );
}
$commas = count( $words );
if( $commas > 1 )
{
$commas = $commas - 1;
}
$words = implode( ', ' , $words );
$words = trim(str_replace( ' ,' , ',' , $this->trim_all( $words ) ) , ', ');
if( $commas )
{
$words = $this->str_replace_last( ',' , ' and' , $words );
}
return $words;
}
else if( ! ( ( int ) $num ) )
{
return 'Zero';
}
return '';
}
public function str_replace_last( $search , $replace , $str )
{
if( ( $pos = strrpos( $str , $search ) ) !== false ) {
$search_length = strlen( $search );
$str = substr_replace( $str , $replace , $pos , $search_length );
}
return $str;
}
public function trim_all( $str , $what = NULL , $with = ' ' )
{
if( $what === NULL )
{
// Character Decimal Use
// "\0" 0 Null Character
// "\t" 9 Tab
// "\n" 10 New line
// "\x0B" 11 Vertical Tab
// "\r" 13 New Line in Mac
// " " 32 Space
$what = "\\x00-\\x20"; //all white-spaces and control chars
}
return trim( preg_replace( "/[".$what."]+/" , $with , $str ) , $what );
}
Output :
you can do a keyBy('name'); after the get() function lookup keyBy example below
$collection = collect([
['product_id' => 'prod-100', 'name' => 'desk'],
['product_id' => 'prod-200', 'name' => 'chair'],
]);
$keyed = $collection->keyBy('product_id');
$keyed->all();
/*
[
'prod-100' => ['product_id' => 'prod-100', 'name' => 'Desk'],
'prod-200' => ['product_id' => 'prod-200', 'name' => 'Chair'],
]
*/
Is there a Laravel 4 equivalent to the codeigniter url_title() function? Or should I just copy it over?
For reference this is the codeigniter one:
function url_title($str, $separator = '-', $lowercase = FALSE)
{
if ($separator == 'dash')
{
$separator = '-';
}
else if ($separator == 'underscore')
{
$separator = '_';
}
$q_separator = preg_quote($separator);
$trans = array(
'&.+?;' => '',
'[^a-z0-9 _-]' => '',
'\s+' => $separator,
'('.$q_separator.')+' => $separator
);
$str = strip_tags($str);
foreach ($trans as $key => $val)
{
$str = preg_replace("#".$key."#i", $val, $str);
}
if ($lowercase === TRUE)
{
$str = strtolower($str);
}
return trim($str, $separator);
}
well I would vote for using something little better adjusted to your need and laravel itself https://github.com/MattHans0n/slug
how to use packages you could read on that page http://laravel.com/docs/packages