Fatal error: Uncaught Error: Call to undefined method PDOStatement::fetch_assoc() in C:\xampp\htdocs\PDO_connection.php:40

Hi. Actually have two part question here. First: in need of help understanding why fetch_assoc() is an undefined method. Why is fetch_assoc considered undefined in my code?
If I use fetch() it does not throw error, however it does display:
Warning: Invalid argument supplied for foreach() in C:\xampp\htdocs\PDO_connection.php on line 54
I have tried changing the case from lower to upper FETCH_ASSOC(), that did not remedy the problem. I have tried using as PDO::FETCH_ASSOC(), then I get different FATAL error.
I tried adding attribute to my $pdo variable
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
Secondly: Can I assume that I get the Warning on line 54 because $data is empty as a result of the call to undefined method?
My goal was to be able to output/view as many or as few columns of each row as desired.
Code is as follows:

<?php
//This class is used for connecting to the database.
class Dbh {
	private $servername;
	private $username;
	private $password;
	private $dbname;

public function connect() {
	$this->servername = "localhost";
	$this->username = "root";
	$this->password = "";
	$this->dbname = "miles_away_travel";
	$this->charset = "utf8mb4";

	try {
		//set dsn
		$dsn = 'mysql:host='.$this->servername.';dbname='.$this->dbname.';charset='.$this->charset;
		//create a PDO instance
		$pdo = new PDO($dsn,$this->username,$this->password);
		//also set some attributes after PDO construction with the setAttribute method:
		$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
		//$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
		//$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
		return $pdo;
	}  catch (PDOException $e) {
	 	echo "<h2>connection failed:</h2> ".$e->getMessage();
	 }
}
}

//This class is used to query the database
class FindUser extends Dbh {
	protected function seekUser(){
		$sql = "SELECT COUNT(*) FROM members";
		$result = $this->connect()->query($sql);
		$numRows = $result->fetchColumn();
		//print_r($numRows);
		if ($numRows >0){
			while($row = $result->fetch_assoc()){
			 $data[] = $row;
			 return $data;
			 //print_r($data);
			}
			//return $data;
		}
	}
}

//This class will be used view or use database column data
class UserData extends FindUser {
	public function showData(){
		$newData = $this->seekUser();
		foreach ($newData as $data) {
			echo $data['username']."</br>";
			echo $data['password']."</br>";
		}
	} 
} 

?>

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>PDO Connect_Query_Display</title>
	</head>
	<body>
		<?php
			$showUser = new UserData();
			$showUser->connect();
			echo $showUser->showData();
		?>
	</body>
</html>

    The php.net documentation for the PDO and PDOStatement classes list what methods there are. You must use one of the documented methods and there is no fetch_assoc() method. Programming is an exact science and randomly trying things that aren't part of the language definition won't work.

    As to the data related error, the posted code makes no sense. This is not good OOP or good programming and the logic it implements doesn't do anything useful (sorry, if that statement is all negative.) Even the naming and organization of user written classes and methods isn't good.

    Some of the problems -

    1) There should be no application specific values defined inside of any class, i.e. the connection credentials, database name, and character set. Classes should be general purpose and reusable without editing the code or values in them. Extending the PDO class with a class that contains your connection attributes (not sure why you have any of those commented out), but accepts the connection credentials, database name, and character set as call time parameters, would be useful. This would result in an instance of the PDO class that you can use in the rest of the code.

    2) When you make a database connection, it should be stored so that it can be reused in the rest of the code. The current code is creating and destroying the connection to run a single query and because the connection and all the resources for it, in php and on the database server, isn't immediately released, this style of coding will tie up multiple connections.

    3) If there is a database connection error, the code should halt execution so that it doesn't produce follow-on errors. The current code echos any connection error (which would be a security issue on a live/public server), then continues running, and will produce errors about null values. If you remove the try/catch logic you have now, a connection error will be caught by php and output the same type of information you are getting for the undefined method. This would be the preferred operation, since you can simply change php's display_errors and log_errors settings to cause this information to be logged when on a live/public server.

    4) Class names should not contain verbs/actions. Class names should be nouns/things. You can have a user class, that contains properties and methods associated with a user. If you want to find if a user exits, retrieve user data, or display user data, you would define methods in the user class to perform these actions/verbs. Each Class should be responsible for ONE type of thing and each of the class Methods within a class should do ONE thing. Testing if a user exists is one action. Retrieving user data is a different action. Your current code, using the COUNT(*) query is useful to find if a user exists, through without a WHERE clause in the query, only tells you if there are any users, not if a specific user exists, and it doesn't have anything to do with retrieving user data.

      @
      Thank you for your reply.
      The coding I presented in this post was mimic of video tutorial on youtube.
      Provided by mmtuts
      title:How To Connect To A Database Using PDO PHP | OOP PHP Tutorial | Learn OOP PHP
      Published on Jan 9, 2018
      https://www.youtube.com/watch?v=yWJFbPT3TC0&t=553s
      I thought I was getting up-to-date properly structured coding tutorial. When the fetch_assoc failed to work I did a lot of searches trying to find out why. As a result of thoes searches I had tried the other coding such as PDO::FETCH_ASSOC. No where in my searches did I see mention that fetch_assoc was not usable in PDO query. Thank you. I now know that. So my question is answered as to why it was an undefined method.

      There should be no application specific values defined inside of any class, i.e. the connection credentials, database name

      but accepts the connection credentials, database name, and character set as call time parameters, would be useful.

      Can you please point to good example?
      Terminate connections like this $stmt = null; $dbh = null; ?
      I will study further error handling and naming conventions.

        New_PHP_Guy wrote:

        No where in my searches did I see mention that fetch_assoc was not usable in PDO query.

        fetch_a_bucket is not usable in a PDO query either, but this sentence is the only place on the net where you'll see that mentioned.

        It's more useful to check the manual (as already said) to see what is usable (it's a much shorter list than one that lists what isn't). The method you want is listed there (and its page will provide examples of how to use the PDO_FETCH_ASSOC flag with it).

          @
          I didn't understand that undefined meant unusable in this instance.
          I thought I was somehow coding it improperly.
          I know it's monkey see monkey do, but this is the code I saw used in the video tutorial, so it's not like I pulled it out the air like fetch_a-bucket.
          There are many many of us that could benefit from the knowledge of experienced moderators such as yourself.
          Have any of you considered doing tutorials?
          I would be willing to pay reasonable $$ to learn from professionals that moderate here.

            I watched the linked to video and the next one in the series. The Author of that code has a basic understanding, but no actual experience.

            Besides unconditionally echoing the raw database error in the catch {} block, which gives hackers the file system path, which often contains the hosting account name, and the database username, he doesn't understand how PDO uses exceptions, and wasn't observant enough to notice that the error information being output by his try/catch block for a connection error and php's handling of the exceptions, for the case where he misspelled ->execute(), was different. He clearly stated that in order to get the try/catch block to work for the connection, that the setAttribute() statement was necessary. This is incorrect. The connection will always throw an exception on an error, and his try/catch block will always work. The setAttribute() statement that he showed, caused the rest of the PDO database statements to throw an exception on an error. So, removing the try/catch block for the connection, as I suggested, in addition to simplifying the code, will let php catch and handle the connection error exactly the same as for the rest of the database statements, where you can control what happens with the actual error information by simply changing php's display_errors and log_errors settings. The only time you should have a try/catch block for database errors, is if the error means something to your application, and you need to handle it in your code, such as someone submitting duplicate data.

            The Author also seems to think that using a loop to retrieve data from a query that will match only a single row and using a return statement inside of that loop is good coding. Using this logic will easily result in only retrieving the first row from a query that intends to match one or more rows. If a query is expected to match only a single row, just fetch the data from that row. If a query is expected to match one or more rows, use a loop or just fetch all the data at once using the fetchAll() method.

            Can you please point to good example?

            The following class extends the PDO class and adds a general purpose non-prepared/prepared query method.

            // extend pdo class with a generic non-prepared/prepared query method
            // by extending the pdo class, all the PDO base methods are available without having to write wrapper methods in the class
            class _pdo extends pdo
            {
            	public function __construct($dsn,$username=null,$password=null,$driver_options=null)
            	{
            		parent::__construct($dsn, $username, $password, $driver_options);
            		$this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // set the error mode to exceptions
            		$this->setAttribute(PDO::ATTR_EMULATE_PREPARES,false); // turn off emulated prepared queries, run real prepared queries
            		$this->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE,PDO::FETCH_ASSOC); // set default fetch mode to assoc
            	}
            
            // general purpose query method. accepts an optional array of input parameters and returns a pdostatement object
            public function query($sql,$params=array())
            {
            	if($params){
            		// prepared query
            		$stmt = parent::prepare($sql);
            		$stmt->execute($params);
            		// note: to execute a prepared query more than once, you would call the ->execute() method on the returned stmt object, supplying an array of new input values for each new execution
            	} else {
            		// non-prepared query
            		$stmt = parent::query($sql);
            	}
            
            	// return the pdo statement object
            	return $stmt;
            }
            }
            
            // example usage, based on your starting code
            $db_servername = "localhost"; 
            $db_username = "root"; 
            $db_password = ""; 
            $db_name = "miles_away_travel"; 
            $db_charset = "utf8mb4"; 
            
            //set dsn 
            $dsn = "mysql:host=$db_servername;dbname=$db_name;charset=$db_charset";
            //create an instance of the user defined _pdo class, which extends the PDO class
            $pdo = new _pdo($dsn,$db_username,$db_password);
            
            // at this point $pdo is an instance of the extended PDO class. you can use the general purpose non-prepared/prepared query() method from the user written class or any of the PDO methods.

            Any class that is dependent on having a database connection should use "dependency injection" (a web search will tell you what this means) to get access to the database connection.

            BTW - you should NOT use the root account and a blank password for your application's database connection. You should create a specific user that only has the necessary permissions that your application needs.

              New_PHP_Guy wrote:

              I didn't understand that undefined meant unusable in this instance.

              Well if it's not defined then how can you use it?

              That's why the manual is so useful a reference. Try to use a method on a PDOStatement and it complains the method doesn't exist? Check the manual's description of the PDOStatement class to see what methods do exist. Because they have to exist for you to use them.

                I came back to this post this morning to mark as resolved, but I can't find the "mark as resolved" in thread tools.
                As it has been awhile since I had used this forum and it is now under new ownership, perhaps things have changed.
                How do I mark as resolved?

                  New_PHP_Guy;11065661 wrote:

                  I came back to this post this morning to mark as resolved, but I can't find the "mark as resolved" in thread tools.
                  As it has been awhile since I had used this forum and it is now under new ownership, perhaps things have changed.
                  How do I mark as resolved?

                  Wait for the new owners to re-deploy under whatever new forum platform they're going to use, I guess. (shrug)

                    Write a Reply...