SitePoint Sponsor

User Tag List

Results 1 to 18 of 18
  1. #1
    SitePoint Addict
    Join Date
    Oct 2005
    Posts
    288
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)

    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 Code:
    <?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_ERRMODEPDO::ERRMODE_EXCEPTION);
          
    $this->dbManager->setAttribute(PDO::ATTR_EMULATE_PREPARESfalse);
      }
      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 Code:
    <?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
    PHP Code:
    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
    Last edited by SpacePhoenix; Oct 21, 2012 at 12:50. Reason: swapped code tags for php tags around php code

  2. #2
    From Italy with love silver trophybronze trophy
    guido2004's Avatar
    Join Date
    Sep 2004
    Posts
    9,491
    Mentioned
    161 Post(s)
    Tagged
    4 Thread(s)
    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:
    PHP Code:
    $stmt $this->dbManager->getDbManager()->prepare("SELECT name FROM horses ORDER BY name"); 

  3. #3
    SitePoint Addict
    Join Date
    Oct 2005
    Posts
    288
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Thank you Guido,

    Quote Originally Posted by guido2004 View Post
    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:
    PHP Code:
    $stmt $this->dbManager->getDbManager()->prepare("SELECT name FROM horses ORDER BY name"); 
    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
    Code:
    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

  4. #4
    From Italy with love silver trophybronze trophy
    guido2004's Avatar
    Join Date
    Sep 2004
    Posts
    9,491
    Mentioned
    161 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by Grnadpa View Post
    That doesn't explain why it worked when I inherited it (rather than passed it) before.
    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
    Code:
    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.

  5. #5
    SitePoint Addict
    Join Date
    Oct 2005
    Posts
    288
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by guido2004 View Post
    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

    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.

  6. #6
    From Italy with love silver trophybronze trophy
    guido2004's Avatar
    Join Date
    Sep 2004
    Posts
    9,491
    Mentioned
    161 Post(s)
    Tagged
    4 Thread(s)
    Example 1: inheritance
    PHP Code:
    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
    PHP Code:
    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?

  7. #7
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,050
    Mentioned
    152 Post(s)
    Tagged
    0 Thread(s)
    @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 Code:
    <?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_ERRMODEPDO::ERRMODE_EXCEPTION);
          
    $this->dbManager->setAttribute(PDO::ATTR_EMULATE_PREPARESfalse);
      }
      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 Code:
    <?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
    PHP Code:
    Class lessons_horse_Form
     
    extends lessons_template_Base{
        public function 
    __construct($dbManager)
            {
                
    parent::__construct($dbManager);
            }
    ...
        Function 
    BuildContent(){
    ...
            
    $horseValues $this->dbManager->GetHorseList(); 
    And
    PHP Code:
    <?php
    Class lessons_template_Base
    {
        protected 
    $dbManager null;
        
        public function 
    __construct($dbManager)
        {
            
    $this->dbManager $dbManager;
        }
    }
    ?>
    Implementation:
    PHP Code:
    $horseDbManager = new lessons_horse_dbManager();
    $horseForm = new lessons_horse_Form($horseDbManager);
    $horseForm->BuildContent(); 
    Be sure to congratulate Patche on earning July's Member of the Month
    Go ahead and blame me, I still won't lose any sleep over it
    My Blog | My Technical Notes

  8. #8
    SitePoint Addict
    Join Date
    Oct 2005
    Posts
    288
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by cpradio View Post
    [php]<?php
    Class lessons_horse_dbManager extends lessons_template_dbManager {

    protected $trace = true;

    Public Function __Construct() {
    if($this->trace) {
    echo("<br />lessons_horse_dbManager->__Construct");
    }
    }
    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?

  9. #9
    From Italy with love silver trophybronze trophy
    guido2004's Avatar
    Join Date
    Sep 2004
    Posts
    9,491
    Mentioned
    161 Post(s)
    Tagged
    4 Thread(s)
    Quote Originally Posted by Grnadpa View Post
    nit: wouldn't this disable the parent's (Class lessons_template_dbManager ) construct rendering it null?
    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:
    PHP Code:
    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?

  10. #10
    SitePoint Wizard siteguru's Avatar
    Join Date
    Oct 2002
    Location
    Scotland
    Posts
    3,629
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    My head's just exploded!

    One of these days I'll get time to get my head around OOP.

    PS - Just noticed that it's 10 years ago this month that I joined SP.
    Ian Anderson
    www.siteguru.co.uk

  11. #11
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,050
    Mentioned
    152 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Grnadpa View Post
    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?
    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 Code:
    <?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_ERRMODEPDO::ERRMODE_EXCEPTION);
          
    $this->dbManager->setAttribute(PDO::ATTR_EMULATE_PREPARESfalse);
        }
        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 Code:
    <?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 Code:
    <?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 Code:
    <?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 Code:
    <?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().

    Quote Originally Posted by guido2004 View Post
    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:
    PHP Code:
    Public Function __Construct() {
      if(
    $this->trace) {
        echo(
    "<br />lessons_horse_dbManager->__Construct");
      }
      
    parent::__construct();

    Yep, I missed that one, but I caught the form one Thanks for the assist @guido2004 ;
    Be sure to congratulate Patche on earning July's Member of the Month
    Go ahead and blame me, I still won't lose any sleep over it
    My Blog | My Technical Notes

  12. #12
    SitePoint Addict
    Join Date
    Oct 2005
    Posts
    288
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    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
    • template
    • menu
    • student
    • horse
    • tack
    • select

    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 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

  13. #13
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,050
    Mentioned
    152 Post(s)
    Tagged
    0 Thread(s)
    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).

    Quote Originally Posted by Grnadpa View Post
    -- 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 ]
    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.
    Be sure to congratulate Patche on earning July's Member of the Month
    Go ahead and blame me, I still won't lose any sleep over it
    My Blog | My Technical Notes

  14. #14
    SitePoint Addict
    Join Date
    Oct 2005
    Posts
    288
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by cpradio View Post
    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.
    PHP Code:
    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?

  15. #15
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,050
    Mentioned
    152 Post(s)
    Tagged
    0 Thread(s)
    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/...stract-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.
    Be sure to congratulate Patche on earning July's Member of the Month
    Go ahead and blame me, I still won't lose any sleep over it
    My Blog | My Technical Notes

  16. #16
    SitePoint Addict
    Join Date
    Oct 2005
    Posts
    288
    Mentioned
    1 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by cpradio View Post
    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/...stract-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?

  17. #17
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,050
    Mentioned
    152 Post(s)
    Tagged
    0 Thread(s)
    DI = dependency injection

    Creating Mocks for Testing
    http://www.phpunit.de/manual/3.0/en/mock-objects.html
    http://css.dzone.com/books/practical...ng-patterns-39
    http://phpmaster.com/an-introduction...bject-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.
    Be sure to congratulate Patche on earning July's Member of the Month
    Go ahead and blame me, I still won't lose any sleep over it
    My Blog | My Technical Notes

  18. #18
    I solve practical problems. bronze trophy
    Michael Morris's Avatar
    Join Date
    Jan 2008
    Location
    Knoxville TN
    Posts
    2,011
    Mentioned
    62 Post(s)
    Tagged
    0 Thread(s)
    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.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •