Switching methods for a single result - php

I have a situation where I require to consult different objects in PHP via different methods to find some data.
The question regards more about formatting the code than an actual programming issue. What I am trying to do is not using several if's to gather this data like:
$data = obj->getData();
if (!isset($data)) $data = othObj->getThisData();
if (!isset($data)) $data = anothObj->getTheData();
if (!isset($data)) $data = anothOne->getAData();
...
process($data)
I was wondering what are the best practices in this case, if there is a better way using another procedures, like foreach or switch/case.
Thanks!

You can make an array of the possible objects you want to try, then run a loop. Might be more maintainable. This code can be modified to include parameters and use call_user_func_array instead.
$dataCallback = array(
array($othObj, 'getData'),
array($othObj, 'getThisData'),
array($anothObj, 'getTheData'),
array($anothOne, 'getAData'),
);
for($i = 0, $t = count($dataCallback); !isset($data) && $i < $t; $i++) {
$callback = $dataCallback[$i];
$data = call_user_func($callback);
}
if (isset($data))
process($data);
else
//no valid data returned at all ...

It doesn't look too bad the way it is.
It could be a bit more efficient if the if's were nested. e.g.
if (!isset($data = othObj->getData()))
if (!isset($data = othObj->getThisData()))
if (!isset($data = anothObj->getTheData()))
$data = anothOne->getAData()))
// ...
process($data)
Since there are less calls to isset (though they're pretty cheap anyway, so I wouldn't worry about it).

Personally I'd do something like this:
$data = null;
if (isset($obj->getData()) $data = $obj->getData();
else if (isset($othObj->getThisData()) $data = $othObj->getThisData();
else if (isset($anothObj->getTheData()) $data = $anothObj->getTheData();
else if (isset($anothOne->getAData()) $data = $anothOne->getAData();
process($data)
This saves processing time if the earlier objects actually return something. Since it is an elseif setup once it finds data it'll stop processing the other if clauses.
I don't think a switch statement would be appropriate in this case. Switches tend to be testing the value of one variable (is $a = 1, 2, 3 or 4).

($data = $ob1->get()) || ($data = $ob2->get()) || ($data = $ob3->get());
Would work, but if you're get function returns an empty array or false or empty string instead of NULL, it's going to continue looking for data...

I'd probably group the objects to ask for data into an array:
$objArray = array($obj, $othObj, $anothObj, ... );
Then run through a while loop until I had data:
$i = 0;
do {
$data = $objArray[$i]->getData();
$i++;
} while(!isset($data) && $i < count($objArray));

Related

How to use isset in if return variable in laravel

i have some condition if request isset or no and retun in view blade laravel like this:
$compare1 = $request->compare1;
$compare2 = $request->compare2;
if (isset($compare1)) {
$laptop1 = Laptop::where('slug', $compare1)->firstOrFail();
return view('compare.index', ['laptop1' => $laptop1->id]);
} elseif(isset($compare2)) {
$laptop2 = Laptop::where('slug', $compare2)->firstOrFail();
return view('compare.index', ['laptop1' => $laptop1->id, 'laptop2' => $laptop2->id]);
}elseif(isset($compare1, $compare)) {
$laptop1 = Laptop::where('slug', $compare1)->firstOrFail();
$laptop2 = Laptop::where('slug', $compare2)->firstOrFail();
return view('compare.index', ['laptop1' => $laptop1->id, 'laptop2' => $laptop2->id]);
}else {
return view('compare.index');
}
if isset($compare1, $compare) run, $laptop2 not found, any solution for this case...?
Thanks before
The structure you have is incorrect. Currently, your code elseif(isset($compare1, $compare)) will never execute because if either $compare1 or $compare2 are set, your if statement will already exit before it gets to the 3rd one. You also have a lot of redundant code (repeating a line of code depending on which if block is executed) which is easily reduced to fewer lines and cleaner code.
Simple Approach
Consider this; (You should be able to replace your entire if block with this)
//set up an empty array to return
$return = [];
//check if `$compare1` is set, and add to return array if it is
if(isset($compare1)) {
$return['laptop1'] = (Laptop::where('slug', $compare1)->firstOrFail())->id;
}
//same as above but for `$compare2`
if(isset($compare2)) {
$return['laptop2'] = (Laptop::where('slug', $compare2)->firstOrFail())->id;
}
return view('compare.index', $return);
Dynamic Approach
This may be a little bit overkill if you are just doing 2 comparisons but it definitely has some upsides.
Similar amount of code as the simple approach
No need to define separate variables for every comparison (e.g $compare1 = $request->compare1;, etc)
Easily add more comparisons to your return by simply adding them to the $comparisons array
Future Proof
Code:
//empty array to return
$return = [];
//list of variables to compare
$comparisons = ['compare1', 'compare2'];
//loop through each comparison
foreach($comparisons as $key => $request_object) {
$count = $key + 1; //keys start at 0, so we add 1 to make it count sequentially 1,2,3 ...
$comparison = $request->{$request_object}; //grab your comparison object
//check if comparison object is set, add it to return array if it is
if(isset($comparison)) {
$return["laptop{$count}"] = (Laptop::where('slug', $comparison)->firstOrFail())->id;
}
}
return view('compare.index', $return);
Maybe it should be better to split code in parts to improve code quality.
Some thoughts after analyze your code:
Always returns view('compare.index')
Laravel Request contains a has method
Multiple conditional concatenation using if elseif.. (it decrease legibility)
It's more logical to pass the complete model to view instead of only id.
Otherwise, rename var to laptop1Id or similar
Take care of code duplication
Proposal
if($request->has('compare1')) {
$laptop1 = Laptop::where('slug', $request->get('compare1'))->firstOrFail();
}
if($request->has('compare2')) {
$laptop2 = Laptop::where('slug', $request->get('compare2'))->firstOrFail();
}
return view('compare.index', compact('laptop1', 'laptop2'));
If find/search a Laptop is a domain rule, you could encapsulate it in a method/scope in model, querying like:
$laptop = Laptop::findOrFailBySlug($val);
$laptop = Laptop::slug($val)->firstOrFail();

Cleaning up a very long if statement

I have a very long list of strings called $stringfilter1 $stringfilter2 etc all the way up to $stringfilter50
I have another string $reporteremail and I want to make a conditional statement whereby if any of the $stringfilter strings is present in the $reporteremail, some code is executed. At the moment my code looks like this and it works:
if (stripos($reporteremail, $stringfilter1) !== false || stripos($reporteremail, $stringfilter2) !== false || stripos($reporteremail, $stringfilter3) !== false [...]) {
runcode();
}
This is very very long though. I have cut it short here.
I was wondering if there's a cleaner, more efficient way to do this?
EDIT:
I am writing a plugin for a bug tracker. The strings are entered on another page in text boxes. I access them on this page by running a function that looks like
$t_filter = plugin_config_get( 'filter1' );
$stringfilter1 = string_attribute( $t_filter1 );
I would agree looping through an array would be the best way to do this. How can I push each new string onto the end of an array without having to write that snippet above out 50 times?
How can I push each new string onto the end of an array without having to write that snippet above out 50 times?
Try this:
$needles = [];
for ($i = 0; $i < 50; $i++) {
$t_filter = plugin_config_get("filter$i");
$needles[] = string_attribute($t_filter);
}
I have a very long list of strings called $stringfilter1 $stringfilter2 etc all the way up to $stringfilter50
[...]
This is very very long though. I have cut it short here.
I was wondering if there's a cleaner, more efficient way to do this?
Try this, it should go after the code block above.
$flag = false;
foreach ($needles as $needle) {
if (stripos($reporteremail, $needle) !== false) {
$flag = true;
break;
}
}
if ($flag) {
runcode();
}
The code above works by iterating through the $needles array and sets a flag if stripos doesn't return false. After it's finished iterating, it checks if the flag is true, if so, this means that one of the needles was found in the array.
EDIT
Alternatively, you could do it all in one loop, which is both faster and more efficient.
$flag = false;
for ($i = 0; $i < 50; $i++) {
$t_filter = plugin_config_get("filter$i");
$needle = string_attribute($t_filter);
if (stripos($reporteremail, $needle) !== false) {
// One of the needles was found in $reporteremail.
runcode();
break;
}
}
You don't need a loop. First put all your filters in an array instead of having them in separate variables. I would try to do this by modifying the input source rather than doing it in your PHP script. (Based on your comments I'm not sure if that's possible or not, so maybe you do need a loop like the one in the other answer.) Then you can use str_ireplace to check for your filter strings in the $reporteremail. (This will not modify $reporteremail.)
str_ireplace($filters, '', $reporteremail, $count);
if ($count) {
// run code
}
The $count parameter will contain a count of how many replacements were performed. If it's nonzero, then at least one of the filters was found in $reporteremail.

PHP How to compact my variables into a FOR loop?

I have a list of variables which I am using for some PHP functions. I know I can simplify this code but I am not really sure how. I have tried for(){} loops but haven't figured out how to make the result usable.
HERE ARE MY VARIABLES
$saveData_1 = post_data($url_1);
$saveData_2 = post_data($url_2);
$saveData_3 = post_data($url_3);
$saveData_4 = post_data($url_4);
$saveData_5 = post_data($url_5);
$saveData_6 = post_data($url_6);
$saveData_7 = post_data($url_7);
$saveData_8 = post_data($url_8);
$saveData_9 = post_data($url_9);
$saveData_10 = post_data($url_10);
$saveData_11 = post_data($url_11);
$saveData_12 = post_data($url_12);
$saveData_13 = post_data($url_13);
$saveData_14 = post_data($url_14);
$saveData_15 = post_data($url_15);
$saveData_16 = post_data($url_16);
$saveData_17 = post_data($url_17);
$saveData_18 = post_data($url_18);
$saveData_19 = post_data($url_19);
$saveData_20 = post_data($url_20);
HERE IS WHAT I HAVE TRIED
for($i = 0; $i<20; $i++) {
$saveData = '$saveData_'.$i;
$postData = 'post_data($url_'.$i.')';
print($saveData. '=' .$postData. ';');
}
This works for the most part, it prints out my list of variables. But what I don't know, is how to make this usable so instead of printing them to the page being displayed in the browser they will be instead be "printed" to my php script.
Basically my goal is to write the Variables of above without having to actually write a new variable each time.
If I understand what you're asking correctly, you want to have actual "variables" that you can use, such as $saveData_16, but don't want to type each one out?
If that's the case, you can what's called a variable-variable:
$varName = 'saveData_16';
$postName = 'url_16';
$$varName = post_data($$postName);
Note the double $$ prefixing the variable's name.
This can be dropped into a loop with:
for ($i = 0; $i < 20; $i++) {
$varName = 'saveData_' . $i;
$postName = 'url_' . $i;
$$varName = post_data($$postName);
}
Variable-variables can be tricky to remember and a nuisance to keep up with, especially if they contain similar names/values as existing variables. If they do, they can easily overwrite an important variable in your code. If this is a potential issue, you should probably avoid them.
An alternative to variable-variables, but on the same topic, if your values really do come from $_POST (based on the function-name post_data()), you can look into PHP's extract() function which performs the same "extraction" for you:
extract($_POST);
I would recommend against using extract() unless you're very clear on what's needed though. It can easily overwrite other variables that use the same name as a posted field. It does provide an optional second-parameter named $extract_type that is a flag specifying the behavior of the extract and, through this, you can tell it to not overwrite existing variables:
extract($_POST, EXTR_SKIP);
Again though, this can cause headaches by simply forgetting the optional flag - so I would avoid this function if this is a risk.
As an additional alternative, you could store the results into an array and use the array as-is. I personally support this method and, if you use strings as your array indexes, it can be just-as-readable as using a variable (and more-readable than a variable-variable).
If you're currently using $_POST, well, you should be all set - it's already in array form. However, if you're obtaining your data in a different method you can always build your own array with:
// build your array
$saveData = array();
for ($i = 0; $i < 20; $i++) {
$urlName = 'url_' . $i;
$saveData['url_' . $i] = post_data($$urlName);
}
// access a value
echo $saveData['url_4'];
I'm not 100% sure what your $url_# variables are, so I've kept that as a variable-variable in the array example; if it's just a string, you could replace it with the string instead.
You need to use arrays. I will type up an example and add it to this comment. But what you want to use is an array variable. One array can hold all this and you can iterate (loop) through it easily.
Here ya go:
<?php
/* what the heck? You need arrays ;-)
$saveData_1 = post_data($url_1);
$saveData_2 = post_data($url_2);
$saveData_3 = post_data($url_3);
$saveData_4 = post_data($url_4);
$saveData_5 = post_data($url_5);
$saveData_6 = post_data($url_6);
$saveData_7 = post_data($url_7);
$saveData_8 = post_data($url_8);
$saveData_9 = post_data($url_9);
$saveData_10 = post_data($url_10);
$saveData_11 = post_data($url_11);
$saveData_12 = post_data($url_12);
$saveData_13 = post_data($url_13);
$saveData_14 = post_data($url_14);
$saveData_15 = post_data($url_15);
$saveData_16 = post_data($url_16);
$saveData_17 = post_data($url_17);
$saveData_18 = post_data($url_18);
$saveData_19 = post_data($url_19);
$saveData_20 = post_data($url_20);
*/
$saveData = array(); // just a formality. You don't need to delcare variable types in PHP.
for ($i = 1; $i < 21; $i++) {
$var = 'url_'.$i;
$saveData[$i] = post_data($$var);
}
// now all your stuff is in $saveData
// what's in savedata 20? this is:
echo $saveData[20];
// you could skip all of this and just use the $_POST superglobal
$_POST[$url_20]
// I'm curious to see what you are doing, because this process should be re-thought.

should this method be decomposed into separate methods?

This method takes search a search keyword and parsed mysql query, and rewrites the where expression to include LIKE %keyword%.
It works well, but I dont know if its good or bad practice to have a method with this many loops...
private function build_where($query_array, $options)
{
//add WHERE starting point
$where = '';
if(!empty($query_array['WHERE']))
{
//build where array
$where_array = $query_array['WHERE'];
//start the where
$where .= 'WHERE ';
//get columns array
$columns_array = $this->build_columns_array($query_array);
//if there is a search string
if(!empty($options['sSearch']))
{
//check for enabled columns
$i = 0;
$columns_length = count($columns_array);
for($i; $i < intval($columns_length); $i++)
{
//create the options boolean array
$searchable_columns['bSearchable_'.$i] = $options['bSearchable_'.$i];
}
//loop through searchable_columns for true values
foreach($searchable_columns as $searchable_column_key => $searchable_column_val)
{
if($searchable_column_val == true)
{
//get an integer from the searchable_column key
$column_id = preg_replace("/[^0-9]/", '', $searchable_column_key);
//lookup column name by index
foreach($columns_array as $columns_array_key => $columns_array_val)
{
//if the $columns_array_key matches the $column_id
if($columns_array_key == $column_id)
{
//loop to build where foreach base expression
$i = 0;
$where_length = count($where_array);
for($i; $i < intval($where_length); $i++)
{
//append the existing WHERE Expressions
$where .= $where_array[$i]['base_expr'];
}
//append the LIKE '%$options['sSearch'])%'
$where .= ' AND '.$columns_array_val." LIKE '%".$options['sSearch']."%' OR ";
}
}
}
}
//remove the last OR
$where = substr_replace($where, "", -3);
}
else
{
//loop to build where
$i = 0;
$where_length = count($where_array);
for($i; $i < intval($where_length); $i++)
{
$where .= $where_array[$i]['base_expr'];
}
}
}
//print_r($where_length);
return $where;
}
The school of thought of Kent Beck or Martin Fowler would actually advise you to refactor this large methods down to many small methods. It's not easily read in my opinion, which would be the main reason to refactor.
Breaking up methods is not primarily about reuse. Doing so can make code easier to read, test, and maintain. Clear method names can also substitute for inline comments. This method does two high-level things which could be separated: building a where clause with options and without options. Another hint for me is that the logic that builds the where clause with options looks meaty enough to warrant its own method.
private function build_where($query_array, $options) {
if(!empty($query_array['WHERE'])) {
$where_array = $query_array['WHERE'];
$columns_array = $this->build_columns_array($query_array);
if (empty($options['sSearch'])) {
return $this->build_where_with_options($where_array, $columns_array, $options);
}
else {
return $this->build_where_without_options($where_array, $columns_array);
}
}
else {
return '';
}
}
Now you can quickly scan build_where() to see that there are three possible forms the where clause may take and when along with the input each form needs to produce its result.
Here are some minor improvements you can make throughout your code:
count() returns an integer and doesn't need the intval() calls in your for loops. Even if you left those in, it would be better to apply the call outside the loop so its done only once as it yields the same value each time.
if($searchable_column_val == true) is equivalent to if($searchable_column_val) since both cast $searchable_column_val to a boolean value and the latter passes when that casted boolean value equals true.
$where = substr_replace($where, "", -3) can be replace with $where = substr($where, 0, -3) and is a little clearer.
Instead of looping through an array looking for a specific key you can take advantage of PHP's arrays by simply grabbing the value with that key.
For the last one, this code
foreach($columns_array as $columns_array_key => $columns_array_val)
{
//if the $columns_array_key matches the $column_id
if($columns_array_key == $column_id)
{ ... }
}
can be replaced by this
$columns_array_val = $columns_array[$column_id];
...
Personal preference really. Some programmers would chop this up into several functions. Personally, I think it's fine the way you have it. If I saw something that I thought might be reusable, I'd refactor it out into a separate file that could be included.
In my opinion, some programmers are too quick to make things "reuesable" before they even have something to reuse it with.

php for loop variable names

i got a code of 100-200 rules for making a table. but the whole time is happening the same.
i got a variable $xm3, then i make a column . next row, i got $xm2 and make column. next row, i got $xm1 and make column.
so my variables are going to $xm3, $xm2, $xm1, $xm0, $xp1, $xp2, $xp3.
is there a way to make a forloop so i can fill $xm and after that a value from the for loop?
In this kind of structure you'd be better off using an array for these kinds of values, but if you want to make a loop to go through them:
for($i = 0; $i <= 3; $i++) {
$var = 'xm' . $i
$$var; //make column stuff, first time this will be xm0, then xm1, etc.
}
It is not fully clear what you are asking, but you can do
$xm = 'xm3';
$$xm // same as $xm3
in PHP, so you can loop through variables with similar names. (Which does not mean you should. Using an array is usually a superior alternative.)
As far as I am aware using different variable names is not possible.
However if you uses arrays so as below
$xm[3] = "";
$xm[2] = "";
$xm[1] = "";
$xm[0] = "";
or just $xm[] = "";
Then you can use a for each loop:
foreach($xm as $v) { echo $v; }
Edit: Just Googled and this is possible using variable names but is considered poor practice. Learn and use arrays!
You can do this using variable variables, but usually you're better off doing this sort of thing in an array instead.
If you're positive you want to do it this way, and if 'y' is the value of your counter in the for loop:
${'xm' . $y} = $someValue;
You can easily do something like this:
$base_variable = 'xm';
and then you can make a loop creating on the fly the variables;
for example:
for ($i=0; $i<10; $i++)
{
$def_variable = $base_variable . $i;
$$def_variable = 'value'; //this is equivalent to $xm0 = 'value'
}

Categories