在PHP中将多维数组转换为条件表达式

I'm trying to convert an array to condition-like expression string that will be used in the JS Filtrex library. The JS part is irrelevant for this question.

Following is a sample PHP array that I'm trying to work with.

$condition = array(
    'and',
    array(
        array(
            'field' => 'show_image',
            'compare' => '==',
            'value' => 1
        ),
    ),
    array(
        'or',
        array(
            'field' => 'filter',
            'compare' => '!=',
            'value' => 1
        ),
        array(
            'field' => 'align',
            'compare' => '==',
            'value' => 'left'
        )
    )
);

The resulting string will be something like this :

show_image == 1 and ( filter != 1 or align == "left" )

I'm not sold on the array structure so you're free to modify the array as long as it can be extended with more conditions.

Thanks in advance.

You just need to recurse such a structure, and switch according to the first element:

  • If it's a string, then treat the remaining arguments as operands/expressions, joined by operator

  • For associative arrays, just concat field/compare/value into a string

For example:

function join_expr($opts) {
    if (isset($opts["compare"])) {    // optional ↓↓↓ caretaking of value type(!)
        return "($opts[field] $opts[compare] '$opts[value]')";
    }
    elseif (gettype($opts[0]) == "string") {
        $op = array_shift($opts);
        return join(
            " $op ",
            array_map("join_expr", $opts)   // recurse on operands/subexpressions
        );
    }
    elseif (count($opts) == 1) {   // somewhat redundant
        return "(" . join_expr($opts[0]) . ")";
    }
    else {
        trigger_error("wrong struct");
    }
}

i don't know if it works on all cases, but on yours it works well ( i guess )

what I advise you is to delete the nested array $condition[1] and write directly $condition[1][0] array ( the new array structure is in the code below )

<?php

$condition = array(
    'and',
    array(
            'field' => 'show_image',
            'compare' => '==',
            'value' => 1
    ),
    array(
        'or',
        array(
            'field' => 'filter',
            'compare' => '!=',
            'value' => 1
        ),
        array(
            'field' => 'align',
            'compare' => '==',
            'value' => 'left'
        )
    )
);

function generateExpression( Array $condition ) : String
{
 return $condition['field'] . " " . $condition['compare'] . " " . $condition['value'];
}

function generateConditionExpression ( Array $conditionTree ) : String
{
  if ( array_key_exists("field",$conditionTree)  ) 
  {
     return generateExpression($conditionTree);
  }
  //else
  return "(" . generateConditionExpression( $conditionTree[1] ) . " " . $conditionTree[0] . " " . generateConditionExpression( $conditionTree[2] ) . ")";

}

echo generateConditionExpression($condition);

One way to do it with nested foreach(),

<?php
$condition = array(
    'AND',
    array(
        array(
            'field' => 'grid_enable',
            'compare' => '==',
            'value' => 1
        ),
    ),
    array(
        array(
            'field' => 'grid_column',
            'compare' => '>=',
            'value' => 3
        ),
    ),
    array(
        'OR',
        array(
            'field' => 'filter',
            'compare' => '!=',
            'value' => 1
        ),
        array(
            'field' => 'align',
            'compare' => '==',
            'value' => 'left'
        )
    ),
    array(
        array(
            'field' => 'grid_gutter',
            'compare' => '==',
            'value' => 'large'
        ),
    ),
);
$final_result = '';
$double = false;
foreach($condition as $k=>$v){
    $result = [];
    if(!is_array($v)){
        $operator_out = $v;
    }else{
        foreach($v as $k1=>$v1){
            if(!is_array($v1)){
                $operator_inn = $v1;
            }else{
                $result[] = implode(' ',array_values($v1));
            }
        }
    }
    if(count($result) == 1){
        if($double){
            $final_result.= " ".$operator_out." ".$result[0];    
        }else{
            $final_result.= $result[0]." ".$operator_out." ";    
        }
    }
    else if(count($result) == 2){
        $final_result.= "(".implode(" $operator_inn " , $result).")";
        $double= true;
    }
}
echo $final_result;
?>

DEMO: https://3v4l.org/BWmus