Typical GenericObjectCollection Implementation

Typical GenericObjectCollection Implementation

Here's the code for the new version of the UserHome class (in the file userhome.phpm) that makes use of GenericObjectCollection. You may notice from the fact that this class has member variables that this particular version cannot be used as a static class. It needs to be instantiated as any other does. This requirement is brought about by the pagination methods introduced; shortly, you'll see why we've introduced them.

   <?    class UserHome {      var $items_per_page = 12;      var $item_count;      var $page_count;      public function __construct() {        return(true); # No useful constructor in a Home Class      }      public function SetItemsPerPage($items_per_page) {        $this->items_per_page = $items_per_page;      }      public function GetItemCount() {        return ($this->item_count);      }      public function GetPageCount() {        return ($this->page_count);      }      public function GetAllUsersWithFirstNameBeginningWith($strLetter,$page_    num=1) {        $dbc = new GenericObjectCollection("user", "User");        $sql = new sql();        $strLetter = strtolower($strLetter);        $sql->query("SELECT id FROM \"user\" WHERE lower(first_name) LIKE    '$strLetter%'");        $result_rows = $sql->get_table_hash();        for ($i=0; $i<=sizeof($result_rows)-1; $i++) {          $dbc->AddTuple($result_rows[$i]["id"]);        };        $dbc->SetPageSize($this->items_per_page);        $dbc->PopulateObjectArray($page_num);        $objArray = $dbc->RetrievePopulatedObjects($page_num);        $this->item_count = $dbc->GetItemCount();        $this->page_count = $dbc->GetNumPages();        return($objArray);      }    }    ?> 

Note that the previous class looks quite different from our previous incarnation of UserHome, even though it has the same core method GetAllUsersWithFirstNameBeginningWith and that method appears to return an array of User objects, just as before.

It works quite differently, however, as you'll see.

Try It

Use the following snippet of code to try out the new UserHome class.

   $uH = new UserHome();    $arUsers = $uH->GetAllUsersWithFirstNameBeginningWith('j');    for ($i=0; $i<=sizeof($arUsers)-1; $i++) {      print $arUsers[$i]->GetField("first_name")."".            $arUsers[$i]->GetField("last_name") . "<br />\n";    }; 

You should see exactly the same output as before, if you're still using our test data from earlier:

   John Doe    Jane Doe 

How it Works

The new UserHome class does not extend GenericObjectCollection. It simply makes use of it as a support class.

The method to get matching users whose name begins with a given letter starts out much the same as with the traditional method discussed earlier. A crucial additional line exists before the query takes place, however:

   $dbc = new GenericObjectCollection("user", "User"); 

This creates a new instance of GenericObjectCollection, mapped to the user table and the User object. Note that the User object must have been declared previously for this to work. The name of the table and the name of the class are both stored as private member variables of GenericObjectCollection and retained for later use.

Things get back to their usual self for a little while as we query the database to find matching users and ask only for their id values.

       $sql = new sql();        $strLetter = strtolower($strLetter);        $sql->query("SELECT id FROM \"user\" WHERE lower(first_name) LIKE    '$strLetter%'");        $result_rows = $sql->get_table_hash(); 

In a similar fashion to earlier, we loop through the resulting data except that this time, rather than simply instantiate a matching GenericObject instance, we add the id value to the GenericObjectCollection class, as follows:


The GenericObjectCollection class simply records this data onto a stack for later use.

We then tell the GenericObjectCollection class a vital piece of information: a page size. In the preceding example, this is simply hard coded into the UserHome class. This information is offered because you are unlikely to ever want to retrieve populated versions of the entirety of your matching entities in one fell swoop. Rather, you will want to retrieve a certain slice of them.

You may well have a thousand matching objects; you aren't likely to want to do anything with a thousand simultaneously, so by setting your page size to, say, 12 items per page, you can retrieve a collection of objects equal only to the size of the page you have specified. This is not just neatness. It is pretty much a must to ensure any degree of performance when working with large numbers of matching entities.

This class-level logic closely mirrors the likely logic for the implementation of your particular application.


All you need to do now is to tell the GenericObjectCollection class to give you that particular slice of objects as an array. This method accepted a page number parameter, defaulting to 1 if not specified. You simply pass this on, here:


In response, the GenericObjectCollection now constructs and executes a query like that shown previously:

   SELECT * FROM "user" WHERE id IN (12091, 12092, 12093, 12094, 12095,    12096, 12097, 12098, 12099, 12100) 

This simple list of id values is constructed using the _GetCommaSeparatedIDList() internal method. This takes start and end limits as its parameters, which are derived from the current page number. The list of id values returned corresponds to these limits, so, for example, if 50 and 60 are passed as respective start and end limits, the 50th thru 60th matching id values returned by the original query will be included in the output list of id values.

Before making use of the query results, the class first creates an internal collection of user objects in a loop. It uses the eval method because when you originally instantiated the GenericObjectCollection class, you passed the name of the class you are using as a string. The eval method allows you to build up and then execute PHP code dynamically or on the fly, as shown:

   $refObjArrayIndexObj = &$this->obj_array[$this_index];    $s="\$refObjArrayIndexObj = new ".$this->class_name."(".$this_db_row_id.");";    eval($s); 

The value of $this_index is calculated using the internal _GetIndexFromTupleID() method, which matches the position in the complete array of matching objects to the row id value currently being examined in the query output.


You may note that the preceding example doesn't make use of the collection class discussed in Chapter 5. Rather, it simply returns an array of objects. This means you can't do fancy things such as run an iterator over the collection. To keep things simple, though, we've left it as it is. It's not hard to adapt if you feel strongly about it.

The method then refers to the grid of data generated by the query to repeatedly use the SetField property of that GenericObject instance to make its various fields match the results returned by the database:

   $refObjArrayIndexObj->ForceLoaded();    foreach ($this_row as $key => $value) {      if (!(is_numeric($key))) {        $refObjArrayIndexObj->SetField($key, $value);      };    }; 

By using the ForceLoaded method, the GenericObjectCollection class can trick the GenericObject instance in question into "thinking'' it has been loaded. This is perfectly okay because all the fields of that class are being set right now by GenericObjectCollection, using the grid of data it has just retrieved from the database.

Finally, back to UserHome, where you can now see it retrieving the array of instantiated objects:

   $objArray = $dbc->RetrievePopulatedObjects($page_num); 

Again, start and end limits are quickly calculated from the supplied page number and the appropriate slice of objects taken and returned.

You can also see the results of the enforced pagination by getting the total number of matches and, from there, the total number of pages (some fairly simple math is at work here):

   $this->item_count = $dbc->GetItemCount();    $this->page_count = $dbc->GetNumPages(); 

These values can then be retrieved using the exposed methods of the UserHome class.

GenericObjectCollection Summary

The GenericObjectCollection class can seem daunting at first; it's certainly a large amount of code to perform a relatively simple task.

In fact, its source code belies the simplicity of its essential premise. It prepopulates an array of instantiated GenericObject-extended classes with data so that when they are dealt with as a set rather than in isolation, the number of SQL queries that needs to be performed against your database is kept to a minimum.

Any kind of administration application that needs to display some kind of tabular, paginated view of entities on a system and then allows your user to drill down to view or edit that entity will benefit immensely from using this hierarchy.

Experimentation is the key to understanding. Why not have a go at rewriting some of your existing applications to make use of the GenericObject and GenericObjectCollection classes? They will be used extensively in the rest of this book, so it's worth getting to know them well now.

Professional PHP5 (Programmer to Programmer Series)
Professional PHP5 (Programmer to Programmer Series)
Year: 2003
Pages: 182

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net