Jump to content

OOP linking parent/child classes (and if to instantiate or not)


johnsmith153

Recommended Posts

class Database {

 

// actual database interaction

 

// also uses mysql_real_escape etc.

 

}

 

class Users extends Database {

 

function checkIfUsernameExists() {

 

}

 

}

 

class addUser extends Users {

 

// perform all add user stuff

 

}

 

class searchUser extends Users {

 

// all search user stuff

 

}

 

So, to add a new user, I would instantiate, then perform various queries to the addUser class, probably also using the checkIfUsernameExists method in the Users class. Finally, Database would add the record.

 

Question: what do I instantiate? Do I instantiate just the addUser one, or first the Database class? I know how to do it, but what about instantiation, using parent:: self:: etc. etc. (how do I link these clasess together is what I think I mean).

Link to comment
Share on other sites

If ClassB inherits from ClassA  (using the extend keyword), instantiating an object of ClassB automatically instantiates the ClassA-inherited members. You could instantiate an object of CassA, but it would only have the members and methods of ClassA and would have NO knowledge of ClassB (ClassA can't access ClassB's members, inheritance is a one-way street).

 

Here's an example:

 

<?php
    // test.php

    class Widget
    {
        public $foo = 'hello';
        public $bar = 'world';
        
        function widgetize()
        {
            echo $this->foo . ' ' . $this->bar . "\n";
        }
    }
    
    class WidgetConfabilator extends Widget
    {
        function confabilate($new_bar)
        {
            $this->bar = $new_bar;
        }
    }
    
    $myObj = new WidgetConfabilator();
    $myObj->widgetize();
    $myObj->confabilate("php land");
    $myObj->widgetize();
    
    // Reusing same variable--I'm lazy
    $myObj = new Widget();
    $myObj->widgetize();
    $myObj->confabilate("php land");    // Error
    $myObj->widgetize();                // This code never gets executed
?>

 

C:\Scripts>php .\test.php
hello world
hello php land
hello world

Fatal error: Call to undefined method Widget::confabilate() in C:\Scripts\test.php on line 31

 

Link to comment
Share on other sites

Your design is all sorts of messed up.  You don't need to create a child class every time you want to gain some functionality.  Objects can contain other objects.  Also, your User activities should be methods as they act on a User.  Classes denote things.  Methods denote actions on things.  Further, does it make sense to have a User be a Database?  Roughly, you should have something like:

 

abstract class Database
{
   public static function searchUser($info)
   {
      // search for the user and return the User object if it is found 
   }
}

class User
{
   private $_db;

   public function __construct(Database $db)
   {
      $_db = $db;
   }

   public function saveChanges()
   {
      // search the $_db to see if $this exists.  If so, create a new User and save.  Otherwise, update existing User
   }
}

Link to comment
Share on other sites

Thanks, and I have taken all this onboard.

 

Listening to bits of the advice, other advice I have been given and my own favoured style, I want to do this:

 

(1) Use the database class like this: http://snipplr.com/view/12697.16543/

 

(2) But also want to encapsulate most of the code they have used outside of the Database class (in their example) in a Users class. Yes, I would include all searchUser and addUser methods in the Users class (rather than creating theirown classes), this should have been obvious to me (thanks).

 

Would this be ok, and how would I then do it? I think I would instantiate the User class and access the Database from that - but how do you then store the connection variable (in procedural programming I simply do $con = mysql_connect etc. but where do I store the $con variable if I'm not instantiating the class?)

 

An important point is that there could be many classes accessing many different databases and tables so I need to bear this in mind (it's not only the Users class that gets used for db work). I don't think putting the query in the Database class is right bearing this in mind. I may have 20 or so child classes of Database (Users, Product etc...).

Link to comment
Share on other sites

Look up the singleton and registry patterns.  Either one should work.  Inside your User class you would access then like this (probably in the constructor):

 

class User {
   private $db;

   function __construct() {
      // singleton (Database class is singleton and returns a single instance)
      $this->db = Database::get_instance();

     // registry (Database instance is stored in a registry)
     $this->db = registry::get('db');
   }
}

 

Or, when you instantiate the User object you can pass in the Database instance:

 

$db = new Database();
$user = new User($db);

 

 

 

Link to comment
Share on other sites

I was working on a reply but AbraCadaver hit all the points I needed to make. Here's the example I had going just for kicks:

// Database class from http://snipplr.com/view/12697.16543/
class Database
{
    private $host;
    private $user;
    private $pwd;
    // etc ...    
}

// Your home-brewed User class
class User
{
    private $db;
    private $id;
    public $name;
    
    public function __construct($database, $id)
    {
        $this->db = $database;
        $this->id = $id
    }
    
    public function update_record()
    {
        $this->db->query("UPDATE employee_table SET name='$this->name' WHERE id='$this->id'");
    }
    
    // etc...
}

// Now using these classes is very simple. Every object of type 'User' will 
// need to know what database to work on.

// Instantiate our employee database
$employee_db = new Database();
$employee_db->init('localhost', 'mysql_user', 'mysql_password', 'employee_db_name');

// Instantiate our customer database
$customer_db = new Database();
$customer_db->init('localhost', 'mysql_user', 'mysql_password', 'customer_db_name');

// Now on to the users, lets work with an employee-style user
$some_employee = new User($employee_db);    
$some_employee->name = "John Doe";          // Change some data
$some_employee->update_record();            // Update database record

// And using the second database...
$some_customer = new User($customer_db);
// etc...

Link to comment
Share on other sites

I'm not actually using the exact DB class - was going to write my own. I just understood it better than a lot - and found that it fit with how I wanted to do the inheritance side of things.

 

To ensure when i do write my own that it's better than this, can you give me a few points as to what these are doing wrong. I suppose my Users class is better then declaring the query outside the class for a start (maybe a link to a better one, but it must use this inheritance style ideally).

 

Thanks again to AbraCadaver, dontpanic and Nightslyr for the help - very thorough help and has saved me a lot of time.

 

I was reviewing my learnings on Lynda.com to try and understand this - but am sure it's a little basic even for me!

 

Thanks again.

Link to comment
Share on other sites

I'm wrong. Lynda had all this - must have been asleep when that video was on.

 

Actually, Lynda uses a call to User::update_record (no need to instantiate the User class).

 

Only thing is, their code says you can use gloabal inside the class - so then you don't need to pass the instantiated Database variable to User::update_record() - but I can't get this to work.

 

Can you use global inside a class?

Link to comment
Share on other sites

The global keyword is pretty tricky. You cannot have global member variables (it doesn't make sense, really), but you can use global variables within functions:

 

class Foo
{        
    public function do_something()
    {
        global $var;
        echo "In class Foo, var=$var\n";
    }
}

$obj = new Foo();
$var = 'bar';
$obj->do_something();
$var = 'changed';    
$obj->do_something();

 

C:\Scripts>php .\test2.php
In class Foo, var=bar
In class Foo, var=changed

 

Link to comment
Share on other sites

Now I've gone crazy.

 

How about something like this:

 

It's not finished, I could do with a hand finishing.

 

DatabaseAccess does all the expected stuff you'll see in most Database classes (mysql_real_escape, mysql_connect etc.)

 

DatabaseControl stores details of errors (whilst dealing with say add user request) and also manipulates results (may need to get from another class etc.)

 

...then we have Users and many othr classes.

 

<?php

class DatabaseAccess {

protected $myDbase = array("dog", "cat", "rabbit");//based on query below etc will return something, use this as example

}

class DatabaseControl extends DatabaseAccess {

protected = $error_logging;

public = $result_set;


function go($query, $db) { 

	//this gets from what provided in $query and stores relevant error logging and result set ready for display at the end




	echo $this->$result_set;

}

}

class User extends DatabaseControl {

public static function searchUsers($db) {

	//stores $error-logging (only really when adding/updating)
	//also stored the $result_set in class above

	///////////////////

	//commented out as needs adjustment

	//$returned = $db->myDbase;
	//$result = $db->fetch_array($returned);
	//return $result;
}

}


//every time
$db1 = new DatabaseAccess();

//per new query requirement
$query1 = new DatabaseControl();
$query1 = go("User::searchUsers",$db1);

//per new query requirement
$query2 = new DatabaseControl();
$query2 = go("Product::findItem",$db1);

?>

Link to comment
Share on other sites

Don't use globals.  You shouldn't use globals in procedural code.  In OOP, they essentially destroy one of the basic principles of OOP itself.  Argument lists exist for a reason.  Pass in what you need as function/method parameters.

 

EDIT: You really need to stop and look at what you're doing.  You clearly don't understand when/why inheritance is used.  What you need to do is compose objects, not simply create a whole long list of parent/children objects.  Searching for a User makes sense from the context of a db, not from the context of a User itself.  A User object should be returned from the search.

Link to comment
Share on other sites

class User extends DatabaseControl

 

You are missing a fundamental concept of object orientation (OO). Unlike procedural programming, OO is all about separating your data into small, logical groups called classes. The way your rolling all of your database functionality into your User objects tells us that you are thinking of objects incorrectly. Don't inherit Database with your User class, simply pass an object of type Database into your User constructor like our example code has shown. From inside the User class you can use that previously created database object  to manipulate your database while ensuring only one connection is ever made.

 

Honestly you should start with with exactly two classes, and expand to many classes with inheritance later on. Use a single Database class to encapsulate all database functionality, and a single User class to define a generic user.

 

Have a look at php.net's object orientation tutorial. Its not the best introduction to OO, but I'm certain it will turn on a lot of lights for you.

Link to comment
Share on other sites

So if I go with the solution posted by dontpanic at 03:34:46.

 

If I ask the question very differently that may help me achieve what I want.

 

When someone submits a form to add a record (say a new user) I then of course have various checks to make (does username exist?, create new password and encrypt it etc.) If there is an error (username requested already taken or password not minimum number of characters) I then want to store details of these errors so I can display them to the user. How would this best be done (which class / how interact etc)?

Link to comment
Share on other sites

Either make another class, called something like "Logger", and pass an object of that type into your User or Database class's constructor and manage a log from there, or you could parse your class's return values and report in the procedural part of your script.

 

for example,

 

$logger = new Logger();
$myObj = new User($logger);
$myOtherObj = new User($logger); // Uses the same logger!
$myObj->do_something_bad(); // This function fails for whatever reason and reports to $logger

or

 

$myObj = new User();
$ret = $myObj->do_something_bad();
if ($ret == false)
{
    echo 'An error occurred.';
}

Could be possible solutions, but I'm sure you could think up any number of alternatives. This is one of those things that has lots of solutions, some good and some bad, but it all comes down to preference and context. If your class methods are atomic enough (as in, they do exactly one complete operation), it is very easy to check return values and report in the global scope. If your methods are long-winded it may be neccessary to report warnings or other non-critical sorts of things before you want to return. In that case I would go with a logger object that is either global (easy, not safe) or instanced (difficult, but safe). My first example uses a global logger object.

 

 

 

 

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

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.

×
×
  • 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.