剖析 WordPress `wpdb` 类源码:`query_as_array()` 方法将结果转换为关联数组。

各位听众,晚上好!今天咱们来聊聊 WordPress 里一个挺实在的家伙:wpdb 类的 query_as_array() 方法。这玩意儿,简单说,就是把数据库查询的结果,变成咱看得懂、用得顺手的关联数组。别怕,听起来高大上,其实跟咱们平时用的数组没啥大区别,只是多了些“标签”。

开场白:为啥要用数组?

想象一下,你去超市买东西,结账的时候,收银员给你一堆数字,告诉你总共花了多少钱。如果你想知道具体每样东西多少钱,是不是得费劲巴拉地去翻购物清单? 但如果收银员给你一张清单,上面写着“苹果:5元,香蕉:3元,牛奶:10元”,是不是一目了然?

数组就相当于这个清单。它把数据组织起来,用“键”(Key)来标记每个数据,方便我们快速找到想要的东西。

主角登场:query_as_array() 方法

query_as_array()wpdb 类的一个方法,它的作用就是执行 SQL 查询,然后把结果转换成关联数组。关联数组,顾名思义,就是键和值之间有关联的数组。

源码解剖:一步步看明白

为了更好地理解,咱们直接扒开源码看看。虽然 WordPress 版本不同,代码可能会略有差异,但核心思想是不变的。

首先,我们得找到 wpdb 类。一般情况下,它位于 wp-includes/wp-db.php 文件里。然后,找到 query_as_array() 方法。

<?php
/**
 * Executes a SQL query and returns an array of the results.
 *
 * @since 3.5.0
 *
 * @param string $query SQL query.
 * @param string $output_type Optional. Any of ARRAY_A | ARRAY_N | OBJECT | OBJECT_K constants.
 *                            With one of the first two, return an array of associative or numerically indexed arrays of row results.
 *                            With OBJECT, return an array of row objects.
 *                            With OBJECT_K, return an associative array of row objects keyed by the value of first field.
 *
 * @return array|null Database query results in an array, or null on failure.
 */
public function query_as_array( $query, $output_type = ARRAY_A ) {
    $this->func_call = __FUNCTION__;
    return $this->get_results( $query, $output_type );
}

诶?等等,这代码也太简单了吧! 实际上,query_as_array() 只是对 get_results() 方法的一个封装,并默认指定了输出类型为 ARRAY_AARRAY_A 就是我们想要的关联数组。

所以,真正的“幕后英雄”是 get_results() 方法。我们来看看 get_results() 的部分源码(为了简化,只展示关键部分):

<?php
/**
 * Gets one variable from the query result.
 *
 * @since 2.0.0
 *
 * @param string      $query      SQL query.
 * @param int         $x          Optional. Offset of the variable to return.
 * @param int         $y          Optional. Offset of the row from the query result to return.
 * @param mixed       $output     Optional. A constant of OBJECT, ARRAY_A, ARRAY_N.
 * @return array|object|null Database query results based on the type of output.
 */
public function get_results( $query = null, $output = OBJECT ) {
    if ( null === $query ) {
        $query = $this->last_query;
    }

    if ( ! $query ) {
        return null;
    }

    // This method must be able to be called statically, for example, from wp-cli.
    $db = $this;

    // Take note of the call time.
    $this->timer_start();

    // Kill embedded linebreaks and tabs.
    $query = trim( preg_replace( '/[rnt]+/', ' ', $query ) );

    // Run the query.
    $result = @mysqli_query( $this->dbh, $query );

    $this->num_queries++;

    // If there is an error then take note of it.
    if ( $this->last_error = mysqli_error( $this->dbh ) ) {
        $this->print_error();
        return null;
    }

    $this->last_query = $query;

    // Store collation of first result.
    if (
        $result
        && is_object( $result )
        && $result instanceof mysqli_result
        && ! isset( $this->collate )
    ) {
        $this->set_result_collation( $result );
    }

    // If query took less than 100ms, log call time. After that, track it.
    if ( $this->timer_stop() < 0.1 ) {
        $this->queries[] = array(
            'query'     => $query,
            'num_rows'  => mysqli_num_rows( $result ),
            'time_taken' => $this->timer_stop(),
        );
    } else {
        $this->long_queries[ md5( $query ) ] = array(
            'query'     => $query,
            'num_rows'  => mysqli_num_rows( $result ),
            'time_taken' => $this->timer_stop(),
        );
    }

    // Bail if there is no result.
    if ( ! $result ) {
        return null;
    }

    // The return value array.
    $return_value = array();

    // If fetching rows as objects, skip the foreach loop and just convert the object.
    if ( OBJECT === $output ) {
        while ( $row = mysqli_fetch_object( $result ) ) {
            $return_value[] = $row;
        }
    } elseif ( ARRAY_A === $output ) {
        while ( $row = mysqli_fetch_assoc( $result ) ) {
            $return_value[] = $row;
        }
    } elseif ( ARRAY_N === $output ) {
        while ( $row = mysqli_fetch_row( $result ) ) {
            $return_value[] = $row;
        }
    } else {
        while ( $row = mysqli_fetch_object( $result ) ) {
            $return_value[ $row->{$output} ] = $row;
        }
    }

    // Free the result.
    @mysqli_free_result( $result );

    // Reset the collation of the database connection.
    $this->set_charset( $this->dbh );

    return $return_value;
}

重点关注 ARRAY_A 的部分,mysqli_fetch_assoc() 函数就是把数据库的每一行结果转换成关联数组的关键。它会把数据库表的列名作为数组的键,对应的值作为数组的值。

用法示例:手把手教你用

说了这么多理论,不如来点实际的。假设我们有一个名为 wp_my_table 的表,里面有 id, name, email 三列。现在我们要查询所有记录,并以关联数组的形式返回。

<?php
global $wpdb;

$query = "SELECT * FROM {$wpdb->prefix}my_table"; // 注意:使用 $wpdb->prefix 获取表前缀,避免硬编码

$results = $wpdb->query_as_array( $query );

if ( $results ) {
    foreach ( $results as $row ) {
        echo "ID: " . $row['id'] . "<br>";
        echo "Name: " . $row['name'] . "<br>";
        echo "Email: " . $row['email'] . "<br>";
        echo "<hr>";
    }
} else {
    echo "No results found.";
}
?>

这段代码做了什么呢?

  1. 获取全局 $wpdb 对象: $wpdb 是 WordPress 提供的数据库操作对象,我们通过 global $wpdb; 来获取它。
  2. 构建 SQL 查询语句: $query 变量存储了我们要执行的 SQL 查询语句。注意,我们使用了 $wpdb->prefix 来获取表前缀,这是 WordPress 推荐的做法,可以避免硬编码表名,提高代码的灵活性。
  3. 执行查询并获取结果: $results = $wpdb->query_as_array( $query ); 这行代码就是核心。它使用 query_as_array() 方法执行查询,并将结果存储在 $results 变量中。
  4. 遍历结果并输出: 如果查询成功,$results 将是一个包含关联数组的数组。我们使用 foreach 循环遍历 $results,对于每一行结果,我们都可以通过 $row['id']$row['name']$row['email'] 来访问对应的值。

query_as_array() 的优势:为啥要用它?

  • 可读性好: 关联数组用键来标识数据,比数字索引的数组更容易理解和维护。
  • 方便访问: 我们可以直接通过键名来访问数据,不需要记住数据的索引位置。
  • 灵活性高: 关联数组可以存储各种类型的数据,包括字符串、数字、数组等。

query_as_array() 的局限性:也要小心使用

  • 性能问题: 对于大量数据的查询,query_as_array() 可能会占用较多的内存。 因为它会将所有结果一次性加载到内存中。
  • 不适合复杂的查询: 如果查询涉及到多个表连接或者复杂的条件判断,使用原生 SQL 查询可能更灵活。

与其他方法的对比:get_results() 的其他选项

get_results() 方法除了 ARRAY_A,还支持其他几种输出类型:

输出类型 说明 示例
OBJECT 返回一个对象数组,每个对象对应一行数据。 $row->id, $row->name, $row->email
ARRAY_N 返回一个数字索引数组的数组,每个数组对应一行数据。 $row[0], $row[1], $row[2]
OBJECT_K 返回一个对象数组,键是第一列的值,值是对象。 $results[$id]->name, $results[$id]->email

高级用法:定制你的数组

有时候,我们可能需要对查询结果进行一些定制化的处理。比如,我们想要把结果按照某个字段进行分组。

<?php
global $wpdb;

$query = "SELECT * FROM {$wpdb->prefix}my_table";

$results = $wpdb->get_results( $query, OBJECT ); // 获取对象数组

$grouped_results = array();

if ( $results ) {
    foreach ( $results as $row ) {
        $grouped_results[$row->category_id][] = $row; // 假设有一个 category_id 字段
    }
}

// 现在 $grouped_results 就是一个以 category_id 为键,包含对应记录的数组
?>

这段代码先把查询结果转换成对象数组,然后遍历数组,按照 category_id 字段进行分组,最终得到一个以 category_id 为键,包含对应记录的数组。

最佳实践:如何优雅地使用 query_as_array()

  • 避免 SELECT *: 只选择需要的列,可以减少数据传输量,提高性能。
  • 使用预处理语句: 防止 SQL 注入攻击,提高安全性。
  • 分页查询: 对于大量数据的查询,使用分页可以减少内存占用,提高用户体验。
  • 合理使用缓存: 对于经常访问的数据,可以使用缓存来减少数据库查询次数。

总结:query_as_array(),你的好帮手

wpdb 类的 query_as_array() 方法是一个非常实用的工具,它可以帮助我们轻松地将数据库查询结果转换成关联数组,方便我们进行数据处理和展示。但也要注意它的局限性,合理使用,才能发挥它的最大价值。

今天的讲座就到这里,希望大家有所收获! 谢谢大家!

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注