I finished some small app using laravel, it works, but I have some kind of spaghetti code. I would like to ask your help. I have some theory about code patterns, but I do not have idea how to implement them in my case.
The most weird part: I have input data that should be analyzed step by step and some actions needs to be taken. For example I have an input an array with parameters
$array = ['param1','param2','param3', ..., 'paramN']
I need to analyze it from param1 to paramN and take some actions. Code structure looks like:
if($array['param1'] == 'X') {
some action
} else { return ....}
if($array['param2'] == 'Y') {
some action
} else {
if($array['param3'] == 'Z') {
return ....
} else { return ....}
}
This code includes uses some Facades, Validation in if blocks, but the number of ifs is terrible. But it's business logic.
Maybe you can give me an idea in which way I can reorganize my code to make it more clear?
you can use collection to refactor your logic with a very good readable code the above code can be look like below
$array = ['param1','param2','param3', ..., 'paramN'];
$collection = collect($array);
now define your processing as callback
$param1Callback = function($value) {
// $value will be value of param1 which is X in your comparision so write your if logic here
if($value == 'x') {
// process here
return true;
}
return false;
}
$callbacks = ['param1' => $param1Callback, 'param2' => $param2Callback ...];
$collection->each(function($value, $key) use($callbacks){
if($callbacks[$key]($value)) {
// here param1 value is processed with 'X' and that callback returned true
}
... and so on
});
hopefully you get the idea here is the full documentation about available methods of collections.
https://laravel.com/docs/5.3/collections#available-methods
Related
I'm trying to simplify a piece of PHP code looking like this :
public function gather($parameters, $infos)
{
$gathered = [];
$number = false;
foreach ($infos as $info) {
foreach ($parameters as $parameter) {
if (strlen($parameter['number'])) {
$this->mark($parameter['number'], $info, $parameter, $gathered); // $gathered is passed by reference
$number = true;
continue;
}
if (strlen($parameter['default'])) {
$this->mark($parameter['default'], $info, $parameter, $gathered); // $gathered is passed by reference
continue;
}
if ($config['smth']) {
doSomething(...);
}
}
}
if (number > 0) {
doSomething(...);
}
return $gathered;
}
Here are the problems I have :
These nested foreaches are really ugly and I'd love to the second one in the other function. But it uses variables that are created and used outside. I already used references with $gathered even if it's not really good.
The conditions of all ifs are different and are using different variables, do you know any way to generalize a case like this?
I am ok to rewrite big parts of the functions, so I'd be happy to hear other design ideas too.
[EDIT] The mark functions add elements to $gathered with some conditions and transformations.
[EDIT] Added more details.
im just learning php
Im trying to add a log with comments to my functions output.
Right now it looks like this:
//the function
function add1($x){
if($GLOBALS['logging'] === 'on'){ $log[] = 'Adding 1 to '.$x;};
$a = $x + 1;
if($GLOBALS['logging'] === 'on'){
$return[] = $a;
$return[] = $log;
return $return;
}else{ return $a; };
};
//calling the function
if($GLOBALS['logging'] === 'on'){
$return = add1($x);
$number = $return[0];
$log = $return[1];
}else{ $number = add1($x); };
Im kinda annoyed by the fact i need to retype this if statement.
So i made a seperate function for returning the function
which looks like this:
//function
function log_return($data = 'x', $log = 'x'){
if($GLOBALS['logging'] === 'on'){
if($data !== 'x') $return[] = $data;
if($log !== 'x') $return[] = $log;
return $return;
} return $data;
};//function end
And returning it with:
return $return = isset($log) ? log_return($data, $log) : log_return($data);
Now my quastion is: Is there a way to call a function with function..
like:
call_function(add1($x));
so i can return it either with log or without..
Given the answer https://stackoverflow.com/a/2700760/5387193 - this should work:
function add1($a)
{
// add1 code goes here
}
function call_function($name, $param)
{
$name($param);
}
call_function('add1', $x);
On a side note, your variable and function names aren't very intuitive. Perhaps you should study how to write good quality readable code. I recommend reading chapter 9 of Refactoring by Martin Fowler, it's quite good. You can find a PDF version on the web.
Another note, your return statement return $return = isset($log) ? log_return($data, $log) : log_return($data); has a unnecessary assignment to $return. The code should simply read
return isset($log) ? log_return($data, $log) : log_return($data);
Yes, it is possible. To simplify:
function first($x) {
return $x+1;
}
function second($y) {
return $y+1;
}
echo second(first(1)); // Returns 3, ie. 1+1+1
As gview said in his comment, don't use global variables. Argument lists exist for several reasons, included but not limited to making code easier to read, edit, and debug. The same goes for function and variable names.
Moreover, your code is very messy. It can be consolidated:
function addTo($currentValue, $valueToAdd, $logging = 0)
{
if ($logging) {
logWrite('addTo', "Adding $valueToAdd to $currentValue");
return $currentValue + $valueToAdd;
} else {
return $currentValue;
}
}
function logWrite($operation, $message)
{
$log = getLog(); // maybe it's a file, or DB record or something
// perform the write, depending on your implementation
}
$number = addTo($someStaringValue, $someOtherValue, 1);
All of this said, logging should not control program flow. In other words, whether something is logged by the system or not should have no bearing on what your code is trying to do. I really think you need to take a broader view of what you're trying to do and break it up into components.
At best, your code should tell a logger to log info, and the logger itself should determine if logging is actually turned on. If it is, the info is logged. If not, then the code that calls on the logger still works and goes about its business.
This is more or less a readability, maintainability and/or best practice type question.
I wanted to get the SO opinion on something. Is it bad practice to return from multiple points in a function? For example.
<?php
// $a is some object
$somereturnvariable = somefunction($a);
if ($somereturnvariable !== FALSE) {
// do something here like write to a file or something
}
function somefunction($a) {
if (isset($a->value)) {
if ($a->value > 2) {
return $a->value;
} else {
return FALSE;
} else {
// returning false because $a->value isn't set
return FALSE;
}
}
?>
or should it be something like:
<?php
// $a is some object
$somereturnvariable = somefunction($a);
if ($somereturnvariable !== false) {
// do something here like write to a file or something
}
function somefunction($a) {
if (isset($a->value)) {
if ($a->value > 2) {
return $a->value;
}
}
return FALSE
}
?>
As a matter of practice, I always try to return from ONE point in any function, which is usually the final point. I store it in a variable say $retVal and return it in the end of the function.It makes the code look more sane to me.
Having said that, there are circumstances where say, in your function as the first line you check if a var is null and if yes you are returning. In this case, there is no point in holdin on to that variable, then adding additional checks to skip all the function code to return that in the end.
So...in conclusion, both ways works. It always depends on what the situation is and what you are more comfortable with.
I'm stuck in Drupal Panels / PHP Access plugins.
At least, now I found the three conditions to create my final snippet. the purpose of it is to return TRUE; if "condition1 is TRUE" OR "condition2 is TRUE" OR "condition3 is TRUE". I found a lot of similar questions, but the last condition force me to post here to find the right way to do this.
Condition 1:
// At least $view1->result has result.
$view1 = views_get_view('sp_onglet_videos');
$view1->set_display('views-tab-embed_1');
$output1 = $view1->preview();
if ($view1->result) {
return TRUE;
}
Condition 2 (same thing):
// At least $view2->result has result.
$view2 = views_get_view('sp_onglet_audio');
$view2->set_display('views-tab-default');
$output2 = $view2->preview();
if ($view2->result) {
return TRUE;
}
Condition 3 is more complex:
// Checks for content in the field field_txt_videos.
if (isset($contexts['argument_nid_1']->data-> field_txt_videos)) {
$field = $contexts['argument_nid_1']->data-> field_txt_videos;
if (is_null($field)) {
return FALSE;
}
if (is_array($field)) {
foreach ($field as $key => $val) {
if (is_array($val)) {
$field[$key] = array_filter($val);
}
}
$field = array_filter($field);
return count($field);
}
if (is_string($field) && trim($field) == '') {
return FALSE;
}
if ($field) {
return TRUE;
}
return FALSE;
}
I would like to have something clean (and functional) like this:
if ($view1->result && $view2->result && $field) {
return TRUE;
}
But it's to tricky for my php knowledge. Need a little help !
You want to save the result of the 3rd condition (into a variable) and use this result to run your final condition/query. But you can query the 3rd condition if it is a function.
It is better to properly space your code and use plenty of newlines.
However, PHP does have some pretty cool tricks to do assignment inside conditional statements.
if(($view1 = views_get_view('sp_onglet_videos')) AND $view1->set_display('views-tab-embed_1') AND ($output1 = $view1->preview()) AND $view1->result) return TRUE;
However, as you can see this code is a mess - don't do it unless your assignment is really small. Take this simple security check at the top of a PHP file:
<?php defined('BASE_PATH') OR die('Not Allowed');
If I have a database table containing a flag that can have multiple states, should I do this
if ($Object->isStateOne()) {
// do something
}
else if ($Object->isStateTwo()) {
// do something else
}
else if ($Object->isStateThree()) {
// do yet something else
}
or this
switch ($Object->getSomeFlag()) {
case ObjectMapper::STATE_ONE:
// do something
break;
case ObjectMapper::STATE_TWO:
// do something else
break;
case ObjectMapper::STATE_THREE:
// do yet something else
break;
}
?
Whichever makes sense, of course.
The switch looks much cleaner. But, what is this 'state' you are checking? If you're translating a string, use an array, for example.
The if has different behavior from switch. The method calls MAY have side effects. The if is better if multiple states may be active on the object at once, though.
From an OO perspective, both are discouraged. If you have different state, you may want to create a virtual methods, and override it in inherited class. Then based on polymorphism, you can avoid the if, and switch statement.
The second option just seems to be the better solution. IMHO, the unsightly duplication of the comparison code that would accompany the methods of the first solution would be the show-stopper for me, for example:
public function isStateOne() {
if(strcmp(ObjectMapper::STATE_ONE, '1') == 0) {
return true;
}
}
public function isStateTwo() {
if(strcmp(ObjectMapper::STATE_TWO, '2') == 0) {
return true;
}
}
public function isStateThree() {
if(strcmp(ObjectMapper::STATE_THREE, '3') == 0) {
return true;
}
}
Of course, others might disagree. I just don't like having classes cluttered up with 'almost the same' methods.