Trying to populate collection only as needed using callback function
I am going through WROX Professional PHP5 Chapter 5 - Collections.
I am trying to populate a collection from a DB (MSSQL) only as members are called, rather than populate everything, using callback functions as detailed in the book. I just can't get the result as expected, which is: "Bob Smith is currently enrolled in CS101". Hopefully the code will explain:
//factoryTest.php
<?php
require_once 'class.Collection.php';
require_once 'class.CourseCollection.php';
require_once 'class.Course.php';
require_once 'class.Student.php';
require_once 'class.StudentFactory.php';
$studentID = 1;
try {
$objStudent = StudentFactory::getStudent($studentID);
$objStudent2 = StudentFactory::getCoursesForStudent($studentID, $col);
} catch (Exception $e) {
die("Student #$studentID doesn't exist in the database!");
}
print $objStudent .
($objStudent->courses->exists("CS101") ? ' is ' : ' is not ').
'currently enrolled in CS101';
//displays: "Bob Smith is enrolled in CS101"
?>
//class.StudentFactory.php
<?php
class StudentFactory {
public static function getStudent($id) {
$serverName = "";
$connectionInfo = array("Database"=>"Registra");
$con = sqlsrv_connect($serverName, $connectionInfo)
or die("Failure connecting to database!");
$sql = "SELECT * FROM \"student\" WHERE \"studentid\" = $id";
$result = sqlsrv_query($con, $sql);
if ($result === false) {
throw new Exception("Student $id does not exit.");
} else {
$row = sqlsrv_fetch_array($result, SQLSRV_FETCH_ASSOC);
return new Student($row['studentid'], $row['name']);
}
}
public static function getCoursesForStudent($id, $col) {
$serverName = "";
$connectionInfo = array("Database"=>"Registra");
$con = sqlsrv_connect($serverName, $connectionInfo)
or die("Failure connecting to database!");
$sql = "SELECT \"course\".\"courseid\",
\"course\".\"coursecode\",
\"course\".\"name\"
FROM \"course\"
INNER JOIN \"studentcourse\"
ON \"course\".\"courseid\" = \"studentcourse\".\"courseid\"
INNER JOIN \"student\"
ON \"studentcourse\".\"studentid\" = \"student\".\"studentid\"
WHERE
\"course\".\"courseid\" =
\"studentcourse\".\"courseid\" AND
\"studentcourse\".\"studentid\" = $id";
$res = sqlsrv_query($con, $sql);
if (sqlsrv_has_rows($res)) {
$rowCount = sqlsrv_num_rows($res);
while ($row = sqlsrv_fetch_array($res, SQLSRV_FETCH_ASSOC)) {
$objCourse = new Course($row['courseid'], $row['coursecode'],
$row['name']);
$col->addItem($objCourse, $objCourse->getCourseCode());
// echo $row['courseid']." ".$row['coursecode']." ".$row['name'];
}
return $col;
}
}
}
?>
//class.Student.php
<?php
class Student {
private $_id;
private $_name;
public $courses;
public function __construct($id, $name) {
$this->_id = $id;
$this->_name = $name;
$this->courses = new CourseCollection();
$this->courses->setLoadCallback('_loadCourses', $this);
}
public function getName() {
return $this->_name;
}
public function getID() {
return $this->_id;
}
public function _loadCourses(CourseCollection $col) {
$courses = StudentFactory::getCoursesForStudent($this->_id, $col);
}
public function __toString() {
return $this->_name;
}
}
?>
//class.CourseCollection.php
<?php
class CourseCollection extends Collection {
public function addItem(Course $obj, $key = null) {
parent::addItem($obj, $key);
}
}
?>
//class.Course.php
<?php
class Course {
private $_id;
private $_courseCode;
private $_name;
public function __construct($id, $courseCode, $name) {
$this->_id = $id;
$this->_courseCode;
$this->_name;
}
public function getName() {
return $this->_name;
}
public function getID() {
return $this->_id;
}
public function getCourseCode() {
return $this->_courseCode;
}
public function __toString() {
return $this->_name;
}
}
?>
//class.Collection.php
<?php
class Collection {
private $_members = array(); // Collection members.
private $_onload; // Holder for callback function
private $_isLoaded = false; // Flag that indicates whether the
// has been invoked.
public function addItem($obj, $key = null) {
$this->_checkCallback();
if ($key) {
if (isset($this->_members[$key])) {
throw new KeyInUseException("Key \"$key\" already in use!");
} else {
$this->_members[$key] = $obj;
}
} else {
$this->_members[] = $obj;
}
}
public function removeItem($key) {
$this->_checkCallback();
if (isset($this->_members[$key])) {
unset($this->_members[$key]);
} else {
throw new KeyInvalidException("Invalid key \"$key\"!");
}
}
public function getItem($key) {
$this->_checkCallback();
if (isset($this->_members[$key])) {
return $this->_members[$key];
} else {
throw new KeyInvalidException("Invalid key \"$key\"!");
}
}
public function length() {
$this->_checkCallback();
return sizeof($this->_members);
}
public function keys() {
$this->_checkCallback();
return array_keys($this->_members);
}
public function exists($key) {
$this->_checkCallback();
return (isset($this->_members[$key]));
}
/**
* Use this method to define a function to be invoked prior to accessing the
* collection. The function should take a collection as its sole parameter.
*/
public function setLoadCallback($functionName, $objOrClass = null) {
if ($objOrClass) {
$callback = array($objOrClass, $functionName);
} else {
$callback = $functionName;
}
// Make sure the function/method is valid.
if (! is_callable($callback, false, $callableName)) {
throw new Exception("$callableName is not callable as a " .
"parameter to onload");
return false;
}
$this->_onload = $callback;
}
/**
* Check to see if a callback has been defined and if so,
* whether or not it has already been called. If not,
* invoke the callback function.
*/
private function _checkCallback() {
if (isset($this->_onload) && ! $this->_isLoaded) {
$this->_isLoaded = true;
call_user_func($this->_onload, $this);
}
}
}
class KeyInvalidException extends Exception {}
class KeyInUseException extends Exception {}
?>
I always get "Bob Smith is not currently enrolled in CS101".
Even in a foreach loop:
foreach ($objStudent->courses as $objCourse) {
print $objCourse->getCourseCode." - ".$objCourse->getName"
}
I don't get anything. What am I doing wrong? What am I missing? Is it going out of scope?
The DB is connecting and returning correctly!
Hope you can help - cheers