public function getUser(string $username = null) : array { if (!$this->user) { if (!$username) { throw new \RuntimeException('No user was authenticated yet, the username parameter is mandatory'); } // IDEA separate into AuthDao / UsersDao, both to clean things up // and to show an example of how to work with a child of Dao $data = Database::run('SELECT id, username, email, password FROM users WHERE username = ?', [$username], [Col::INT()]); if (!$data) { return $res->build(HttpStatus::NotFound, [], ['title' => 'User not found', 'detail' => 'No user exists with the provided username']); } $this->user = $data[0]; } return $this->user; }
/** * Simple wrapper for running database queries. * * @param $q Database query to be run, as a string. Alternatively, an * already-prepared statement can be provided, so that the preparation * can be done simply once. * @param $params List of parameter values. Optionally, an associative * element can be provided, whose key would indicate the type (given as * one of Col's functions), and whose value would be the actual value of * the parameter, overriding the auto-detection. * @param $colTypes List of types in the form of a Col function, which may * be needed for SELECT statements. * * NOTE run() relies on PDOStatement::getColumnMeta() * (http://php.net/manual/en/pdostatement.getcolumnmeta.php) to simplify * the queries, which is marked as EXPERIMENTAL; thus, please verify it * is working in your particular conditions before using. */ public function run($q, array $params = [], array $colTypes = []) { $isSelect = $this->isSelect($q); if (!$isSelect && $colTypes) { throw new \InvalidArgumentException('There is no point on providing column types in non-SELECT statements'); } if (is_string($q)) { $stmt = $this->lastStmt = $this->get()->prepare($q); } else { if ($q instanceof \PDOStatement) { $stmt = $this->lastStmt = $q; } else { throw new \InvalidArgumentException('The first parameter must be either a query string or a prepared statement'); } } $i = 1; foreach ($params as $key => $val) { $stmt->bindParam($i++, $val, is_string($key) ? Col::getPdoType($key) : $this->getPdoType($val)); } $stmt->execute(); $colsCount = count($colTypes); if ($isSelect) { if ($colsCount === 0) { return $stmt->fetchAll(); } $res = []; $meta = []; $types = []; for ($i = 0; $i < $colsCount; ++$i) { $meta[] = $stmt->getColumnMeta($i); $types[] = $colTypes[$i] ? Col::getPdoType($colTypes[$i]) : ($meta[$i]['pdo_type'] ?: \PDO::PARAM_STR); // First binding, to avoid ending up with a null element $stmt->bindColumn($i + 1, $res[$meta[$i]['name']], $types[$i]); } $data = []; // IDEA try to make it work with $stmt->fetchAll(\PDO::FETCH_BOUND); while ($stmt->fetch(\PDO::FETCH_BOUND)) { $data[] = $res; // Break the references, otherwise all the elements will point // to the same object, and therefore contain the same data $res = null; // Bind the next result, in case there is any more data for ($i = 0; $i < $colsCount; ++$i) { $stmt->bindColumn($i + 1, $res[$meta[$i]['name']], $types[$i]); } } return $data; } return $this->rowCount(); }
public function deleteOne(int $id) : int { return $this->db->run('delete from ' . $this->tableName . ' where ' . $this->idName . ' = ?', [Col::INT() => $id]); }