微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

PHP:在嵌套的 foreach 循环中查找最后一项?

如何解决PHP:在嵌套的 foreach 循环中查找最后一项?

我正在使用 API 来根据用户输入检索记录。

有过滤器和过滤器组将被连接,但是,我不希望每个循环嵌套中的最后一个 ANDOR 连接到字符串。

我想要一个 if 语句来查找嵌套 foreach 循环中的最后一项并将不同的字符串连接到末尾,如下所示:

if($i == $lastItem) {
    $conditions .= 'WHERE ' . $type . ' = '. "'". $tag . "'";
} 
else {
    $conditions .= 'WHERE ' . $type . ' = '. "'". $tag . "'" . ' ' . $condition . " ";
}

使用 PHP 为每个循环查找嵌套的最后一项的最佳实践是什么?

这是参考代码

$conditions = "";
$filters = "";
foreach($request->filters as $key=>$filter) {
            foreach($filter as $item) {
                if($item['tag']) {
                    $type = $item['code'];
                    $tag = $item['tag'];
                    $condition = $item['condition'];
                    $conditions .= 'WHERE ' . $type . ' = '. "'". $tag . "'" . ' ' . $condition . " ";
                }
            }
          $groupCondition = $request->conditions[$key]['condition'];
          $filters .= '('.$conditions.') ' . $groupCondition . " ";
        }

这里是一个请求的例子,两个对象在foreach循环中组合:

filter: {
  0 {
    0 {
     code: 'gl_code',condition: 'OR',tag: '10-140-4700-0401'
    }
  1 {
    0 {
     code: 'ty_letter_no',condition: 'AND',tag: 'AM123'
    },1 {
     code: 'gl_code',tag: '10-140-4700-0401'
    }   
}

groupConditions: {
   0 {
     condition: 'OR'
     }
   1 {
     condition: 'AND'
     }
}

这是当前输出的示例:

"(WHERE ty_letter_no = 'AM123' AND )
 OR 
(WHERE ty_letter_no = 'AM123' AND WHERE solicit_code = '19-10NL' AND WHERE ty_letter_no = 'AU' AND ) 
AND 
(WHERE ty_letter_no = 'AM123' AND WHERE solicit_code = '19-10NL' AND WHERE ty_letter_no = 'AU' AND WHERE solicit_code = '19-04HRGOLF' AND ) 
AND "

我希望它输出

"(WHERE ty_letter_no = 'AM123') 
OR 
(WHERE ty_letter_no = 'AM123' AND WHERE solicit_code = '19-10NL' AND WHERE ty_letter_no = 'AU')
AND 
(WHERE ty_letter_no = 'AM123' AND WHERE solicit_code = '19-10NL' AND WHERE ty_letter_no = 'AU' AND WHERE solicit_code = '19-04HRGOLF')"

解决方法

只是一个想法:您总是可以了解当前迭代是否是第一个(通过使用标志)。

通过这种方式,您可以:

  • 跳过第一次迭代
  • 在每次迭代时保存当前项目,以供下一次迭代使用
  • 在第 Nth 次迭代时更新查询,附加与 (N-1)th 次迭代相关的信息
  • 在循环外附加与上次迭代相关的信息,应用您需要的修改

类似的东西

$conditions = "";
$filters = "";
$isFirst = true;
foreach($request->filters as $key=>$filter) {
    foreach($filter as $item) {
        if($isFirst)
        {
            $isFirst = false;
        }
        else
        {   if($previousItem['tag']) {
                $type = $previousItem['code'];
                $tag = $previousItem['tag'];
                $condition = $previousItem['condition'];
                $conditions .= 'WHERE ' . $type . ' = '. "'". $tag . "'" . ' ' . $condition . " ";
            }
        }
        $previousItem = $item;
    }
    $groupCondition = $request->conditions[$key]['condition'];
    $filters .= '('.$conditions.') ' . $groupCondition . " ";
}

if(isset($previousItem))
{
    $type = $previousItem['code'];
    $tag = $previousItem['tag'];
    $conditions .= 'WHERE ' . $type . ' = '. "'". $tag . "'"
}

您可以尝试根据您的需要调整此解决方案。

,

我将在这些假设下工作:

  • 请求来自“LEGO 模型”构建器,因此 condition 条目始终存在,但有时并不重要(特别是当它是其组的最后一个时)。
  • 生成的代码必须是有效的 SQL 条件。
  • 字符串值不需要转义,并且已针对 SQL 注入进行了验证。

如果是这样,那么您可以使用有限状态机来实现您的结果:

$completeCondition = '';
$groupjoin         = '';
foreach ($request->filter as $index => $conditions) {
   $conditionjoin    = '';
   $partialCondition = '';
   foreach ($conditions as $triplet) {
       $partialCondition .= "{$conditionjoin}{$triplet->code} = '{$triplet->tag}'";
       $conditionjoin = " {$triplet->condition} ";
   }
   $completeCondition .= "{$groupjoin}({$partialCondition})";
   $groupjoin = " {$request->groupConditions[$index]->condition} ";
}
if (!empty($completeCondition)) {
    $completeCondition = " WHERE {$completeCondition}";
}

使用此版本的请求,

$request = json_decode('{
"filter": [
    [
       { "code": "gl_code","condition": "OR","tag": "10-140-4700-0401" }
    ],[
       { "code": "ty_letter_no","condition": "AND","tag": "AM123" },{ "code": "gl_code","tag": "10-140-4700-0401" }
    ]
],"groupConditions": [ { "condition": "OR" },{ "condition": "AND" } ]
}');

结果如下,有效的 SQL:

WHERE (gl_code = '10-140-4700-0401') 
   OR (ty_letter_no = 'AM123' AND gl_code = '10-140-4700-0401')

(如果目标语言不是 SQL,当然可以稍微更改代码)。

更精细的结果(PDO 支持)

通常,您不希望在您的 SQL 代码中按原样包含请求字符串,因为这允许用户随意更改您将执行的 SQL 代码。例如,如果我要发送一个

标签
'||SLEEP(60)||'

上面的代码很乐意将其编码为 gl_code = ''||SLEEP(60)||'',这是一个有效的 SQL 请求,将被执行,暂停您的线程 60 秒。如果我知道您正在使用 MySQL,我可以使用 LOCK() 函数执行一些技巧并尝试耗尽内部元数据内存。如果您真的倒霉,并且复合查询没有被禁用(它们通常是!),那么我非常担心我可以拥有您的 SQL 服务器。即使是这样,也可以使用 LEFT JOINsUNIONSELECT 'code' INTO DUMPFILE '/var/www/nasty.php' 来完成一些肮脏的技巧,而且并非所有安装都针对所有这些技巧进行了加固。

为了避免这种情况,我们使用 PDO 和 SQL 参数化。这需要分两部分发送查询,一个准备好的查询,如

`...AND gl_code = :value1`

和一个包含...':value1' => 'AL-1234-56'...绑定列表

$completeCondition = '';
$groupjoin         = '';
$bindingList       = array();

foreach ($request->filter as $index => $conditions) {
   $conditionjoin    = '';
   $partialCondition = '';
   foreach ($conditions as $triplet) {
       $bind = ':b' . count($bindingList);
       $partialCondition .= "{$conditionjoin}{$triplet->code} = {$bind}";
       $conditionjoin = " {$triplet->condition} ";
       $bindingList[$bind] = $triplet->tag;
   }
   $completeCondition .= "{$groupjoin}({$partialCondition})";
   $groupjoin = " {$request->groupConditions[$index]->condition} ";
}
if (!empty($completeCondition)) {
    $completeCondition = " WHERE {$completeCondition}";
}

// Now we could safely do (supposing $pdo is my PDO DB object)
$stmt = $pdo->prepare($completeCondition);
$stmt->execute($bindingList);
while ($row = $stmt->fetch()) {
    // Do something with the row.
}
,

为了严格回答您的问题,您能否通过检查当前迭代(在您的情况下,是键)与数组的长度 (count()) 来确定您是否正在处理最后一次迭代。您可以通过确保 if( $iteration < $length - 1 ),然后您不在最后一次迭代中来避免在该迭代中执行某些操作。如果您想检查自己是否,请if( $iteration == $length - 1 )

为了解决更大范围的问题:

在以编程方式构建查询时,我经常发现使用 WHERE 1=1 开始更容易,因为它允许您使用 implode() 作为粘合剂 AND 数组 (并且某些 DBMS 甚至不会解析 1=1)。

$filters = "";
$length  = count($request->filters);

foreach($request->filters as $key => $filter) {
    $conditions = array( "WHERE 1=1" );
    
    foreach($filter as $item){
        $tag  = $item['tag'];
        $type = $item['code'];
        $condition = $item['condition'];
        
        $conditions[] = "{$type} = '{$tag}'";
    }
    
    // Build the conditions
    $filters .= sprintf( '(%s)',implode(' AND ',$conditions ) );
    
    // If this isn't the last item,add the group conditions
    $filters .= ( $key < $length-1 ) ? " {$request->conditions[$key]['condition']} " : '';
}

最后一行检查以确保它不是最后一项(通过在附加最后一个条件之前确保当前索引小于 $length-1。使用上面的,你最终会得到一个过滤器:

(WHERE 1=1 AND gl_code = '10-140-4700-0401') OR (WHERE 1=1 AND ty_letter_no = 'AM123' AND gl_code = '10-140-4700-0401')

如果您要发送的服务不喜欢 1=1(它根本不应该有任何影响,但如果它确实),您可以执行 {{ 1}} 结尾。

$filters = str_replace( '1=1 AND ','',$filters );

另请注意,这不会输出严格有效的 SQL,只是您请求的格式(我假设服务出于自身原因需要这种格式的过滤器)

这是一个quick example

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。