php Use 0 key as value in URL to retrieve data - php

So recently I was working on fixing an older system which is using keys from an array to retrieve certain data.
This is the array with several brands of cars, the key is used to filter certain things so the URL would look like http://example.com/page?brands=1&foo=bar
public static $carBrandGroups = [
0 => 'Volvo',
1 => 'BMW',
2 => 'Renault',
3 => 'Tesla',
4 => 'Opel',
5 => 'Peugeot',
6 => 'Toyota',
7 => 'Mercedes',
8 => 'Honda',
9 => 'Fiat',
]
Now the system works and retrieves everything except when 0 is passed which is logical since 0 is considered empty. I believe there must be a way, is there?
The function which passes additional information is shown below:
public static function getCarBrandGroup($modelNumber)
{
if ($modelNumber == 999) {
return 'Other';
}
if (isset(self::$carBrandGroups[$modelNumber[0]])) {
return self::$carBrandGroups[$modelNumber[0]];
}
return 'Unknown';
}
The modelNumber is three digits so everything that starts with 0 is a volvo model, so 001 is Volvo V40, 101 is BMW X6 and so on...
Like I stated above, everything works except for 0, is there a way to make this work and seen as a value?
Thank you in advance.

I'll asume the $modelNumber is a string with 3 characters in it. In your example you're passing $modelNumber[0] and it should result in an error.
Option 1: split your string into an array
public static function getCarBrandGroup($modelNumber)
{
if ($modelNumber == 999) {
return 'Other';
}
// Split your string into an array
$modelNumber = str_split($modelNumber);
if (isset(self::$carBrandGroups[$modelNumber[0]])) {
return self::$carBrandGroups[$modelNumber[0]];
}
return 'Unknown';
}
Option 2: use the first character to get the required model
public static function getCarBrandGroup($modelNumber)
{
if ($modelNumber == 999) {
return 'Other';
}
// Get the first character using substr
$modelNumber = substr($modelNumber, 0, 1);
if (isset(self::$carBrandGroups[$modelNumber])) {
return self::$carBrandGroups[$modelNumber];
}
return 'Unknown';
}
Option 3: Or if you like shorthand & fancy PHP7 operators:
public static function getCarBrandGroup($modelNumber)
{
if ($modelNumber == 999) {
return 'Other';
}
return self::$carBrandGroups[substr($modelNumber, 0, 1)] ?? 'Unknown';
}

You're most likely getting passed integer 1 instead of string 001. If you know that each code is supposed to have exactly three digits, then you can force a zero-padded reformat and cast to string at the same time by using sprintf():
public static function getCarBrandGroup($modelNumber)
{
$modelNumber = sprintf('%03d', $modelNumber);
if ($modelNumber === '999') {
return 'Other';
}
if (isset(self::$carBrandGroups[$modelNumber[0]])) {
return self::$carBrandGroups[$modelNumber[0]];
}
return 'Unknown';
}
Now, you can pass either (string) "001" or (int) 1.

Related

Heuristic to sort any "kind" of technical measurements in PHP

I different lists with measurements of the same dimension but a bit mixed units like
"1 m, 200 mm, 1 ft"
or maybe also
"1 °C, 273 K" and so on.
Now I want to sort them by absolute order
"200 mm, 1 ft, 1 m" and "273 K, 1 °C"
I am wondering if this a an already solved problem, as I do not want to reinvent the wheel. I am afraid, this might be some kind of "shopping for PHP extensions" questions, but I already found some helpful packages:
https://github.com/PhpUnitsOfMeasure/php-units-of-measure can do all kind of conversation between units of measure.
I already have created code to separate unit and number.
So what I am thinking, to "brute force" the unit to a certain dimension of those:
https://github.com/PhpUnitsOfMeasure/php-units-of-measure/tree/master/source/PhysicalQuantity
Next I could pick the first dimension and convert everything to the first "main" SI unit and sort it.
Right?
Generally, what you need to do is convert these units to some common measurement, but only for the purpose of sorting.
Use usort() and a custom callback function. In your callback, do the conversion for the purpose of comparison.
Be sure to keep the original unit when returning the result though, or rounding errors will creep in.
That is the solution I came up with, based on the suggestions
public function testCompareLength()
{
$this->assertLessThan(0, $this->objectDe->compareFunction('100 mm', '1 m'));
}
public function testCompareTemperature()
{
$this->assertLessThan(0, $this->objectDe->compareFunction('1 K', '0 °C'));
$this->assertGreaterThan(0, $this->objectDe->compareFunction('0 °C', '1 K'));
$this->assertEquals(0, $this->objectDe->compareFunction('-273 °C', '0 K'));
}
/**
* #param $numberString
*
* #return array
*/
public function parseNumber($numberString): array
{
$values = preg_split('/(?<=[0-9.,])(?=[^0-9,.]+)/i', $numberString);
$float = $values[0];
$unit = $values[1] ?? '';
$decPos = strpos($float, '.');
if ($decPos === false) {
$precision = 0;
} else {
$precision = strlen($float) - $decPos - 1;
}
return ['float' => $float, 'unit' => $unit, 'precision' => $precision];
}
private function heuristicMeasureFactory($measure)
{
$prioritizedDimensions = [
Temperature::class,
Length::class,
];
$unit = trim($measure['unit']);
foreach ($prioritizedDimensions as $class) {
foreach ($class::getUnitDefinitions() as $definition) {
if ($definition->getName() == $unit) {
return new $class($measure['float'], $unit);
}
}
}
// now process aliases
foreach ($prioritizedDimensions as $class) {
foreach ($class::getUnitDefinitions() as $definition) {
foreach ($definition->aliases as $alias) {
if ($alias == $unit) {
return new $class($measure['float'], $unit);
}
}
}
}
return null; // NaN
}
/**
* Sort apples and oranges -- kind of. Not.
*
* Compares two strings which represend a measurement of the same physical dimension
*/
public function compareFunction($a, $b)
{
$definitions = Temperature::getUnitDefinitions();
$aParsed = $this->parseNumber($a);
$aVal = $this->heuristicMeasureFactory($aParsed);
$bParsed = $this->parseNumber($b);
$bVal = $this->heuristicMeasureFactory($bParsed);
if ($aVal == null || $bVal == null) {
return strnatcmp($aVal, $bVal); // fallback to string comparision
}
return bccomp($aVal->subtract($bVal)->toNativeUnit(), 0, 36);
}

PHP Check if multiple values in array are all positive or all negative or partly

I have an array with 2 possible values negative and positive. If all the values are positive my function has to return positive.
If all values are negative my function has to return negative. If the values are a mix then my function has to return partly.
My code always returns partly, unfortunately, I don't know why.
const RESULT_OF_SEARCH_POSITIVE = "positive";
const RESULT_OF_SEARCH_NEGATIVE = "negative";
const RESULT_OF_SEARCH_PARTLY = "partly";
...
private function calculateResultOfSearch(array $imagesResultArray)
{
if (array_unique($imagesResultArray) === self::RESULT_OF_SEARCH_POSITIVE) {
return self::RESULT_OF_SEARCH_POSITIVE;
} elseif(array_unique($imagesResultArray) === self::RESULT_OF_SEARCH_NEGATIVE)
{
return self::RESULT_OF_SEARCH_NEGATIVE;
} else {
return self::RESULT_OF_SEARCH_PARTLY;
}
}
As we know the count() function always returns the count of the array. So it goes to the else case in every match of the condition.
You should try something like this:
class Demo{
const RESULT_OF_SEARCH_POSITIVE = "positive";
const RESULT_OF_SEARCH_NEGATIVE = "negative";
const RESULT_OF_SEARCH_PARTLY = "partly";
function calculateResultOfSearch(array $imagesResultArray)
{
if (count(array_count_values($imagesResultArray)) == 1 && $imagesResultArray[0] === self::RESULT_OF_SEARCH_POSITIVE) {
return current($imagesResultArray);
} elseif(count(array_count_values($imagesResultArray)) == 1 && $imagesResultArray[0]== self::RESULT_OF_SEARCH_NEGATIVE) {
return self::RESULT_OF_SEARCH_NEGATIVE;
} else {
return self::RESULT_OF_SEARCH_PARTLY;
}
}
}
$demo = new Demo();
print_r($demo->calculateResultOfSearch(["positive","positive"]));
array_count_values() returns an array using the values of the array as keys and their frequency in the array as values.
Here is a simple way to check the values of an array containing the same value using array_count_values function and count if all keys are the same this should equal.
Reference
A much simplified version of the code which if array_unique just has 1 value, then return it (also I only call it once rather than repeatedly calling it which is very inefficient)...
private function calculateResultOfSearch(array $imagesResultArray)
{
$unique = array_unique($imagesResultArray);
return (count($unique) == 1 ) ? $unique[0]
: self::RESULT_OF_SEARCH_PARTLY;
}
Edit:
I am unfortuantly back after realising I've wasted 20% of the lines I wrote :( If all the items are the same, I can just return the first item of the array passed in so I don't need to store the result of array_unique() at all :-/
private function calculateResultOfSearch(array $imagesResultArray)
{
return ( count(array_unique($imagesResultArray)) == 1 ) ?
$imagesResultArray[0]: RESULT_OF_SEARCH_PARTLY;
}
Try this code :
const RESULT_OF_SEARCH_POSITIVE = "positive";
const RESULT_OF_SEARCH_NEGATIVE = "negative";
const RESULT_OF_SEARCH_PARTLY = "partly";
...
private function calculateResultOfSearch(array $imagesResultArray)
{
if (!in_array(RESULT_OF_SEARCH_NEGATIVE)) {
return self::RESULT_OF_SEARCH_POSITIVE;
} elseif(!in_array(RESULT_OF_SEARCH_POSITIVE)) {
return self::RESULT_OF_SEARCH_NEGATIVE;
} else {
return self::RESULT_OF_SEARCH_PARTLY;
}
}
You're comparing (count(array_unique($imagesResultArray)) which return an int with self::RESULT_OF_SEARCH_POSITIVE or self::RESULT_OF_SEARCH_NEGATIVE which return a string equals to either "positive" or "negative" so it's always false.
You need to change your conditions.
EDIT after OP's edit
PHP: array_unique
Takes an input array and returns a new array without duplicate values.
In your case you might want to check if the resulting array only has one element equals to "positive" or "negative".
$newArray = array_unique($imagesResultArray);
if (count($newArray) == 1 && $newArray[0] === self::RESULT_OF_SEARCH_POSITIVE) {
return self::RESULT_OF_SEARCH_POSITIVE;
} elseif(count($newArray) == 1 && $newArray[0] === self::RESULT_OF_SEARCH_NEGATIVE) {
return self::RESULT_OF_SEARCH_NEGATIVE;
} else {
return self::RESULT_OF_SEARCH_PARTLY;
}
With your guys answers I came to this "clean" solution.
private function calculateResultOfSearch(array $imagesResultArray)
{
$results = array_unique($imagesResultArray);
if(count($results) == 1 && $results[0] === self::RESULT_OF_SEARCH_NEGATIVE) {
return self::RESULT_OF_SEARCH_NEGATIVE;
}
if(count($results) == 1 && $results[0] === self::RESULT_OF_SEARCH_POSITIVE) {
return self::RESULT_OF_SEARCH_POSITIVE;
}
return self::RESULT_OF_SEARCH_PARTLY;
}

Return one dimensional array instead of multi

Ok so I'm wanting just the years returned as a one dimensional array instead of a multi when pulling from the database:
public function getYears($make = null, $model = null) {
// select table
$this->select(array('DISTINCT year'));
$this->table($this->table);
// build where clause
if( $make !== null && $model !== null ) {
$this->where('make', '=', $make);
$this->and_where('model', '=', $model);
} elseif( $make !== null && $model === null ) {
$this->where('make', '=', $make);
} elseif( $make === null && $model !== null ) {
$this->where('model', '=', $model);
}
return $this->all();
}
This function basically just returns SELECT DISTINCT year FROM vehicle;
In another script, I'm doing:
$years = $vehicle->getYears();
However it returns as so:
array (size=1)
0 =>
array (size=1)
'year' => string '2014' (length=4)
1 =>
array (size=1)
'year' => string '2013' (length=4)
I want it to just return as so:
array (size=1)
0 => string '2014' (length=4)
1 => string '2013' (length=4)
Is that possible? Because I'm putting it through my select form helper function that takes a one dimensional array of all your options and loops through them. But it can't loop through a multi-list because it's trying to echo out the index which is unfortunately an array.. not a string.
Thanks for any help.
Just an example, needs error checking / check for valid column, etc...
public function all($column=null) {
$this->execute();
if($column === null) {
return $this->stmt->fetchAll(PDO::FETCH_ASSOC);
}
while($row = $this->stmt->fetch(PDO::FETCH_ASSOC)) {
$result[] = $row[$column];
}
return $result;
}
Or create another method all_single($column) or something and use that instead. No need to loop through the results of all(). The only time you'll need this is if you only get a single column.
Also you can restructure this based on whether you like to return once or return early as shown.
You either edit your All method, create a new method such as AllOneDimentional (not a great name!) or you take the data returned from All and manipulate it:
foreach($years as $year)
$onedimyears[]=$year['year'];
In the past when i've attempted this i've used this function
function flatten(array $array) {
$return = array();
array_walk_recursive($array, function($a) use (&$return) { $return[] = $a; });
return $return;
}
It's as about as efficient as you can get assuming your on php 5.3 or greater

Passing object method to array_map() [duplicate]

This question already has answers here:
How to use class methods as callbacks
(5 answers)
Closed 3 months ago.
class theClass{
function doSomeWork($var){
return ($var + 2);
}
public $func = "doSomeWork";
function theFunc($min, $max){
return (array_map(WHAT_TO_WRITE_HERE, range($min, $max)));
}
}
$theClass = new theClass;
print_r(call_user_func_array(array($theClass, "theFunc"), array(1, 5)));
exit;
Can any one tell what i can write at WHAT_TO_WRITE_HERE, so that doSomeWork function get pass as first parameter to array_map. and code work properly.
And give out put as
Array
(
[0] => 3
[1] => 4
[2] => 5
[3] => 6
[4] => 7
)
To use object methods with array_map(), pass an array containing the object and the objects method name. For same-object scope, use $this as normal. Since your method name is defined in your public $func property, you can pass func.
As a side note, the parentheses outside array_map() aren't necessary.
return array_map( [$this, 'func'], range($min, $max));
The following code provides an array of emails from an $users array which contains instances of a class with a getEmail method:
if(count($users) < 1) {
return $users; // empty array
}
return array_map(array($users[0], "getEmail"), $users);
I tried search for solution but it not works, so I created custom small map function that will do the task for 1 variable passed to the function it simple but will act like map
class StyleService {
function check_css_block($str){
$check_end = substr($str,-1) == ';';
$check_sprator = preg_match_all("/:/i", $str) == 1;
$check_sprator1 = preg_match_all("/;/i", $str) == 1;
if ( $check_end && $check_sprator && $check_sprator1){
return $str;
} else {
return false;
}
}
function mymap($function_name, $data){
$result = array();
$current_methods = get_class_methods($this);
if (!in_array($function_name, $current_methods)){
return False;
}
for ($i=0; $i<count($data); $i++){
$function_result = $this->{$function_name}($data[$i]);
array_push($result, $function_result);
}
return $result;
}
function get_advanced_style_data($data)
{
return $this->mymap('check_css_block', $data);
}
}
this way you can call a function name as string $this->{'function_name'}() in php

How to get the real type of a value inside string?

I was searching here on StackOverflow about converting string to the real value and i didn't found.
I need a function like "gettype" that does something like the result above, but i can't do it all :s
gettypefromstring("1.234"); //returns (doble)1,234;
gettypefromstring("1234"); //returns (int)1234;
gettypefromstring("a"); //returns (char)a;
gettypefromstring("true"); //returns (bool)true;
gettypefromstring("khtdf"); //returns (string)"khtdf";
Thanks to all :)
1+ for Svisstack! ;)
Here is the function if someone want it:
function gettype_fromstring($string){
// (c) José Moreira - Microdual (www.microdual.com)
return gettype(getcorrectvariable($string));
}
function getcorrectvariable($string){
// (c) José Moreira - Microdual (www.microdual.com)
// With the help of Svisstack (http://stackoverflow.com/users/283564/svisstack)
/* FUNCTION FLOW */
// *1. Remove unused spaces
// *2. Check if it is empty, if yes, return blank string
// *3. Check if it is numeric
// *4. If numeric, this may be a integer or double, must compare this values.
// *5. If string, try parse to bool.
// *6. If not, this is string.
$string=trim($string);
if(empty($string)) return "";
if(!preg_match("/[^0-9.]+/",$string)){
if(preg_match("/[.]+/",$string)){
return (double)$string;
}else{
return (int)$string;
}
}
if($string=="true") return true;
if($string=="false") return false;
return (string)$string;
}
I used this function to know if the number X is multiple of Y.
Example:
$number=6;
$multipleof=2;
if(gettype($number/$multipleof)=="integer") echo "The number ".$number." is multiple of ".$multipleoff.".";
But the framework that i work returns always the input vars as strings.
You must try to convert it in specified order:
Check is double
If double, this may be a integer, you must convert and compare this values.
If not, this is char if lenght is == 1.
If not, this is string.
If string, try parse to bool.
You can't use gettype because you may get string type of decimal writed in string.
Here is an updated version of this 9 year old function:
/**
* Converts a form input request field's type to its proper type after values are received stringified.
*
* Function flow:
* 1. Check if it is an array, if yes, return array
* 2. Remove unused spaces
* 3. Check if it is '0', if yes, return 0
* 4. Check if it is empty, if yes, return blank string
* 5. Check if it is 'null', if yes, return null
* 6. Check if it is 'undefined', if yes, return null
* 7. Check if it is '1', if yes, return 1
* 8. Check if it is numeric
* 9. If numeric, this may be a integer or double, must compare this values
* 10. If string, try parse to bool
* 11. If not, this is string
*
* (c) José Moreira - Microdual (www.microdual.com)
* With the help of Svisstack (http://stackoverflow.com/users/283564/svisstack)
*
* Found at: https://stackoverflow.com/questions/2690654/how-to-get-the-real-type-of-a-value-inside-string
*
* #param string $string
* #return mixed
*/
function typeCorrected($string) {
if (gettype($string) === 'array') {
return (array)$string;
}
$string = trim($string);
if ($string === '0') { // we must check this before empty because zero is empty
return 0;
}
if (empty($string)) {
return '';
}
if ($string === 'null') {
return null;
}
if ($string === 'undefined') {
return null;
}
if ($string === '1') {
return 1;
}
if (!preg_match('/[^0-9.]+/', $string)) {
if(preg_match('/[.]+/', $string)) {
return (double)$string;
}else{
return (int)$string;
}
}
if ($string == 'true') {
return true;
}
if ($string == 'false') {
return false;
}
return (string)$string;
}
I am using it in a Laravel middleware to transform form values that were stringified by browser JavaScript's FormData.append() back into their correct PHP types:
public function handle($request, Closure $next)
{
$input = $request->all();
foreach($input as $key => $value) {
$input[$key] = $this->typeCorrected($value);
}
$request->replace($input);
return $next($request);
}
To create that, type in your CLI php artisan make:middleware TransformPayloadTypes.
Then paste in the above handle function.
Don't forget to paste in the typeCorrected function also. I currently recommend making it a private function in your middleware class, but I don't claim to be a super-expert.
You can imagine that $request->all() is an array of key/value pairs, and it comes in with all values stringified, so the goal is to convert them back to their true type. The typeCorrected function does this. I've been running it in an application for a few weeks now, so edge cases could remain, but in practice, it is working as intended.
If you get the above working, you should be able to do something like this in Axios:
// note: `route()` is from Tightenco Ziggy composer package
const post = await axios.post(route('admin.examples.create', {
...this.example,
category: undefined,
category_id: this.example.category.id,
}));
Then, in your Laravel controller, you can do \Log::debug($request->all()); and see something like this:
[2020-10-12 17:52:43] local.DEBUG: array (
'status' => 1,
'slug' => 'asdf',
'name' => 'asdf',
'category_id' => 2,
)
With the key fact being that you see 'status' => 1, and not 'status' => '1',
All of this will allow you to submit JSON payloads via Axios and receive non-nested values in your FormRequest classes and controllers as the actual payload types are mutated. I found other solutions to be too complex. This above solution allows you to submit flat JSON payloads from pure JavaScript easily (so far, haha).

Categories