Jump to content
Larry Ullman's Book Forums

Recommended Posts

I'm struggling to understand how class inheritance works. I have 2 classes, one inherited by the other.

class DatabaseObject {
   public static function find_all() {
	return static::find_by_query("SELECT * FROM " . self::$table_name);
	}
}

class Photograph extends DatabaseObject {

   public function image_path() {
      return $this->upload_dir .DS. $this->filename;
    }
}


In another file I try to access the method

$photos = Photograph::find_all();
foreach ($photos as $photo) {
	echo '<figure><img src=../"'. $photo->image_path() . '" width="100"><figcaption>' . $photo->caption . ' ' . $photo->size_as_text() . '</figcaption></figure>';
}

 

but get the error message

Fatal error: Call to undefined method DatabaseObject::image_path() in /Applications/MAMP/htdocs/photo_gallery/public/admin/view_photos.php on line 22

I don't understand why its trying to access the method through the DatabaseObject class when I have specified the Photograph class. Would anyone be able to explain what is going on here?

 

Hopefully I've provided enough information. Thanks

Link to comment
Share on other sites

Edward - thanks for your responses and your questions as they are raising fundamentals that I haven't quite got to grips with yet.

 

find_all() is a static method defined in the databaseObject class which is inherited by other classes including Photograph. The find_all() method gets all the records in the database, instantiates each record as a new instance of the calling class and returns an array of objects. so $photos in the original code should be an array of Photograph objects which I can loop through and display.

 

I've dumped out the returned array being returned and its full of objects not fully formed e.g.

 

Here is my databaseObject class

array(3) {
  [0]=>
  object(DatabaseObject)#5 (1) {
    ["db_fields":protected]=>
    array(0) {
    }
  }
  [1]=>
  object(DatabaseObject)#6 (1) {
    ["db_fields":protected]=>
    array(0) {
    }
  }
  [2]=>
  object(DatabaseObject)#7 (1) {
    ["db_fields":protected]=>
    array(0) {
    }
  }
}

 

class DatabaseObject {
	
	protected static $table_name;
	protected $db_fields=array();
	//common database methods
	
	public static function find_all() {
		return static::find_by_query("SELECT * FROM " . self::$table_name);
	}
	
	public static function find_by_id($id) {
		global $database;
		$result_array = static::find_by_query("SELECT * FROM  " . self::$table_name . " WHERE id = {$id} LIMIT 1");
		return !empty($result_array) ? array_shift($result_array) : false;
	}
	
	public static function find_by_query($q="") {
		global $database;
		$r = $database->query($q);
		$object_array = array();
		while ($row = $database->fetch_array($r)) {
			$object_array[] = static::instantiate($row);
		}
		return $object_array;
	}
	
	protected static function instantiate($record) {
		$object = new self;
		foreach ($record as $attribute=>$value) {
			if ($object->has_attribute($attribute)){
				$object->attribute = $value;
			}
		}
		return $object;
	}
	
	protected function has_attribute($attribute) {
		// returns an assoc array with all attributes as the
		// keys and their current values as the values
		$object_vars = $this->attributes();
		
		//use array_key_exists to determine if the passed attribute exists as an
		// attribute of the current object 
		return array_key_exists($attribute, $object_vars);
	}
	
	protected function attributes() {
		//return an array of attribute key value pairs for db fields only
		$attributes=array();
		foreach ($this->db_fields as $field) {
			if (property_exists($this, $field)) {
			$attributes[$field]=$this->$field;
			}
		}
		return $attributes;
	}

and here is the Photograph class

class Photograph extends DatabaseObject {

	protected static $table_name="photographs";
	public $db_fields=array('id', 'filename', 'type', 'size', 'caption');
	
	//attributes correponding to d/b table columns
	public $id;
	public $filename;
	public $type;
	public $size;
	public $caption;

I imagine I've made a mess of the visibility and I'm not sure that the array $db_fields  from the Photograph class has been properly coded. Hope this makes sense and you can give me some guidance. If you don't want to look through all this, I understand.

Link to comment
Share on other sites

The reason for the error message is your problem here. Static methods are not inherited as they are static. Static methods do not get inherited by childeren that extends a parent.

 

I would GUESS the confusion comes after seing YII code. As that is a framework, it can't be looked at as normal PHP code. However, this is done through some "magic" that hides the essential parts of the code. Larry might be able to clarify on this. It's way over my head without diving into the framework. (I can't give a CLEAR explanation at least)

 

A better suggestion might be to look at another library using the factory pattern. RedBeans PHP is an ORM (Active Records is an ORM pattern) build in PHP. It also uses the factory pattern for object creation. Here is how you perform simple CRUD:

 

// Insert (Note: Returns Post objects)
$post = R::dispense('post');
$post->text = 'Hello World';

//Create or Update
$id = R::store($post);

// Get
$post = R::load('post',$id);

// Delete
R::trash($post);             
 


This is very similar to something like User::get_by_pk() or what it's called in YII. However, these methods are not INHERITED. A possible solution is using the magic method like __get() and __set(). (which I actually know YII does) These methods could do a call to a method found in ActiveRecords (which most DB models in YII extend) looking something like: (__set() is as we know called when a method is not found in a class)

__set( $method ) {
   return parent::set(__CLASS__, $method)
}
 


Active Records could then transform that into something like: (ta-da)

__set( $class = __CLASS__ ) {
   return ActiveRecordsClass::$method($class);
}
 

Obviously, this is framework magic and not very interesting for most of us, but that's a very plausible explanation at least. :)

 

Hope my stupid ramblings clearify something atleast.

  • Upvote 1
Link to comment
Share on other sites

I'm not using / haven't looked at a framework.

 

I was under the impression that static methods and properties  could be inherited by child classes as long as the visibility was set correctly i.e. not private. Maybe I should move the static methods find_by_id(), find_all() and find_by_query() into the child classes but seems a shame to repeat all that code in several classes. I was hoping with the use of late static binding to be able to make the databaseObject class more abstract.

 

Because the objects aren't being instantiated properly, I wonder if the problem is how the array $db_fields is set up.

Link to comment
Share on other sites

Getting closer - I knew that something was wrong with how the object was being instantiated.. Have changed the method to use get_called_class in the instantiate method.

protected static function instantiate($record) {
   $class_name = get_called_class();
   $object = new $class_name;
   foreach ($record as $attribute=>$value) {
      if ($object->has_attribute($attribute)){
	   $object->attribute = $value;
      }
   }
   return $object;
}

now the object looks like this

array(4) {
  [0]=>
  object(Photograph)#5 (11) {
    ["db_fields"]=>
    array(5) {
      [0]=>
      string(2) "id"
      [1]=>
      string(8) "filename"
      [2]=>
      string(4) "type"
      [3]=>
      string(4) "size"
      [4]=>
      string(7) "caption"
    }
    ["id"]=>
    NULL
    ["filename"]=>
    NULL
    ["type"]=>
    NULL
    ["size"]=>
    NULL
    ["caption"]=>
    NULL
    ["temp_path":"Photograph":private]=>
    NULL
    ["upload_dir":protected]=>
    string(6) "images"
    ["errors"]=>
    array(0) {
    }
    ["upload_errors":protected]=>
    array(8) {
      [0]=>
      string(26) "File uploaded successfully"
      [1]=>
      string(41) "File size larger than upload_max_filesize"
      [2]=>
      string(40) "File size larger than form MAX_FILE_SIZE"
      [3]=>
      string(28) "File only partially uploaded"
      [4]=>
      string(19) "File does not exist"
      [6]=>
      string(34) "Temporary directory does not exist"
      [7]=>
      string(19) "Can't write to disk"
      [8]=>
      string(34) "File upload prevented by extension"
    }
    ["attribute"]=>
    string(10) "sunflowers"
  }

Need to figure out why the values from the d/b aren't getting into the object.

Link to comment
Share on other sites

Found the error. This line in the instantiate function

$object->attribute = $value;

should be

$object->$attribute = $value;

This part of the project working nicely now - I can extend databaseObject class to User class and Photograph class and all the CRUD is taken care of. Now to try to create a logfile using sessions so I will probably be back :)

Link to comment
Share on other sites

erm i don't know if this would help or not, but try to use

 

 

new static() //for static binding will return the class that call it
 

 

 

instead of

 

new self() // will return the class that have the method definition (if u dont override the method)
 

 

note: sorry for the bad english..

 

 

correct me if im wrong

Link to comment
Share on other sites

To clarify my understanding, it would be good to get some feedback re some of the comments in this thread.

 

Static methods do not get inherited by childeren that extends a parent.

In my parent class(databaseObject) is a public static function find_by_all() which I'm accessing via child classes (Comment and Photograph)

If one method is static I think they all have to be?

 I've not seen this before and my classes have both static and non static functions.

 

new self() // will return the class that have the method definition (if u dont override the method)

I understood the point of inheritance was that the child class could access the parent method through an instance of itself (the child) without the need for overriding.
 

Link to comment
Share on other sites

im still newbie and i dont know much, but from what i know ,

 

protected static function instantiate($record) {
        $object = new self;
        foreach ($record as $attribute=>$value) {
            if ($object->has_attribute($attribute)){
                $object->attribute = $value;
            }
        }
        return $object;
    }
 

 

new self() will return the class that has define the method(even if u call Photograph::instantiate()) which is your DatabaseObject.

 

then in that code, u try to do :

 

 $object->attribute = $value 
/*  $object is instance of DatabaseObject because of the new self() , and because of that, it doesn't have the attribute u need..  */
 

 

its like this case :

 

 

class ParentObject
{
    protected static function getObject()
    {
        return new self();
    }
}

class ChildObject extends ParentObject
{
    public static function getObjectChild()
    {
        return self::getObject();
    }
}

$obj = ChildObject::getObjectChild();


echo '<pre>' . print_r($obj) . '</pre>';

 

 

the print_r will show ParentObject, but if u change the ParentObject::getObject() to :

 

 

class ParentObject
{
    protected static function getObject()
    {
        return new static();
    }
}
 

 print_r will show ChildObject();

 

CMIIW


 

Link to comment
Share on other sites

 Share

×
×
  • Create New...