I am trying to call a function that is inside my Database class but I can't seem to get it right.
My Model class calls Database and assigns it to a variable:
class Model {
function __construct() {
$this->db = new Database();
}
function __destruct() {
$this->db = NULL;
}
}
I'm trying to call it from:
class User extends Controller {
function __construct() {
parent::__construct();
}
public function userExists($username) {
return $this->model->db->select('users', 'id', 'email LIKE ?', array($username));
}
}
But I keep getting the error:
Fatal error: Call to a member function select() on a non-object in C:\Apache24\htdocs\Manager 2014\controllers\User.php on line 10
My Database class is:
class Database extends PDO {
function __construct($user = 'root', $pass = '', $database = 'xxx', $host = 'localhost') {
try {
parent::__construct('mysql:host=' . $host . ';dbname=' . $database, $user, $pass);
$this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
$this->setAttribute(PDO::ATTR_PERSISTENT, FALSE);
$this->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
$this->setAttribute(PDO::ATTR_EMULATE_PREPARES, FALSE);
$this->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, FALSE);
$this->exec("SET NAMES utf8");
} catch (PDOException $e) {
Database::handleDbError($e);
}
}
public function select($table, $cols, $where = null, $whereVals = null, $order = null) {
if ($this->tableExists($table)) {
$PDOParams = array();
$SQL = 'SELECT ' . $cols . ' FROM ' . $table;
if ($where) {
$SQL .= ' WHERE ' . $where;
}
if ($whereVals) {
}
if ($order) {
$SQL .= 'ORDER BY ' . $order;
}
return $SQL;
} else {
}
}
public function tableExists($table) {
$r = $this->PDO->prepare("SHOW TABLES LIKE ?");
$r->execute(array($table));
return $r->rowCount();
}
}
What am I doing wrong?
If I:
print_r(get_class_vars ('UserModel'));
From the User class I get:
Array( [db] => )
Which doesn't make sense to me because that is being assigned in Model in the constructor that is being called when UserModel is called.
Instead of select() in my Database class, I now have the following in my main Model class:
public function get($primaryKeyId) {
$table = str_replace('model', '', strtolower(get_called_class()));
$SQL = "SELECT * FROM $table WHERE id = ?";
$r = $this->db->prepare($SQL);
$r->execute(array($primaryKeyId));
return $r->fetch();
}
There are many essential flaws with your implementation.
First and foremost, get rid of select() function
it's awfully insecure, allowing raw variables into query
it's fantastically inflexible, allowing only silly sandbox queries
it makes unreadable gibberish out of neat almost natural English of SQL
Second, do not create anew DB instance every time you invoke a class. Even simple app will kill your db server with that much number of connects.
pass db as a parameter into constructor instead
As it seems from your comment, errorhandler method doesn't handle anything which makes it evil
if you have no idea how to handle an error - just let it go. Get rid of this try/catch thingy completely
The database object in your model needs to be public so it can be accessed outside of the Model class.
class Model {
public $db;
function __construct() {
$this->db = new Database();
}
function __destruct() {
$this->db = NULL;
}
}
See object visibility for details on the difference between public, protected, and private.
I had just done a custom build of AMP and forgot to enable mysql PDO extension...That's why the DB parameter was empty FACEPALM.
Related
I want to access a connexion database through PDO in a static way but I am still getting an error. This is my code :
require_once 'include/class.Settings.php';
class MonthlySums{
protected static $dbh;
public function __construct() {
$user = Settings::$db['user'];
$pass = Settings::$db['password'];
$dsn = 'mysql:host=' . Settings::$db['host'] . ';dbname=' . Settings::$db['db'];
try {
self::$dbh = new PDO($dsn, $user, $pass);
} catch(PDOException $e) {
die("Error! : " . $e->getMessage());
}
}
public static function get($init_id) {
$sql = "SELECT `year`, `month`, `gains_monthly_sum` FROM `fxs_gain_sums` WHERE `init_id` = '$init_id'";
$resultats = MonthlySums::$dbh->query($sql);
var_dump($resultats);
}
}
I have this error :
Fatal error: Call to a member function query() on a non-object in /home/public_html/gainlosssums.php on line 45
That line is : $resultats = MonthlySums::$dbh->query($sql);
How to make it work a static way ?
When you call a static method in a class, the constructor is not automatically invoked like when you create an instance of the class. With your code you would have to create at least one instance of the class so that the database connection would be set in your static variable. Then you would be able to call it in your desired static way.
If you still want to continue with your code, you could change it to this:
(Please note: this is the OP's modified code and I am not going into other aspects - just the result to the OP's question/problem)
require_once 'include/class.Settings.php';
class MonthlySums{
protected static $dbh;
public function __construct() {
$this->databaseLogin();
}
public static function databaseLogin(){
$user = Settings::$db['user'];
$pass = Settings::$db['password'];
$dsn = 'mysql:host=' . Settings::$db['host'] . ';dbname=' . Settings::$db['db'];
try {
self::$dbh = new PDO($dsn, $user, $pass);
} catch(PDOException $e) {
die("Error! : " . $e->getMessage());
}
}
public static function get($init_id) {
$sql = "SELECT `year`, `month`, `gains_monthly_sum` FROM `fxs_gain_sums` WHERE `init_id` = '$init_id'";
$resultats = self::$dbh->query($sql);
var_dump($resultats);
}
}
MonthlySums::databaseLogin();
As you can see there is a new static method databaseLogin and is called right after the class declaration. This way the static variable for the database connection is set and can be used in other methods.
You now can call it this way:
MonthlySums::get(1234);
Again, this might not be best practice but a solution to your problem.
Im gonna edit the question to make it clearer, so you can see what i have got now, and get an easier understanding of the problem.
<?php
$mysqli = new mysqli("localhost", "user", "password", "test");
class building
{
private $mysqli;
public $buildingid;
public $userid;
public $buildinglevel;
public function __construct($buildingid, $userid, \mysqli $mysqli)
{
$this->buildinglevel;
$this->mysqli = $mysqli;
}
public function getLevel()
{
return $this->mysqli->query("SELECT ".$this->buildingid." FROM worlds WHERE city_userid=".$this->userid."");
}
}
}
?>
Then I use this to create and use the function:
$cityHall = new building("cityHall",$user['id'],$mysqli);
echo $cityHall->getLevel();
This turns out blank, and nothing happens.
You should inject instance of mysqli to __construct() of building class:
$mysqli = new mysqli('user', 'password', 'localhost', 'test');
if ($mysqli->connect_errno) {
printf("Connect failed: %s\n", $mysqli->connect_error);
}
class building
{
private $mysql;
private $buildingid;
private $userid;
// I need to have a mysqli_query here to get the info for the correct building,
//to be able to set the "buildinglevel" for each object from the MYSQL DB, seems basic
//but none of the things ive tried has worked.
public function __construct($buildingid, $userid, $mysqli)
{
$this->buildinglevel;
$this->mysqli = $mysqli;
$this->userid = (int)$userid;
$this->buildingid= (int)$buildingid;
}
public function getLevel()
{
$query = $this->mysqli->query("SELECT ".$this->buildingid." FROM worlds WHERE city_userid=".$this->userid);
$row = $query->fetch_assoc();
if (!$query) {
return $this->mysqli->error;
}
if ($query->num_rows == 0) {
return 'no database records found';
}
return $row;
}
}
$Bulding = new building("cityHall", $user['id'], $mysqli);
$level = $Bulding->getLevel();
var_dump($level);
Objects are units that encapsulate behavior which is exposed to other objects via methods. A wrapper around public properties retrieved from the DB does not object-oriented programming make. In fact, mysqli can do this for you via fetch_object:
$result = $mysqli->query($query);
while ($building = $result->fetch_object()) {
// access properties via $building->buildingid, etc.
}
Unless the building class actually offers functionality via methods, and implements some abstraction, it's not needed. What you can have instead is a DAO (Data Access Object) that wraps the DB (mysqli) and the data it retrieves is used by your model.
interface Dao {
public function get($id);
}
class BuildingDao implements Dao {
// DB can be a wrapper for mysqli
// but servers as an interface so it
// can be replaced easily
public function __construct(DB $db) {
$this->db = $db;
}
public function get($id) {
return $this->db->prepare(
"SELECT buildinglevel FROM building WHERE buildingid = ?"
)->execute($id)->fetch();
}
}
What your class there seems to be, is what’s known as a model: it represents some form of data, in your case a particular building.
One approach is to pass the MySQLi object as a constructor object, as well as the ID of the building you’re wanting to query for, and assign the result to class properties. This would look as follows:
<?php
class Building
{
private $db;
protected $id;
protected $level;
public function __construct(mysqli $db, $id = null)
{
$this->db = $db;
if (!is_null($id) && intval($id) > 0) {
$stmt = $this->db->prepare("SELECT buildingid, buildinglevel FROM building WHERE `id` = ?");
$stmt->bind_param('i', $id);
$stmt->execute();
$stmt->bind_result($this->id, $this->level);
$stmt->fetch();
}
}
public function getId()
{
return (int)$this->id;
}
public function getLevel()
{
return (int)$this->level;
}
}
You can then fetch your building’s properties like this:
$building = new Building($mysqli, 1);
printf('Building ID is %d and building level is %d', $building->getId(), $building->getLevel());
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Reference - What does this error mean in PHP?
I have two classes:
Database Class
Libtables Class
In the Database Class I've created an object to work with mysqli for the entire script. It works when I'm trying to call a function from the Database Class in a .php file.
Simplified Database Class:
class Database {
protected static $_instance;
protected $_mysqli;
protected $_query;
public function __construct($host, $username, $password, $db) {
$this->_mysqli = new mysqli($host, $username, $password, $db)
or die("There was a problem connecting to the database");
}
public function close() {
$this->_mysqli->close();
}
public function query($q) {
return $this->_mysqli->query($q);
}
}
But when I'm trying to call a function from the Database Class in the Libtables Class it fails and I get an error called:
"Call to a member function query() on a non-object"
Simplified Libtables Class:
class Libtables {
function getCol($table) {
$q = "SELECT * from " . $table . ";";
$res = $db->query($q);
return $res;
}
}
I've created a Database Class Object this way:
$db = new Database(DB_HOST, DB_USER, DB_PASS, DB_NAME);
global $db;
You need global $db; in your getCol function.
function getCol($table) {
global $db;
$q = "SELECT * from " . $table . ";";
$res = $db->query($q);
return $res;
}
Edit based on comment below.
Another option is to store the DB as a property in the class. The following a a simplified response. (You should look at making the var $db private and passing in through a constructor. See the documentation on variable scope and object constructors for more details.)
class Libtables {
var $db;
function getCol($table) {
$q = "SELECT * from " . $table . ";";
$res = $this->db->query($q);
return $res;
}
}
$oLibtables = new libtables();
$oLibtables->db = $db;
The issue you are experiencing has to do with scope. The $db variable is not initialized within the Libtables class.
You can go about this either by defining the $db variable global in the function you need to use it i.e.
class Libtables
{
public function getCol($table)
{
global $db;
$q = "SELECT * from " . $table . ";";
$res = $db->query($q);
return $res;
}
}
Or you could inject the variable through the class constructor so that you can use it everywhere (a bit cleaner code than using globals).
class Libtables
{
private $_db;
public function __construct($db)
{
$this->_db = $db;
}
public function getCol($table)
{
$q = "SELECT * from " . $table . ";";
$res = $this->_db->query($q);
return $res;
}
}
Creating Libtables
$db = new Database(DB_HOST, DB_USER, DB_PASS, DB_NAME);
.....
$lib = new Libtables($db);
HTH
Thanks everybody. It works now with passing the Database object through to the libtables Class.
I am learning OOPS concept and also using PDO , but really stuck in very basic problem, please look into my code and tell me what i am doing wrong and how to optimize this code.
class.db.php
class DB
{
public $dbh;
private $qry;
public function __construct($user ='root',$pass = '')
{
try {
$this->dbh = new PDO('mysql:host=localhost;dbname=parixan',$user,$pass);
$this->dbh->exec("SET CHARACTER SET utf8");
echo "connected";
} catch(Exception $e){
die("Unable to connect: " . $e->getMessage());
}
}
}
class.user.php
class User
{
public $db;
protected $_table ='tbl_user';
public function __construct($dbh)
{
$this->db = $dbh;
}
public function getUserDetails($id) //line #10
{
$qry = "SELECT * FROM $this->_table WHERE id = :userID LIMIT 1";
$stmt = $this->db->prepare($qry); //line #13
$stmt->bindParam(':userID',$id,PDO::PARAM_INT);
if ($stmt->execute()) {
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo "<pre>";print_r($row);
}
}
}
}
user.php
include_once('class.user.php');
include_once('class.db.php');
$dbh = new DB();
$obj_user = new User($dbh);
$obj_user ->getUserDetails(1);
on running user.php I got this one
connected Fatal error: Call to undefined method DB::prepare() in
C:\xampp\htdocs\oops\class.user.php on line 13
Thanks.
Within the User object, $this->db is a reference to an object of the DB class. This class does not implement a prepare method, so there is none available when you attempt to call it with $stmt = $this->db->prepare($qry);.
If you persist with your current class design, you need to create such a function:
public function prepare($qry) {
return $this->dbh->prepare($qry);
}
This means you will now have a PDOStatement object, on which you can call bindParam and execute.
Well I tried to extend the class mysqli with its inheritance called Database, which can retrieve database info easily(in one line if done correctly). It does not work out so far, as I am getting the following errors:
Here is the Database Class as defined:
class Database extends mysqli{
private $select, $create, $insert, $alter, $update, $delete, $drop;
protected $mysql, $result, $table, $column, $where, $value, $limit, $order, $type;
public function __construct($host, $user, $pass, $db){
$this->mysql = new mysqli($host, $user, $pass, $db)
or die("Error connecting to the database {$db}");
}
public function __destruct(){
$this->mysql->close();
}
protected function prepareQuery(){
if ($prepare = $this->mysqli->prepare($this->query)) {
trigger_error("Problem preparing query ($this->query) ".$this->mysqli->error, E_USER_ERROR);
}
return $prepare;
}
protected function reset(){
unset($this->table);
unset($this->column);
unset($this->where);
unset($this->value);
unset($this->limit);
unset($this->order);
}
public function select($column){
$this->select = implode(",", $column);
return $this;
}
public function table($table){
$this->table = $table;
return $this;
}
public function where($where, $comparison = "=", $logic){
$i = 0;
foreach ($where as $col => $val){
$wherestring .= (is_array($comparison)) ? " {$col} {$comparison[$i]} '{$val}'" : " WHERE {$col} {$comparison} '{$val}'";
$wherestring .= ($i < (count($where)-1))?" {$logic[$i]}" :" ";
$i++;
}
$this->where = $wherestring;
return $this;
}
public function limit($limit){
$this->limit = $limit;
return $this;
}
public function order($order){
$this->order = $order;
return $this;
}
public function runquery($method){
$query = "{$method} {$this->select} FROM {$this->table}";
if(!empty($this->where)) $query .= " WHERE {$this->where}";
if(!empty($this->limit)) $query .= " LIMIT {$this->limit}";
if(!empty($this->order)) $query .= " ORDER BY {$this->order}";
echo "The generated Query is: \n".$query;
$this->result = parent::query($query);
$result = parent::fetch_array($this->result);
return $result;
}
}
And this is the way I run it from a script file:
include("inc/config.php");
include("classes/class_data.php");
$db = new Database($dbhost, $dbuser, $dbpass, $dbname);
$row = $db->select(array("password","email"))->table($prefix."users")->where(array("uid"=>1, "username"=>Admin"),array("=","="),array("AND"))->limit(2)->order("uid")->runquery("SELECT");
It did not work, and I got the following warning and error messages:
Code:
Warning: mysqli::query() [mysqli.query]: Couldn't fetch Database in classes/class_data.php on line 70
Fatal error: Call to undefined method mysqli::fetch_array() inclass_data.php on line 71
I am a bit confused now since I cant seem to figure out a way to resolve this problem. Can anyone of you please help? Id appreciate it very much.
Thank you
fecth_array() is not the method of mysqli but the MySQLi_STMT class.
And your should not design like that, you are doing thing that extend mysqli, but you also do $this->mysql = new mysqli(...), between is-A and has-A, you should just choose one. I recommend has-A.
In addition to what xdazz said, you are also mixing the “inherits from” and the “uses” patterns: you have a mysqli instance named $this->mysql as well as $this, inherited from the parent:: mysqli. You should decide on either one but not use both. If you want the user of your class to be able to to do anything with it that she can to with a normal mysqli instance (including calling all the superclass methods herself), use inheritance. If you want to restrict the use cases, use a mysqli type instance variable and remove the inheritance. You can then selectively proxy methods of the inner $this->mysql to allow users to call them.