Class Storable

Description

Simple storage class.

Storage is a simple class to allow storage of objects into a backend storage, which is for now limited to what PEAR DB supports.

This is a rewrite from ecdTable and ecdIterator, a hacky class I've put together some time ago to ease my work.

Improvements over ecdTable:

  • uses PEAR::DB
  • integrates the iterator (next())
Disadvantages:
  • not finished and tested as ecdTable and ecdIterator
  • after tests and programming with it, i find it a bit more confusing and less orthogonal than the previous pair

  • see: Storable::Storable()
  • todo: Missing features from ecdIterator:
    • save when join != null
    • auto_increment stuff
    More documentation in the constructor.

Located in /Storable.php (line 79)


	
			
Direct descendents
Class Description
Customer Customer definition
TimeEntry A time entry for a project and user
User The representation of a user
User_Customer
User_Project
StorableTree An implementation of the TreeInSql pattern
Variable Summary
mixed $_count
array $_data
array $_data_orig
mixed $_db
mixed $_dsn
boolean $_exists
string $_join
mixed $_match
int $_offset
mixed $_order
mixed $_res
string $_select
Method Summary
Storable Storable ([mixed $match = null], [int $offset = null], [int $count = null], [string $order = null], [string $select = null], [string $join = null])
void backup_data ()
void connect ()
int count ()
void disconnect ()
boolean exists ()
void feed_hash (Array $hash, [Array $only_keys = null])
mixed get (string $field)
a get_data ()
bool handle_result (mixed $res)
void is_limited ()
whatever next ()
int num_rows ()
int offset ()
boolean remove ()
mixed save ()
void set (string $field, mixed $data)
void set_data (array $hash)
void start_request ()
string tablename ()
string where_clause (array $match)
Variables
mixed $_count (line 143)
array $_data = array() (line 122)

the internal data structure

  • access: protected
array $_data_orig = null (line 132)

a copy of the original data structure, saved before set(). Used to create the WHERE clause in save()

mixed $_db (line 91)

the main database handle

  • access: protected
mixed $_dsn = _DSN (line 105)

the string to pass to DB to connect to the database.

Initialized to the _DSN constant, but can be overriden in child classes.

  • access: protected
boolean $_exists = false (line 114)

if this object's match request exists

  • access: protected
string $_join = '' (line 164)

the join statement, inserted between the FROM and the WHERE

  • access: protected
mixed $_match (line 97)

the match pattern used to find this entry

  • access: protected
int $_num_rows (line 172)

a cache of the numRows*() functions

  • access: protected
mixed $_num_rows_unlimited (line 172)
int $_offset (line 143)

the offset and count

intialized in the constructor, used to create the LIMIT part of the SELECT.

  • access: protected
mixed $_order (line 148)

What comes after the ORDER BY statement.

mixed $_res = null (line 94)

the main SELECT result database handle

  • access: protected
string $_select = "*" (line 156)

what rows to SELECT defaults to *

  • access: protected
Methods
Constructor Storable (line 308)

Main constructor

This constructor takes care of most of the initialization of the internal structures. It is how data is "loaded" from the storage, into the internal $_data array. A lookup into the database is made in function of the $match parameter.

Example usage:

  1. require_once('Storable.php');
  2.  
  3. class Users extends Storable {}
  4.  
  5. /* list all entries in the 'user' table */
  6. $u = new User();
  7. // option 1: current API
  8. while ($u->next()) {
  9. print $u->get('name') . '<br />';
  10. }
  11. // option 2: if next() was called in the constructor
  12. for ($u = new User("name LIKE '%oey%'"); $u->exists() ; $u->next()) {
  13. print "...";
  14. }

But the most interesting facette about this class is how you can use it to save data back to the storage:

  1. $u = new User(array('name' => 'joey'));
  2.  
  3. if ($u->next()) {
  4. $u->set('name', 'joe');
  5. } else {
  6. print "not found, creating new";
  7. }
  8. $u->save();
  9.  
  10. // You could also eventually do this, but Storable would then need
  11. // to DESCRIBE the table and fetch the primary key values to be
  12. // able to save
  13. $u = new User("dfjfkldsjfs", null, null, null, "* JOIN `tablename` ON jkflkdsjlsd");
  14. $u = new User("dfjfkldsjfs", null, null, null, "name,desc");

Note: this constructor doesn't call next() anymore, so the proper way to loop over next() is with a while loop:

  1. $store = new Storable();
  2. while ($store->next()) {
  3. // do stuff
  4. }

While you also have to call next() to load a single object, in opposition with how ecdTable traditionnally worked, but much like how ecdIterator works:

  1. $store = new Storable(array('foo' => 'bar'));
  2. $store->next();
  3. $store->set('foo', 'quux');
  4. $store->save();

Storable Storable ([mixed $match = null], [int $offset = null], [int $count = null], [string $order = null], [string $select = null], [string $join = null])
  • mixed $match:

    Either:

    an associative array where key => value pairs correspond to columns => values. The key and value are equal'd and AND'd in a WHERE request.

    $match can also be a string, in which case it is taken verbatim as a WHERE. The programmer is then responsible of properly escaping the request. Also, this can create problems when the WHERE does not match anything and the programmer creates a row from the resulting object: Storable::save() will attempt an INSERT which might fail if the row already exists.

    Example:

    1. $user = new User(Array('firstname' => 'joe', 'lastname' => 'smith'));

    is equivalent to:

    1. $user = new User("`firstname` = 'joe' AND `lastname` = 'smith'");

  • int $offset: the number of elements to skip before considering results. Equivalent to the offset parameter to LIMIT. Defaults to 0 (skip no element, start from the first).
  • int $count: the number of elements to give. Equivalent to the row_count parameter to LIMIT. Defaults to -1 (everything).
  • string $order: an arbitrary string appended after the LIMIT, usually a ORDER BY statement, but could be anything.
  • string $select:

    the SELECT part, * by default. Be *really* careful with what you put here, as Storable relies on the result set to construct a WHERE clause for the INSERT or UPDATE queries, when the object is saved back in the storage.

    Example:

    1. // this will work
    2. $st = new Storable(null, null, null, null, "primarykey,anothercol");
    3. $st->next(); $st->save();
    4.  
    5. // this is almost guarenteed to crash
    6. $st = new Storable(null, null, null, null, "notaprimarykey");
    7. $st->next(); // this works, though
    8. $st->save();

  • string $join: the JOIN part, which is inserted between the FROM and the WHERE clauses. Is empty by default. Also be very careful with this as save() will attempt to save the columns from the joined table in the original table, which will cause queries to fail.
backup_data (line 775)

backup the data array in $_data_orig for later use, but do not override $_data_orig if it already exists.

void backup_data ()
connect (line 652)

Take care of the DB magic to connect to the database

void connect ()
count (line 638)

The number of items requested in the constructor.

This is the number of items this request is limited to.

int count ()
disconnect (line 670)

disconnect the database handle

  • access: protected
void disconnect ()
exists (line 618)

does this object exists in the storage?

  • return: if this object exists in the storage
boolean exists ()
feed_hash (line 586)

Set the values of this object to the keys and values of the array.

void feed_hash (Array $hash, [Array $only_keys = null])
  • Array $hash: col -> value associative array
  • Array $only_keys: import only those keys from the $hash array
get (line 575)

Get a given field value.

Behavior when fetching an unknown field is undefined.

  • return: the field value
mixed get (string $field)
  • string $field: the field name
get_data (line 609)
  • return: copy of the $_data array
a get_data ()
handle_result (line 692)

Deal with results from PEAR DB queries

Will trigger a fatal (E_USER_ERROR) if DB::isError() returns true, with the error message. The debug info is also printed in a E_USER_NOTICE (so it is not visible by default) to aid debugging.

Cannot be called statically

bool handle_result (mixed $res)
is_limited (line 531)
void is_limited ()
next (line 365)

Load the next row available in this object.

If a row is found matching the parameter, it is loaded into the $_data array and subsequently accessible from get() calls. If more than one row are available, they can be loaded into the object by calling next().

If no row is found, the $match array is loaded in the $_data array, and the object is marked as non-existant (exists() == false). This allows easy object creation:

  1. $nonexist = new Storable(array('name' => 'nonexistent'));
  2. $nonexist->save();

However, the above code will fail if the object does exist, since Storable is not aware of that until next() is called.

  • return: DB::fetchRow(DB_FETCHMODE_ASSOC) returns. That is: a truth value (the associative array representing the row) on succcess, and FALSE on failure.
  • uses: Storable::start_request()
  • uses: Storable::connect()
  • uses: DB::fetchRow(DB_FETCHMODE_ASSOC)
whatever next ()

Redefined in descendants as:
num_rows (line 487)

The number of rows matchnig all predicates.

Note: this can be lower or equal (but not higher) than the requested count, if the number of rows matching the predicates is smaller the the requested count.

int num_rows ()
num_rows_unlimited (line 514)

Number of matches without count() or offset().

  • return:

    number of matches without LIMIT

    If LIMIT is set, this makes a new request to check the total number of rows matching all predicates *except* the LIMIT parameter. If no LIMIT is set (ie. offset() is 0 and count() is -1) it just returns num_rows().

    In other words, this is like num_rows(), but disregarding the offset() and count() parameters.

  • uses: DB::query()
  • uses: DB::numRows()
  • uses: DB::quoteIdentifier()
  • uses: Storable::where_clause()
  • uses: Storable::handle_result()
  • uses: Storable::num_rows()
int num_rows_unlimited ()
offset (line 626)

The offset requested in the constructor.

int offset ()
remove (line 457)

Remove record represented by object in database.

The matching row(s) is(are) deleted on the spot, so no save() is necessary.

Sets this object to be new (exists() == false) if row was successfully deleted.

Warning: this will delete *all rows matching $match* if next() is not called to load an object before calling remove().

Example:

  1. $store = new Storable(array("color" => "red"));
  2. $store->next(); $store->remove(); // remove the first red object
  3.  
  4. $store = new Storable(array("color" => "red"));
  5. $store->remove();

boolean remove ()
save (line 406)

Save the object in underlying storage.

If the object exists(), the request done will be an UPDATE, otherwise it will obviously be an INSERT. save() uses some magic from PEAR (autoExect()) to ease the construction of the request.

save() will not try to save an unmodified object (one that hasn't had set() called on it).

save() will happily try to save non-existant fields. It is the duty of the programmer to make sure the fields exist in the underlying storage.

The where_clause() is based on the original result set fetched from the database ($_data_orig pour les intimes), and totally ignores primary keys. This has the advantage of avoiding costly DESCRIBE statement in the constructor, but might create problems with JOINs or custom SELECT statements.

mixed save ()

Redefined in descendants as:
set (line 562)

Set given field to the given data.

Warning: setting data in a non-existent field might cause a save() query to fail

void set (string $field, mixed $data)
  • string $field: the field name to set
  • mixed $data: the data to set it to
set_data (line 601)

Assign the associative array directly to the internal structures.

void set_data (array $hash)
  • array $hash: an associative "column => value" array
start_request (line 751)

Construct the SELECT statement and call the actual query.

Construct the proper SELECT with the LIMIT, and do the actual query(), but only if not already done.

This is simply code factored out of next() and num_rows().

void start_request ()
tablename (line 544)

What table represents this object. Currently returns the lowercased class name.

Can of course be overriden in child classes.

string tablename ()

Redefined in descendants as:
where_clause (line 719)

Generate a valid WHERE clause.

The match array is AND'd. If $match is not an array, it is used verbatim, so it is the duty of the programmer to escape it properly.

cannot be called statically (because of DB::quoteIdentifier())

string where_clause (array $match)
  • array $match: an associative 'column' => 'value' array

Documentation generated on Tue, 11 Jan 2005 01:38:28 -0500 by phpDocumentor 1.3.0RC3