Cannot find function "prepare" in PDO Object

Based on an earlier thread (PDO: Am I missing the concept completely? ), I’m simply attempting to replace the EXTEND of my database manager with passing the object as an argument. Apparently whatever object I am passing does not contain the “prepare” function.

(The other change I made from the original db was to change the class and variable name from $db to $dbManager)
Here is the class that sets up the dbManager object

<?php
Class lessons_template_dbManager { 
  Protected $dbManager = null;
    Protected $trace = false;

 Public Function __Construct() { // assure connection
    if($this->trace) {
        echo("<br />lessons_template_dbmanager->Construct");
    }
    if(isset($this->dbManager)) {
        if($this->trace) {
            echo("<br />lessons_template_dbmanager->db is set");
        }
        return true;
    }
    if($this->trace) {
        echo("<br />lessons_template_db->dbmanager is NOT set");
    }

    $currentHost = pathinfo($_SERVER['REMOTE_ADDR'],PATHINFO_BASENAME);
    if($currentHost == "127.0.0.1"){        // no place like home
        $theUsername = "root";
        $thePassword = "";
    } else {
--> detail omitted <--
    }
  try {
      $this->dbManager = new PDO('mysql:host=localhost;dbname=lessons;charset=UTF-8', $theUsername, $thePassword);
      $this->dbManager->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
      $this->dbManager->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
  }
  catch(PDOException $ex) {
        echo("<p style='font-weight: bold; color: red; font-size: larger;'><br /><br /><br />A serious error occurred, contact programmer who will need the following detail<br /></p>");
        echo("<br />lessons_template_dbManager->Construct<br />");
    var_dump($ex);
    exit();
  }
 } // end function
} // end class 
?>

Here’s an excerpt class that uses the above passed as an argument

<?php
Class lessons_horse_dbManager {

    protected $trace = true;
    private $dbManager;

    Public Function __Construct($dbManager) {
        if($this->trace) {
            echo("<br />lessons_horse_dbManager->__Construct");
        }
        $this->dbManager = $dbManager;
    }
    Public Function GetHorseList() {
        if($this->trace) {
            echo("<br />lessons_horse_dbManager->GetHorseList");
        }
        // dispensing with try-catch
        $stmt = $this->dbManager->prepare("SELECT name FROM horses ORDER BY name");
        $stmt->execute();
        $nameList = array();
        while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
            $nameList[] = $row['name'];
            } // end while
        return($nameList);
        } // end function  
...

And here’s the calling sequence from the instantiating class

Class lessons_horse_Form
 extends lessons_template_Base{
...
    Function BuildContent(){
...
        $dbManager = new lessons_template_dbManager();
        $horseObj = new lessons_horse_dbManager($dbManager);
        $horseValues = $horseObj->GetHorseList();

The message I get is

Fatal error: Call to undefined method lessons_template_dbManager::prepare() in C:\xampp\htdocs\lessons\horse\dbmanager.php on line 18

[I have “no icon” selected, the message should show as …dbManager colon colon prepare()]

Where line 18 is ‘$stmt = $this->dbManager->prepare(“SELECT name FROM horses ORDER BY name”);’ from class lessons_horse_dbManager. (And again, this worked when I extended the original lessons_template_db class).

Any ideas?
Regards,
grNadpa

The $dbManager you’re passing into your second class isn’t a pdo object. It’s an object that has a property that is a pdo object.

So you could add a getDbManager() method that returns the pdo object, and then change the code in the second class like this:

$stmt = $this->dbManager->getDbManager()->prepare("SELECT name FROM horses ORDER BY name");

Thank you Guido,

That doesn’t explain why it worked when I inherited it (rather than passed it) before. In any case, I made the change and received a similar message

Fatal error: Call to undefined method lessons_template_dbManager::getDbManager() in C:\xampp\htdocs\lessons\horse\dbmanager.php on line 19

I also tried

Public Function __Construct($dbManager) {
		if($this->trace) {
			echo("<br />lessons_horse_dbManager->__Construct");
		}
		$this->dbManager = $dbManager->getDbManager();
	}

instead of the change you suggested. That didn’t work either.

regards,

grNadpa

Yes it does. If your second class inherits the first classes properties, then the second class can acces the pdo object like this: $this->dbManager

In any case, I made the change and received a similar message

I also tried

Public Function __Construct($dbManager) {
		if($this->trace) {
			echo("<br />lessons_horse_dbManager->__Construct");
		}
		$this->dbManager = $dbManager->getDbManager();
	}

instead of the change you suggested. That didn’t work either.

regards,

grNadpa

Does your first class have a getDbManager? If not, you’ll have to write one.

Then I obviously do not understand OOP. Please see the Class lessons_template_dbManager code I originally posted. Other than renaming “db” to “dbManager” there is NO difference between what I used as a parent that I EXTENDed into the former Class lessons_horse_dbManager (which did not contain a __Construct function) and the object I pass into Class lessons_horse_dbManager (that does contain the __Construct function).

I am so frustrated at this point that I need to walk away.

Example 1: inheritance


class Parent { 

  // properties
  Protected $dbManager = null;

  // constructor
  Public Function __Construct() { 
     ...
     $this->dbManager = new PDO('mysql:host=localhost;dbname=lessons;charset=UTF-8', $theUsername, $thePassword);
     ...
  }

}

class Child extends Parent { 

  // constructor
   Public Function __Construct() { 
      ...
      parent::__construct();
  }

  //methods
  Public Function GetHorseList() {  
    ...
    $stmt = $this->dbManager->prepare("SELECT name FROM horses ORDER BY name");
    ...
  } 

}

$child = new Child();
$list = $child->GetHorseList();

In this example (very simplified) the “child” class inherits the dbManager property from the “parent” class. So you can use that property in the “child” class with $this->dbManager, just like you would in the “parent” class.
Example 2: passing an object as a variable


class parent { 

  // properties
  Protected $dbManager = null;

  // constructor
  Public Function __Construct() { 
     ...
     $this->dbManager = new PDO('mysql:host=localhost;dbname=lessons;charset=UTF-8', $theUsername, $thePassword);
     ...
  }

  //methods
  Public Function getDbManager() { 
     return $this->dbManager;
  }

}

class child  { 

  // properties
  Protected $parent = null;

  // constructor
   Public Function __Construct($parent) { 
      $this->parent = $parent;
  }

  //methods
  Public Function GetHorseList() {  
    ...
    $stmt = $this->parent->getDbManager()->prepare("SELECT name FROM horses ORDER BY name");
    ...
  } 

}

$parent = new Parent();
$child = new Child($parent);
$list = $child->GetHorseList();

In this example (also very simplified) the “child” class doesn’t inherit anything, but the “parent” object is passed as a variable. I’ve changed the name of the property in the “child” class, because using the same name in the two classes might have caused the confusion.
I also added a method to the “parent” class that returns the dbManager property.
As you can see, to use the dbManager property of the “parent” class in the “child” class, you can’t use $this->dbManager anymore, because the “child” class has no dbManager property. It has a parent property, which is an object of the “parent” class, which has a public method called “getDbManager” that you can call to get its dbManager property.

I hope this is clear?

@Grnadpa ; First let me say, “Oops!”, I gave you a broken example. The protected $db variable in lessons_template_db causes the code to not run correctly. It would have had to be public or a getter would have needed to be provided.

Now let’s step back a second and look at your current example here with lessons_template_dbManager and lessons_horse_dbManager. In this case, Dependency Injection would be the wrong choice (in my opinion), because the two classes are both dbManagers, so Inheritance seems to make more sense, as lessons_horse_dbMangage is a lessons_template_dbManager.

So you would end up with:

<?php
Class lessons_template_dbManager {
  Protected $dbManager = null;
    Protected $trace = false;

	public function getDbManager()
	{
		return $this->dbManager;
	}
		
 Public Function __Construct() { // assure connection
    if($this->trace) {
        echo("<br />lessons_template_dbmanager->Construct");
    }
    if(isset($this->dbManager)) {
        if($this->trace) {
            echo("<br />lessons_template_dbmanager->db is set");
        }
        return true;
    }
    if($this->trace) {
        echo("<br />lessons_template_db->dbmanager is NOT set");
    }

    $currentHost = pathinfo($_SERVER['REMOTE_ADDR'],PATHINFO_BASENAME);
    if($currentHost == "127.0.0.1"){        // no place like home
        $theUsername = "root";
        $thePassword = "";
    } else {
--> detail omitted <--
    }
  try {
      $this->dbManager = new PDO('mysql:host=localhost;dbname=lessons;charset=UTF-8', $theUsername, $thePassword);
      $this->dbManager->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
      $this->dbManager->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
  }
  catch(PDOException $ex) {
        echo("<p style='font-weight: bold; color: red; font-size: larger;'><br /><br /><br />A serious error occurred, contact programmer who will need the following detail<br /></p>");
        echo("<br />lessons_template_dbManager->Construct<br />");
    var_dump($ex);
    exit();
  }
 } // end function

} // end class
?>

And

<?php
Class lessons_horse_dbManager extends lessons_template_dbManager {

    protected $trace = true;

    Public Function __Construct() {
        if($this->trace) {
            echo("<br />lessons_horse_dbManager->__Construct");
        }
    }
    Public Function GetHorseList() {
        if($this->trace) {
            echo("<br />lessons_horse_dbManager->GetHorseList");
        }
        // dispensing with try-catch
        $stmt = $this->dbManager->prepare("SELECT name FROM horses ORDER BY name");
        $stmt->execute();
        $nameList = array();
        while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
            $nameList[] = $row['name'];
            } // end while
        return($nameList);
        } // end function
...

Now, where DI would come into play is the following class(es): lessons_horse_Form and lessons_template_Base

Class lessons_horse_Form
 extends lessons_template_Base{
    public function __construct($dbManager)
		{
			parent::__construct($dbManager);
		}
...
    Function BuildContent(){
...
        $horseValues = $this->dbManager->GetHorseList();  

And

<?php
Class lessons_template_Base
{
	protected $dbManager = null;
	
	public function __construct($dbManager)
	{
		$this->dbManager = $dbManager;
	}
}
?>

Implementation:

$horseDbManager = new lessons_horse_dbManager();
$horseForm = new lessons_horse_Form($horseDbManager);
$horseForm->BuildContent();

nit: wouldn’t this disable the parent’s (Class lessons_template_dbManager ) construct rendering it null?

And I wonder if this approach doesn’t overly bind these dbManagers too tightly to the implementors. Yes, lesson_horse_Form is the primary CRUD engine for the horse table (as are the corresponding lessons_student and lesson_tack for their respective tables).

BUT there is also an ‘alesson’ table whose form builds drop-down lists from all three student, horse and tack (using the functions GetStudentList, GetHorseList and GetTackList respectively) to assure that only valid values are available on the user’s form.

In this EXTENDS scenario, then, I see four mySQL connections in my lesson form – which makes passing the connection as an argument to the implementor (if I’m using the term correctly) appear to be a preferred approach. Or do we forego encapsulation and throw all four tables into one dbManager for the alesson form?

TO DIGRESS / RANT: I’m having a hard time understanding how replacing Mysql with either PDO or Mysqli represents anything but overkill for a low-activity user-conversational application like this one. In fact, what kind of application requires the high volume that these Mysql replacements are meant to make more efficient – where, say, Java isn’t a better language choice?

Yes. To have the parent’s construct to execute, you must not have a contruct in the child class, or explicitely call the parent’s construct:


Public Function __Construct() {
  if($this->trace) {
    echo("<br />lessons_horse_dbManager->__Construct");
  }
  parent::__construct();
}

By the way, was my explanation of the difference between inheritance and injection of any help?

My head’s just exploded! :lol:

One of these days I’ll get time to get my head around OOP. :slight_smile:

PS - Just noticed that it’s 10 years ago this month that I joined SP. :sparty: :cheer:

Okay, maybe the naming convention led me down the path of extends, let’s rename them and look at Dependency Injection.

Your lessons_template_dbManager doesn’t change

<?php
Class lessons_template_dbManager {
  Protected $dbManager = null;
  Protected $trace = false;

  public function getDbManager()
  {
    return $this->dbManager;
  }

  Public Function __Construct() { // assure connection
    if($this->trace) {
      echo("<br />lessons_template_dbmanager->Construct");
    }
    if(isset($this->dbManager)) {
      if($this->trace) {
        echo("<br />lessons_template_dbmanager->db is set");
      }
      return true;
    }
    if($this->trace) {
      echo("<br />lessons_template_db->dbmanager is NOT set");
    }

    $currentHost = pathinfo($_SERVER['REMOTE_ADDR'],PATHINFO_BASENAME);
    if($currentHost == "127.0.0.1"){        // no place like home
      $theUsername = "root";
      $thePassword = "";
    } else {
      //--> detail omitted <--
    }
    try {
      $this->dbManager = new PDO('mysql:host=localhost;dbname=lessons;charset=UTF-8', $theUsername, $thePassword);
      $this->dbManager->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
      $this->dbManager->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
    }
    catch(PDOException $ex) {
      echo("<p style='font-weight: bold; color: red; font-size: larger;'><br /><br /><br />A serious error occurred, contact programmer who will need the following detail<br /></p>");
      echo("<br />lessons_template_dbManager->Construct<br />");
      var_dump($ex);
      exit();
    }
  } // end function

} // end class  
?>

Your lessons_horse_dbManager becomes lessons_horse_repository

<?php 
Class lessons_horse_repository {

  protected $dbManager = null;
  protected $trace = true;

  Public Function __Construct($dbManager) {
    if($this->trace) {
      echo("<br />lessons_horse_repository->__Construct");
    }
    $this->dbManager = $dbManager;
  }

  Public Function GetHorseList() {
    if($this->trace) {
      echo("<br />lessons_horse_repository->GetHorseList");
    }
    // dispensing with try-catch
    $stmt = $this->dbManager->prepare("SELECT name FROM horses ORDER BY name");
    $stmt->execute();
    $nameList = array();
    while($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
      $nameList[] = $row['name'];
    } // end while
    return($nameList);
  } // end function

  //...
  // Create, Update, Delete functions should also exist
}
?>

Your lessons_horse_Form

<?php
Class lessons_horse_Form
  extends lessons_template_Base{

    // If you don't have any other setup, delete this construct function,
    // as it will then invoke the parent automatically
    public function __construct($dbManager)
    {
      parent::__construct($dbManager);
    }

    //...other functions...

    Function BuildContent(){
      //...
      $horseObj = new lessons_horse_repository($this->dbManager);
      $horseValues = $horseObj->GetHorseList();
      //...
    }
}

Your lessons_template_Base

<?php
class lessons_template_Base
{
  protected $dbManager = null;

  public function __construct($dbManager)
  {
    $this->dbManager = $dbManager;
  }

  //... any shared functions across students, horses, and track
}

Implementation

<?php
$dbManager = new lessons_template_dbManager();
$horseForm = new lessons_horse_Form($dbManager->getDbManager());
$horseForm->BuildContent();

One thing to note. I am passing $dbManager->getDbManager() so the all of the DIs have the same implementation (granted there are two ways to do this). The above, which passes in the getDbManager(), OR each Form/Repository class would have to utilize $this->dbManager->getDbManager()->prepare… for each call.

Why not just pass in $dbManager and then in the construct set the protected $dbManager to getDbManager()? Good question, let’s follow that path

  1. lessons_horse_Form will receive it and set its’ internal $dbManager to the result of getDbManager()
  2. now it passes its’ internal $dbManager to lessons_horse_repository (which is the result of getDbManager())
  3. the construct for the repository now doesn’t need to call getDbManager(), giving it a different implementation

Really isn’t a big deal, but I like to keep my underlying implementations the same, though I will accept the argument that the *_Form classes would have the same implementation of using getDbManager() and the *_repository classes would have the same implementation of not having to call getDbManager().

Yep, I missed that one, but I caught the form one Thanks for the assist @guido2004 ; :slight_smile:

What would be nice is to get a review of my design and code in that I’m an old mainframe programmer who has tried to re-invent himself (albeit with no reasonable expectation of commercial opportunity).

So, let me digress a bit on how I have organized this application. (The closest “official” pattern to what I’m doing that I’ve encountered is the “template” pattern.)

In a typical scenario, the user signs on and selects either a lesson recap or a table maintenance option. Upon that selection, a screen appears with the left column populated with one or more drop-down boxes. For example, a list of horses, or students, or tack, or year-session-lesson number-student name combination. The user makes a selection and presses the select key. If there is an error, the screen returns indicating the error(s), otherwise the right column fills in with a form fitting that criteria for the user to add or update the data. The user fills in / overtypes that form and presses a submit key. Either the screen returns documenting the errors it found, or confirms the addition/change/delete success.

In addition to my index.php, I have the following folders in my c:\xampp\htdocs\lessons folder[LIST]
[]template
[
]menu
[]student
[
]horse
[]tack
[
]select
[*]
[/LIST]
Each folder has the same basic structure

  • db.php (currently converting to dbManager.php)
  • form.css
  • form.php
  • parse.php
  • parseform.php
  • and a folder named “detail”

That detail folder contains

  • form.css
  • form.php
  • parse.php
  • parseform.php

The template folder contains the parents –
– The template/form.php handles preparation and sending of all components of the form EXCEPT an Abstract Function Capture which requires the child to capture all the data the child form requires and an Abstract Function BuildContent() which delegates the preparation of the page’s unique XHTML. Note that the <form’s … action=“/lessons/foldername/parse.php”> will always be to the child folder’s “parse.php”. The database activity in these “form” children simply gets (a) name list(s) from the appropriate foldername/db.php.
– the “parse.php” for each folder is simply the following code

<?php
session_start();
function __autoload($classname) {
	// assumes naming convention where class name
	// includes path separated by underscores
	// e.g. legacy_cls_loader
	$path = str_replace('_', DIRECTORY_SEPARATOR, $classname);
	$path= $_SERVER['DOCUMENT_ROOT'] .  DIRECTORY_SEPARATOR . strtolower($path);
	require_once( "$path.php");
}


/* ----------------------- Main Control ----------------------- */
	$indexObj = new lessons_***_ParseForm();
	exit();
/* --------------------- End Main Control --------------------- */

?>

where *** is the folder name (e.g. student, horse, tack, select)

– The template/parseform.php handles reapplying security and controls the sequence of abstract functions Validate() [forcing the child to verify the data from $_POST and preparing messages for any data failures], Apply() [forcing the child to provide the action(s) to take on a successful Validate()], OnError() [forcing the child to specify what action to take when the Validate() fails – typically instantiating the folder’s “form.php”] and OnCancel() [forcing the child to specify what action to take if user “bails” by clicking the cancel button ]
– The template/db.php instantiates the database connection, leaving the children to handle the CRUD.

Returning to the scenario, for a horse table update the sequence would go
[User signs in and selects horse table maintenance]
(1) menu/form.php (extends template/form.php)
(2) menu/parse.php instantiates menu/parseform.php** which instantiates horse/form.php*
(3) horse/form.php* invokes horse/db.php* (extends template/db.php) to get a list of horses which it places as a drop down list in the left column of a new screen
[user makes a selection and presses the select key.]
(4)horse/parse.php instantiates horse/parseform.php** which, after verifying selection, …

[right column fills in with a form fitting that criteria]
…instantiates horse/detail/form.php*** (note the subsidiary folder horse/detail).
[ The user fills in / overtypes that form and presses a submit key.]
(5) horse/detail/parse.php instantiates horse/detail/parseform.php** which invokes either the “add” or “update” or logical “delete” function within the relevant db.php child.

  • extends template/form.php
    (the code examples above show this as template_base I changed the name in this post for clarity).
    ** extends template/parseform.php
    (the code examples above show this as template_parse)
    *** extends template/horse/form.php (which in turn extends template/form.php)

So that’s how I’ve tried to OOP this application unencumbered by any formal OOP training.

Am I on the right track?

grNadpa

I think you are doing fine with what you indicate as extending other classes/objects as they seem to fit the “is a” test (example, the horse detail form is a template horse form).

One thing I will mention is the above seems to scream INTERFACE. To help ensure all of your extended classes adhere to the Apply(), Validate(), OnError(), OnCancel() methods you may want to create an Interface that they implement so those methods are required to be added. Now I am also making a few assumptions, but the primary assumption is all of the child form parsing needs to have these methods, and if that is the case, an interface will be useful.

These are abstracts in the parent.

Abstract Class lessons_template_ParseForm {
	Public $msgs = array();
  Public Function __Construct() {
// Control process flow of the 'submit' phase of the psuedo-conversation.
// (system convention asks that all cancel actions use submit name="subcancel")
	if(isset($_POST['subcancel'])) {
		$this->OnCancel();		// delegate to OnCancel abstract method / function
		return;
	} // end if

 	$this->VerifyEntry();		// always check for malicious insertion
	
	if($this->Validate()) {        // delegate specific data validation to abstract function
		$this->Apply();        // ONLY IF data valid, delegate C, U and D of CRUD
	} else {
		$this->OnError();     // delegate actions to take if data INVALID
	} // end if-else
	return;
  } // end function __Construct

 abstract Function Validate();
 abstract Function Apply();						
 abstract Function OnError();

 Public Function OnCancel(){
  header('Location: /lessons/index.php'); // default, may be overloaded
 } // end function OnCancel

Function VerifyEntry() {
...
  } // end function VerifyEntry
...
} // end class

Is this not the right place – that I need to move these to a separate class so as to IMPLEMENT them, even though I use them always but only when I EXTEND this class?

That is true, an abstract class will help with that, but Interfaces actually build the contract between your class and how they should be represented/interacted with. Interfaces allow you to them pass the object via DI, and the receiver can call the expected methods without needing to know exactly which class is being handled, it simply knows, you abide by “Interface X” and so these methods are expected to be there and behave this way.

You can read a bit more about abstract class vs interface at http://www.codeproject.com/Articles/6118/All-about-abstract-classes (scroll down to the section titled abstract class vs interface.

In your example, your base abstract class is resembling an interface, but isn’t making any type of contract that can be utilized in a DI approach, but maybe you don’t need that.

Secondly, if you ever wanted to get into Mocking for Testing, you will find it far easier to do if you have an interface and use a DI approach.

I see an extra level of complexity by adding the interface, I don’t see an advantage of doing so … so I must have missed something in the article you linked me to (pardon the grammar).

“DI” is shorthand for what? Where might I understand what “Mocking for Testing” is all about?

DI = dependency injection

Creating Mocks for Testing
http://www.phpunit.de/manual/3.0/en/mock-objects.html
http://css.dzone.com/books/practical-php-testing/practical-php-testing-patterns-39
http://phpmaster.com/an-introduction-to-mock-object-testing/

A common usage is to mock a database interaction, so you can provide repeatable results. Example: if you had a UsersRepository class that contain a GetLatestUser, this would not be predictable on a continuous integration standpoint, as the Latest User will change from time to time as a new user signs up. So by tying it to an interface, you can MOCK the UsersRepository so it returns a specific user, thus allowing you to expect a specific result.

I know what you are thinking, “if you tell it what to return, what are you truly testing?”. You are testing the implementation of the UsersRepository, that you call a method and get back a valid (or invalid; should test for both) result. So if someone goes in and changes the GetLatestUser to return a different object or it now returns the last 5 users, your test will fail because it did not receive what it was expecting.

Tip: If you’re extending the pdo object eventually you may want to extend the pdo statement object. When you do be warned, the __construct function of that internal object is protected instead of being public! That particular PHP gotcha had me scratching my head for 2 days.