PHP中的关联数组结构有问题


Having trouble with associative array structure in PHP

不太确定如何表达这个问题,但我在编写的类中遇到了关联数组的结构问题。代码的相关部分如下所示:

class User {
    public $userData;
    function getUserData() {
        $stmt = $this->conn->prepare('SELECT * FROM user_table WHERE id = :id');
        $stmt->execute(array('id' => $this->ID));
        $row = $stmt->fetchAll();
        foreach($row AS $key => $val) {
            $this->userData[$key] = $val;
        }
        return $this->userData;
    }

我希望能够使用它来设置类属性,这样我就可以像这样访问它们:

$this->userData['username'];

例如,但这对我不起作用。相反,我必须这样访问它们才能起作用:

$this->userData[0]['username'];

我必须添加数组索引才能访问任何数据,我不想这样做,但我不知道如何解决这个问题。

这与PDOStatement::fetchAll()如何返回行数据有关。

即使你只选择了一行,它仍然会返回一个行索引(零),也就是说,如果你做了这个

print_r($row);

你会得到这样的

Array
(
    [0] => Array
        (
            [username] => Roy
            [0] => Roy
        )
)

注意,默认情况下,fetchAll()会返回数据中的每一列两次-一次按列名返回,一次按列索引返回

事实上,您将变量命名为$row可能是让您感到困惑的部分原因——从语义上讲,应该是$rows

要解决这个问题,你有几个选择

  1. 直接访问结果中的第一行

    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); // Notice the addtion of the
    foreach($rows[0] AS $key => $val)
    
  2. 使用PDOStatement::fetch()代替

    $row = $stmt->fetch(PDO::FETCH_ASSOC); // Notice the addtion of the
    foreach($row AS $key => $val)
    

但在这两种情况下,for循环都是不必要的。你可以简单地做

$this->userData = $stmt->fetch(PDO::FETCH_ASSOC);

最后,我必须质疑为什么User::$userData是公开的,但你也为它写了一个getter(从学术上讲,它甚至不是getter——它是一个fetcher)

关于getter的一些注意事项

在编写返回内容的方法时,人们经常使用get作为首选动词。然而,也有一个同样广泛的学派认为,getter应该仅限于返回的班级成员。在我工作的地方,我们的编码标准中有关于方法命名的这些行

  • 非属性getter或setter的方法应避免使用getset作为前缀
    • 可能的get替代方案:查找、获取、获取、定位、拉取、访问
    • 可能的设置替代方案:推送、应用、制作、张贴、标记、附加、分配

这意味着允许开发人员编写get*()方法,只返回类属性,但鼓励他们不要这样做

所以当我说"fetcher"时,这是一个口语术语,并不是常见软件工程术语的一部分。

更好的方法

这里有一个例子,我们应用了一些严格的关注点分离。用户数据的"获取"answers"获取"都有自己的实现,即自己的方法。

class User
{
  private $userData = null;
  /**
   * Lazy-load-style getter for $userData
   *
   * @return array
   */
  public function getUserData()
  {
    if (null === $this->userData)
    {
      $this->userData = $this->fetchUserData();
    }
    return $this->userData;
  }
  /**
   * Fetch the user's data from the database
   *
   * @return array Associative array of user data where the keys
   *               are column names
   */
  private function fetchUserData()
  {
    $stmt = $this->conn->prepare('SELECT * FROM user_table WHERE id = :id');
    $stmt->execute(array('id' => $this->ID));
    return $stmt->fetch(PDO::FETCH_ASSOC);
  }
}

尽管我建议在PDO::fetch()返回FALSE的情况下向User::fetchUserData()添加一个保护检查。也许类似

$user = $stmt->fetch(PDO::FETCH_ASSOC);
if (false === $user)
{
  throw new Exception("User with id ($this->ID) not found");
}

问题是您的$stmt->fetchAll(),在您的查询中,它确实意味着您试图获得一行。您需要将其替换为$stmt->fetch()

原因是在您的示例中,每行的$key是行索引,因此您的赋值语句是,对于行0:

$this->userData[0] = $val;

相反,您想做的是使用两个字段的值将值分配给数组(我猜):

$this->userData[$val['field1']] = $val['field2'];

或者,

$this->userData['username'] = $val['username'];

然而,真正的问题是,我认为,你试图获得一行,并提取一组结果——以及无意中对行进行迭代,而不是你认为的字段。

如果您正在寻找单个用户记录,那么您需要使用关联的单个fetch,而不是fetchAll:

class User {
public $userData;
function getUserData() {
    $stmt = $this->conn->prepare('SELECT * FROM user_table WHERE id = :id');
    $stmt->execute(array('id' => $this->ID));
    $this->userData = $stmt->fetch(PDO::FETCH_ASSOC);
    return $this->userData;
}