Jump to content

Undefined type on Database class


TechnoDiver
 Share

Recommended Posts

As it's been recommended numerous times I've started using/learning PDO. So starting from the beginning I've decided to start refactoring my project using it. Attempting to write a database class has got me stuck and I don't know why. In the following code I'm going to leave in my former, commented out code as well. This is the change I've made to my config file which is located (I'm using XAMPP) in /opt/lampp. Outside my root directory. The commented out code works fine, so this isn't an issue of an improper path name

<?php
// ob_start();
// session_start();

// $timezone = date_default_timezone_set("America/Cancun");
// $whitelist = array('username', 'email', 'email2', 'password', 'password2');
// $table = 'users';
// $conn = mysqli_connect('localhost', 'root', '', 'qcic');

// if($conn->connect_errno) {
//     printf("Connect Failed: %s\n", $conn->connect_error);
//     exit();
// }

// ob_end_flush();

class Database {

    private $dbhost = "localhost";
    private $dbuser = "root";
    private $dbpassword = "";
    private $dbname = "qcic";
    private $conn;

    public function __construct() {
        try {
            $dsn = "mysql:host=" . $this->dbhost . ";dbname=" . $this->dbname;
            $this->conn = new PDO($dsn, $this->dbuser, $this->dbpassword);
        } catch(PDOException $exception) {
            die("DB connection failed: " . $exception->getMessage());
        }
    }     
}

I call the Database class here, in a file named load.php ->

<?php 
require_once($_SERVER['DOCUMENT_ROOT'] . '/../config/config.php'); 
$db_obj = new Database();

The issue is that Database is coming back as "undefined type 'Database'".

The require_once path is definitely the right path. Can someone tell me what the issue is, please?

Link to comment
Share on other sites

27 minutes ago, ginerjm said:

I think you missed something in whatever sample you copied this from.   One has to return something to finish the construction.

I caught that while messing around with it earlier I've modified it to this ->

class Database {

        private $dbhost = "localhost";
        private $dbuser = "root";
        private $dbpassword = "";
        private $dbname = "qcic";
        public $conn;

        public function __construct() {
            try {
                $dsn = "mysql:host=" . $this->dbhost . ";dbname=" . $this->dbname;
                $this->conn = new PDO($dsn, $this->dbuser, $this->dbpassword);
            } catch(PDOException $exception) {
                die("DB connection failed: " . $exception->getMessage());
            }

            $conn = $this->conn;
            return $conn;
        }     
    }

but it still doesn't work, any idea why??

Link to comment
Share on other sites

48 minutes ago, TechnoDiver said:

The require_once path is definitely the right path.

You might want to check again. Your code works fine when all in one file, EG

class Database {

    private $dbhost = "localhost";
    private $dbuser = "*****";
    private $dbpassword = "*****";
    private $dbname = "test";
    private $conn;

    public function __construct() {
        try {
            $dsn = "mysql:host=" . $this->dbhost . ";dbname=" . $this->dbname;
            $this->conn = new PDO($dsn, $this->dbuser, $this->dbpassword);
        } catch(PDOException $exception) {
            die("DB connection failed: " . $exception->getMessage());
        }
    }     
}

$db_obj = new Database();

 

Link to comment
Share on other sites

2 minutes ago, Barand said:

You might want to check again. Your code works fine when all in one file, EG

Yea, I've tried that and don't get the type error either, but with that amended code returning $conn now the other classes aren't picking up the $conn anymore.

But like I said, the commented out code in my first OP works, and I haven't changed the location of anything, just commented out one section and wrote another. That's why I'm sure it's correct. Unless I'm overlooking something.

the path to config.php is /opt/lampp/config/config.php

and the path to load.php is /opt/lampp/htdocs/site/load.php. I didn't just make this file today, I've been using it for months on that path name

But something is beyond my understanding because returning $conn isn't connecting to the other classes. I don't know why putting pretty much the same code into a class has caused so much chaos

 

Link to comment
Share on other sites

So, I've thought about this, I really really hope to receive some input. This is my cleaned up config.php ->

<?php

class Database {
    private $dbhost = "localhost";
    private $dbuser = "root";
    private $dbpassword = "";
    private $dbname = "qcic";
    public $conn;

    public function __construct() {
        try {
            $dsn = "mysql:host=" . $this->dbhost . ";dbname=" . $this->dbname;
            $this->conn = new PDO($dsn, $this->dbuser, $this->dbpassword);
        } catch(PDOException $exception) {
            die("DB connection failed: " . $exception->getMessage());
        }

        // $conn = $this->conn;
        return $this->conn;
    }     
}

ob_start();
session_start();

$timezone = date_default_timezone_set("America/Cancun");
$whitelist = array('username', 'email', 'email2', 'password', 'password2');
$table = 'users';
$conn = new Database();
ob_end_flush();

 

I'm still getting that $conn is unassigned from my other classes. Could this be because the db queries in those classes are in mysql and the connection was made in PDO?

Link to comment
Share on other sites

1 minute ago, ginerjm said:

How are YOU connecting $conn to another class?  Are you passing it in your calls to their methods or setting a class property?

class property ->

<?php 

    class User {

        private $conn;
        private $username;

        public function __construct($conn, $username) {
            $this->conn = $conn;
            $query = mysqli_query($this->conn, "SELECT * FROM users WHERE username='$username'");
 		    return $this->user = mysqli_fetch_array($query);
        }
      
........

but these classes are in mysql and I made the connection in PDO. Would that be the cause??

Link to comment
Share on other sites

28 minutes ago, Barand said:

What are you passing to new User()? the $db_obj or the $db_obj->conn. (it would need to be public for that to work BTW)

I'm returning $this->conn like this

public function __construct() {
        try {
            $dsn = "mysql:host=" . $this->dbhost . ";dbname=" . $this->dbname;
            $this->conn = new PDO($dsn, $this->dbuser, $this->dbpassword);
        } catch(PDOException $exception) {
            die("DB connection failed: " . $exception->getMessage());
        }

        return $this->conn;
    }

and instantiating like this ->

ob_start();
session_start();

$timezone = date_default_timezone_set("America/Cancun");
$whitelist = array('username', 'email', 'email2', 'password', 'password2');
$table = 'users';
$conn = new Database();
ob_end_flush();

$this->conn needs to be made public?? I feel not, but I'm not sure I understood you

32 minutes ago, Barand said:

You can't use a mysqli_xxx() call with a PDO connection.

And there's also this issue, I wasn't sure about that. But I'll have to replace a lot of code before I'll know anything.

Link to comment
Share on other sites

Just now, TechnoDiver said:

I'm returning $this->conn like this

public function __construct() {
        try {
            $dsn = "mysql:host=" . $this->dbhost . ";dbname=" . $this->dbname;
            $this->conn = new PDO($dsn, $this->dbuser, $this->dbpassword);
        } catch(PDOException $exception) {
            die("DB connection failed: " . $exception->getMessage());
        }

        return $this->conn;
    }

and instantiating like this ->

ob_start();
session_start();

$timezone = date_default_timezone_set("America/Cancun");
$whitelist = array('username', 'email', 'email2', 'password', 'password2');
$table = 'users';
$conn = new Database();
ob_end_flush();

$this->conn needs to be made public even though it's the return value?? I feel not, but I'm not sure I understood you

And there's also this issue, I wasn't sure about that. But I'll have to replace a lot of code before I'll know anything.

 

Link to comment
Share on other sites

If you want to continue to use mysql along with pdo, you'll have to make both connections.  I suggest renaming your pdo connection to $pdo instead of $conn since you seem to be already using that var.  Something like:

$pdo = new Database();

Of course what is the point of trying to switch to PDO ( a good move BTW) if you are still going to use mysql code in other parts of your script?

Link to comment
Share on other sites

22 minutes ago, ginerjm said:

Of course what is the point of trying to switch to PDO ( a good move BTW) if you are still going to use mysql code in other parts of your script?

I'm not staying with mysql. I'm working on changing everything over right now, having some problems with it as you can see

<?php

class Database {
    private $dbhost = "localhost";
    private $dbuser = "root";
    private $dbpassword = "";
    private $dbname = "qcic";
    public $conn;

    public function __construct() {
        try {
            $dsn = "mysql:host=" . $this->dbhost . ";dbname=" . $this->dbname;
            $this->conn = new PDO($dsn, $this->dbuser, $this->dbpassword);
        } catch(PDOException $exception) {
            die("DB connection failed: " . $exception->getMessage());
        }

        return $this->conn;
    }     
}

ob_start();
session_start();

$timezone = date_default_timezone_set("America/Cancun");
$whitelist = array('username', 'email', 'email2', 'password', 'password2');
$table = 'users';
$conn = new Database();
ob_end_flush();

This is my DB code in config.php. The following is my __construct code for User.php

<?php 
    class User {
        private $conn;
        private $username;

        public function __construct($conn, $username) {
            $this->conn = $conn;
            $query = "SELECT * FROM users WHERE username='$username'";
 		    $statement = $this->conn->prepare($query);
            $statement->execute();
            $username = $statement->fetchAll(PDO::FETCH_ASSOC);
            return $username;

        }

and it's returning this error ->

Quote

Fatal error: Uncaught Error: Call to undefined method Database::prepare() in /opt/lampp/htdocs/qcic/assets/class/User.php:11 Stack trace: #0 /opt/lampp/htdocs/qcic/assets/class/User.php(137): User->__construct(Object(Database), 'TechnoDiver') #1 /opt/lampp/htdocs/qcic/newsnet/assets/initializations.php(3): require('/opt/lampp/htdo...') #2 /opt/lampp/htdocs/qcic/newsnet/index.php(1): require('/opt/lampp/htdo...') #3 {main} thrown in /opt/lampp/htdocs/qcic/assets/class/User.php on line 11

line 11 that it refers to is ->

$statement = $this->conn->prepare($query);

I'm not exactly sure how to read this message but it seems to me that the Database object is being passed in __construct rather than the $conn value that it returns (that I thought it returned). I'm not sure if that's the case, I have no idea. I thought I understood it better and am really happy I committed everything before starting these changes, because I've made a mess of things. Why isn't the $conn variable passing to these other classes, it's public??

Edited by TechnoDiver
corrected mistake
Link to comment
Share on other sites

6 minutes ago, Barand said:

The question was about how you instantiate the user object

ah, ok, so it's like this for now. because I haven't finished the login and I needed to switch between users with different roles. This will obviously change eventually. ATM it's at the bottom of the User.php class page

$userindex = 0;
$userarray = array("TechnoDiver", "jwick", "ChesterCheetah", "AfghanStan");
$username = $userarray[$userindex];
$user_obj = new User($conn, $username); 

again, this was working fine until I started trying to put config.php in a class. That's where everything went pear shaped

 

EDIT: the User class constructor was in mysql and I really thought when I changed it things would work but it seems I haven't understood something. Because I feel it's not reading the $conn variable in config.php. Again this is a direct result of the database class because $conn was passing fine earlier too.

Edited by TechnoDiver
Link to comment
Share on other sites

4 hours ago, ginerjm said:

One has to return something to finish the construction

In fact, you cannot return something from a __construct function.  The return value is automatically the newly created object.

 

1 hour ago, TechnoDiver said:

I'm not exactly sure how to read this message but it seems to me that the Database object is being passed in __construct rather than the $conn value that it returns (that I thought it returned).

When you do $conn = new Database() then $conn is an instance of your Database class.  You can't return some other value from the constructor like with other functions.   Your PDO connection would then be $conn->conn.

I would suggest for learning PDO that you drop the entire Database class and just deal with PDO directly.  Once your more familiar with PDO and OOP in general you can look into creating a database class if you find a need for one.  The PDO is pretty decent already, there's not a lot of benefit to creating your own class on top of it.

 

Link to comment
Share on other sites

9 minutes ago, kicken said:

I would suggest for learning PDO that you drop the entire Database class and just deal with PDO directly.  Once your more familiar with PDO and OOP in general you can look into creating a database class if you find a need for one.  The PDO is pretty decent already, there's not a lot of benefit to creating your own class on top of it.

So, I actually just started learning PDO today because it's been recommended so many times before. I thought that I finally had a good enough grasp of PHP to leave the MYSQL that I started with. I've written many working classes so far but have refrained from passing too much data directly between them. All this worked great until I started the database class but it was the first thing the tutorial I was watching started with so I decided to give a shot at applying it to my present project

Edited by TechnoDiver
Link to comment
Share on other sites

42 minutes ago, Barand said:

How would you code this sequence?

  1. Create Database object
  2. Create User object passing the database connection and username.

I've put mine in my config file because it's outside of my root directory ( the Database class). It passes the required data (localhost, db name, password, username) and returns the connection data. Instantiate the Class

$conn = new Database();

and that contains the connection data ($this->conn) from ->

class Database {
    private $dbhost = "localhost";
    private $dbuser = "root";
    private $dbpassword = "";
    private $dbname = "qcic";
    public $conn;

    public function __construct() {
        try {
            $dsn = "mysql:host=" . $this->dbhost . ";dbname=" . $this->dbname;
            $this->conn = new PDO($dsn, $this->dbuser, $this->dbpassword);
        } catch(PDOException $exception) {
            die("DB connection failed: " . $exception->getMessage());
        }

        return $this->conn;
    }     
}

That then is loaded by load.php ->

<?php require_once($_SERVER['DOCUMENT_ROOT'] . '/../config/config.php');

and how I had it before this fiasco we're talking about now is that all is passed into initializations.php ->

<?php
require("../load.php");
require("../assets/class/User.php");
require("../assets/class/Category.php");
require("../assets/class/Post.php");
require("../assets/class/Comment.php");
require("../assets/class/Image.php");

// if(session_status() === PHP_SESSION_NONE) {
//     $username = "";
// } else {
//     $username = $_SESSION['username'];
    
// }

$user_obj = new User($conn, $username);
$username = $user_obj->getUsername();
$role = $user_obj->getRole();

$cat_obj = new Category($conn, $username);

Which is required at the top of every page in the site. I hope that answers your question, I really appreciate your responses, I'm lost right now. This makes sense to me and worked great until today. I just can't wrap my head around what is happening to that $conn

 

EDIT: and now I'm trying to get my head around kicken saying that constructors can't return values and trying to figure out how to acces $this-conn in the instantiation AND I'm not even sure if that's the issue, lol

Edited by TechnoDiver
Link to comment
Share on other sites

I would suggest using namespaces and an autoloader using composer. A good link in my opinion is this one - https://phpenthusiast.com/blog/how-to-autoload-with-composer and it really help me understand on how to do it.

 

Then you simply can do this :

<?php
require_once 'assets/config/config.php'; // You still need your trusty configuration file
require_once "vendor/autoload.php"; // Autoloader using composer

use PhotoTech\CMS;
use PhotoTech\Pagination;

Here's my Database Class:

<?php


namespace PhotoTech;

use PDO;
class Database {

    private PDO $_connection;
    // Store the single instance.
    private static ?Database $_instance = null; // Don't initialize before it is called:

    // Get an instance of the Database.
    // @return Database:
    protected static function getInstance(): Database
    {
        if (!self::$_instance) {
            self::$_instance = new self();
        }
        return self::$_instance;
    }

    public static function pdo(): PDO
    {
        $db = static::getInstance();
        return $db->getConnection();
    }

    // Constructor - Build the PDO Connection:
    public function __construct() {
        $db_options = array(
            /* important! use actual prepared statements (default: emulate prepared statements) */
            PDO::ATTR_EMULATE_PREPARES => false
            /* throw exceptions on errors (default: stay silent) */
        , PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
            /* fetch associative arrays (default: mixed arrays)    */
        , PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
        );
        $this->_connection = new PDO('mysql:host=' . DATABASE_HOST . ';dbname=' . DATABASE_NAME . ';charset=utf8', DATABASE_USERNAME, DATABASE_PASSWORD, $db_options);
    }

    // Empty clone magic method to prevent duplication:
    private function __clone() {

    }

    // Get the PDO connection:
    protected function getConnection(): PDO
    {
        return $this->_connection;
    }

}

I have been using it for sometime without any problems

and to use it I simply do the following

 

class DatabaseObject // Extended by the children class:
{
    static protected string $table = ""; // Overridden by the calling class:
    static protected array $db_columns = []; // Overridden by the calling class:
    static protected array $objects = [];
    static protected array $params = [];
    static protected $searchItem;
    static protected $searchValue;

    /*
     * There is NO read() method this fetch_all method
     *  basically does the same thing. The query ($sql)
     *  is done in the class the calls this method.
     */
    public static function fetch_by_column_name($sql)
    {
        $stmt = Database::pdo()->prepare($sql); // Database::pdo() is the PDO Connection 

        $stmt->execute([ static::$searchItem => static::$searchValue ]);
        return $stmt->fetch(PDO::FETCH_ASSOC);
    }

    // More coding...

I should mention I am using PHP 8, so there might be some adjustment that might be needed?  I also make comments that are sometime mental notes to me in order to know what I was doing. 😉

Link to comment
Share on other sites

35 minutes ago, TechnoDiver said:

EDIT: and now I'm trying to get my head around kicken saying that constructors can't return values and trying to figure out how to acces $this-conn in the instantiation AND I'm not even sure if that's the issue, lol

When you have code like:

$conn = new Database();

then $conn will be an instance of your Database class, with it's properties and methods.  You can assign properties and call methods and such in the constructor, but you cannot return a value to be assigned to $conn.

Inside your constructor and methods $this is a reference to the current instance of the class that is being operated on, so when you do:

$this->conn = new PDO(...);

You're assigning the instance of PDO that you create to the conn property of your Database class.  To access if from code outside of the Database class (which requires the property to be public), you'd use $conn->conn

You might be getting confused by the use of the name conn everywhere, so maybe rename the property to something else like pdo.

class Database {
    private $dbhost = "localhost";
    private $dbuser = "root";
    private $dbpassword = "";
    private $dbname = "qcic";
    public $pdo;

    public function __construct() {
        $dsn = "mysql:host=" . $this->dbhost . ";dbname=" . $this->dbname;
        $this->pdo = new PDO($dsn, $this->dbuser, $this->dbpassword);
    }
}

Don't bother catching the exception here, especially if you're just going to kill the script.  The default action for an exception is for PHP to kill the script.  If you want to handle it in some way, do it where you initiate your Database class.

Now, in the rest of your code, you'd initialize your class with new Database() and access the pdo property to work with your PDO connection.

$conn = new Database();
$stmt = $conn->pdo->prepare('SELECT * FROM ...');
...

For your other classes, you need to decide then if you want to pass your Database object ($conn) or your PDO connection ($conn->pdo)  to their constructors and go from there.

Link to comment
Share on other sites

45 minutes ago, kicken said:

You're assigning the instance of PDO that you create to the conn property of your Database class.  To access if from code outside of the Database class (which requires the property to be public), you'd use $conn->conn

ok, I get it, I think but from what you're saying this

<?php

class Database {
    private $dbhost = "localhost";
    private $dbuser = "root";
    private $dbpassword = "";
    private $dbname = "qcic";
    public $conn;

    public function __construct() {
          $dsn = "mysql:host=" . $this->dbhost . ";dbname=" . $this->dbname;
          $this->conn = new PDO($dsn, $this->dbuser, $this->dbpassword);
    }     
}

ob_start();
session_start();

$timezone = date_default_timezone_set("America/Cancun");
$whitelist = array('username', 'email', 'email2', 'password', 'password2');
$conn->conn; //this bit here
ob_end_flush();

Should get that $conn variable assigned and I can't get that damn $this->conn variable assigned to $conn for anything that I do. I'm stupified, I feel like everything I thought I understood is shattered. Why isn't $this->conn passing to $conn??

I need a spoiler or something lol, I'm lost

Edited by TechnoDiver
code edit
Link to comment
Share on other sites

11 minutes ago, kicken said:

You're missing a

$conn = new Database();

HaHa, that's funny because it brings us back to how I originally coded it which gave the error message that started this thread, and the roots of my confusion because this makes sense to me but doesn't work. I get

Quote

Fatal error: Uncaught Error: Call to undefined method Database::prepare() in /opt/lampp/htdocs/qcic/assets/class/User.php:10 Stack trace: #0 /opt/lampp/htdocs/qcic/assets/class/User.php(135): User->__construct(Object(Database), 'TechnoDiver') #1 /opt/lampp/htdocs/qcic/newsnet/assets/initializations.php(3): require('/opt/lampp/htdo...') #2 /opt/lampp/htdocs/qcic/newsnet/index.php(1): require('/opt/lampp/htdo...') #3 {main} thrown in /opt/lampp/htdocs/qcic/assets/class/User.php on line 10

from

<?php 

    class User {
        private $conn;
        private $username;

        public function __construct($conn, $username) {
            $this->conn = $conn;
            $query = "SELECT * FROM users WHERE username='$username'";
 		    $statement = $this->conn->prepare($query);
            $statement->execute();
            $username = $statement->fetchAll(PDO::FETCH_ASSOC);

        }
...........

and we come full circle. I'm completely lost

I tried this too

$conn = new Database();
$conn->conn;

I feel cursed

Link to comment
Share on other sites

The line

$conn->conn;

by itself does absolutely nothing.

Fatal error: Uncaught Error: Call to undefined method Database::prepare() in /opt/lampp/htdocs/qcic/assets/class/User.php:10 
------------------------------------------------------^^^^^^^^^^
Stack trace: 
#0 /opt/lampp/htdocs/qcic/assets/class/User.php(135): User->__construct(Object(Database), 'TechnoDiver') 
------------------------------------------------------------------------^^^^^^^^^^^^^^^^
#1 /opt/lampp/htdocs/qcic/newsnet/assets/initializations.php(3): require('/opt/lampp/htdo...') 
#2 /opt/lampp/htdocs/qcic/newsnet/index.php(1): require('/opt/lampp/htdo...') 
#3 {main} thrown in /opt/lampp/htdocs/qcic/assets/class/User.php on line 10

Your error is because your passing $conn to your user class, which as has been said several times is an instance of your Database class.  See the areas of the error I pointed to.  Your database class doesn't have a prepare method, that's why you get an error.

Your User class code is expecting to receive an instance of the PDO class.  That exists as the conn property on your Database class, referenced by $conn->conn.

$conn = new Database();
$user = new User($conn->conn, 'TechnoDiver');

 

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.