Para::Frame::DBIx - Wrapper module for DBI
Para::Frame::DBIx
is an optional module. It will not be used unless
the application calls it.
The application should initiate the connection and store the object in a global or site variable.
Multipple connections are supported.
On error, an 'dbi' exception is thrown.
The object will blessed into a subclass via /rebless, if availible.
Para::Frame::DBIx->new( \%params )
Param connect
can be a string or a ref to a list with up to four
values. Those values will be passed to DBI/connect. Those will be
$data_source, $username, $password and \%attr.
A connect
data_source must be given. If username or password is not
given or undef, the DBI default will be used.
If the connect
attr is not given the paraframe will use the default
of:
RaiseError => 1,
ShowErrorStatement => 1,
PrintError => 0,
AutoCommit => 0,
If param import_tt_params
is true, the TT select functions will be
globaly exported, connected to this database. Alternatively, you could
export the $dbix object as a TT variable globaly or for a particular
site. That would allow you to access all methods. Only one dbix should
import_tt_params.
The default for import_tt_params
is false.
The param bind_dbh
can be used to bind the $dbh object (returned by
DBI/connect) to a specific variable, that will be updated after
each connection. Example: bind_dbh => \ $MyProj::dbh,
The object uses the hooks Para::Frame/on_fork for reconnecting to the database on forks.
It uses hook Para::Frame/on_error_detect to retrieve error information from exceptions and /rollback the database.
It uses hook Para::Frame/done, Para::Frame/before_switch_req and Para::Frame/gefore_render_output for times to /commit.
The constructor will not connect to the database, since we will probably fork after creation. But you may want to, after you have the object, use Para::Frame/on_startup.
Returns a object that inherits from Para::Frame::DBIx.
Calls init
method in the subclass.
Example:
$MyProj::dbix = Para::Frame::DBIx -> new({ connect => ['dbi:Pg:dbname=myproj'], import_tt_params => 1, }); Para::Frame->add_hook('on_startup', sub { $MyProj::dbix->connect; });
$dbix->init(\%args);
This may be implemented in subclasses.
The param datetime_formatter
should hold the module name to use to
format dates for this database. The default is based on the DB driver.
It's used by /format_datetime. The module will be required during
object construction.
datetime_formatter
defaults to DateTime::Format::Pg which uses
SQL standard ISO 8601
(2003-01-16T23:12:01+0200), which should work
for most databases.
See /select_list
Stores the result in the session or uses the previously stored result.
See /select_list
Stores the result in the session or uses the previously stored result.
Does the actual select in a fork.
Perl: $dbix->select_list($statement, @vals) Tmpl: select_list(statement, val1, val2, ... )
Executes the $statement, substituting all '?' within with the values.
The select *
part of the statement can be left out.
Template example:
[% FOREACH select_list('from user where age > ?', agelimit) %] <li>[% name ] is [% age %] years old [% END %]
Template example:
[% FOREACH rec IN select_list('from user where age > ?', agelimit) %] <li>[% rec.name ] is [% rec.age %] years old [% END %]
Returns:
a Para::Frame::List with the hash records
Exceptions:
dbi : DBI returned error
Perl: $dbix->select_record($statement, @vals) Tmpl: select_record(statement, val1, val2, ... )
Executes the $statement, substituting all '?' within with the values.
The select *
part of the statement can be left out.
Template example:
[% user = select_record('from user where uid = ?', uid) %] <p>[% user.name ] is [% user.age %] years old
Returns:
a ref to the first hash record
Exceptions:
dbi : DBI returned error
dbi : no records was found
Perl: $dbix->select_possible_record($statement, @vals) Tmpl: select_possible_record(statement, val1, val2, ... )
Executes the $statement, substituting all '?' within with the values.
The select *
part of the statement can be left out.
Template example:
[% user = select_record('from user where uid = ?', uid) %] [% IF user %] <p>[% user.name ] is [% user.age %] years old [% END %]
Returns:
a ref to the first hash record or undef
if no record was found
Exceptions:
dbi : DBI returned error
Perl: $dbix->select_key($field, $statement, @vals) Tmpl: select_key(field, statement, val1, val2, ... )
Executes the $statement, substituting all '?' within with the values. $field should be the primary key or have unique values.
The select *
part of the statement can be left out.
Template example:
[% user = select_key('uid', 'from user') %] [% FOREACH id = user.keys %] <p>$id: [% user.$id.name ] is [% user.$id.age %] years old [% END %]
Returns:
The result is indexed on $field. The records are returned as a ref to a indexhash och recordhashes.
Each index will hold the last record those $field holds that value.
Exceptions:
dbi : DBI returned error
Perl: $dbix->delete($statement, @vals)
Executes the $statement, substituting all '?' within with the values.
Exceptions:
dbi : DBI returned error
$dbix->connect()
Connects to the database and runs hook Para::Frame/after_db_connect.
The constructor adds ha hook for on_fork that will reconnect to dhe database in the child.
Returns true or throws an 'dbi' exception.
$dbix->disconnect()
Disconnect from the DB
$dbix->commit()
Runs the hook Para::Frame/before_db_commit.
Returns the result of DBI/commit
$dbix->rollback()
Starts by doing the rollbak. Then runs the hook Para::Frame/after_db_rollback.
Calls Para::Frame::Change/reset
Returns the change object
$dbix->dbh()
Returns the $dbh object, as returned by DBI/connect.
Example:
$dbix->dbh->do("update mytable set a=?, b=? where c=?", {}, $a, $b, $c);
$dbix->get_nextval($seq)
This is done by the SQL query "select nextval(?)"
that doesn't work
with mysql. Use /get_lastval.
Example:
my $id = $dbix->get_nextval('person_id_sequence');
Returns:
The id
Exceptions:
dbi : Failed to get nextval
$dbix->get_lastval $dbix->get_lastval($sequence)
For MySQL, this retrieves the AUTOINCREMENT
value from
mysql_insertid that corresponds to mysql_insert_id().
If given a sequence, it retrieves the value with "select currval(?)"
.
$dbix1->equals( $dbix2 )
Returns true is they are the same object.
$dbix->parse_datetime( $time, $class )
This uses the datetime_formatter
property of $dbix (as set on
construction).
$class defaults to Para::Frame::Time. Can be used to set the class to a suitable subclass to Para::Frame::Time.
Should be a litle more efficiant than using Para::Frame::Time/get directly.
Returns: a Para::Frame::Time object
$dbix->format_datetime( $time )
This uses the datetime_formatter
property of $dbix (as set on
construction) for returning the time to a format suitable for
the database.
It will parse most formats using Para::Frame::Time/get. Especially
if $time
already is a Para::Frame::Time object.
Returns:
The formatted string
Exceptions:
validation : "Time format '$time' not recognized"
$dbix->update( $table, \%set, \%where )
$table
is the name of the table as a string
\%set
is a hashref of key/value pairs where the key
is the field
name and value
is the new value of the field. The value will be
formatted by /format_value with type undef
.
\%where
is a hashref of key/value pars where the key
is the
field name and value
is the value that field should have.
Returns: The number of rows updated
Exceptions:
dbi : ... full explanation ...
Example:
$dbix->update( 'my_users', { email => $new_email }, { user_id => $u->id })
$dbix->insert( $table, \%rec ) $dbix->insert( \%params )
Insert a row in a table in $dbix
Params are: table = name of table rec = hashref of field/value pairs types = definition of field types
The values will be automaticly formatted for the database. types, if existing, helps in this formatting. /format_value is used for this.
Returns number of rows inserted.
Example:
$dbix->insert({ table => 'person', rec => { id => 12, name => 'Gandalf' updated => now(), }, types => { id => 'string', name => 'string', updated => 'boolean', }, });
Exceptions:
dbi : ... full explanation ...
$dbix->insert_wrapper( \%params )
High level for adding a record.
Params are: rec = hashref of name/value pairs for fields to update map = hashref of translation map for interface to fieldname parser = hashref of fieldname/coderef for parsing values types = hashref of fieldname/type table = name of table unless_exists = listref of fields to check before inserting return_field = what field value to return
Example:
our %FIELDMAP = ( id => 'pid', # The DB field is named pid name => 'username', # The DB field is named username ); our %FIELDTYPES = ( updated => 'date', # Parse this as an date and format ); our %FIELDPARSER = # The coderef returns the object ( # and the object has a id() method project => sub{ MyProj::Project->get(shift) }, ); ... my $rec = { id => 12, name => 'Gandalf', updated => now(), # From Para::Frame::Time project => 'Destroy the Ring', # The db field holds the project id }; my $newperson = MyProj::Person->insert($rec); ... sub insert { my( $class, $rec ) = @_; return MyProj::Person->get_by_id( $Para::dbix->insert_wrapper({ rec => $rec, map => \%FIELDMAP, types => \%FIELDTYPE, parser => \%FIELDPARSER, table => 'person', unless_exists => ['name'], # Avoid duplicates return_field => 'id', # used by get_by_id() })); }
rec
is the field/value pairs. Each field name may be translated by
map
. Defaults to an empty record.
map
is used for the case there the name of the object property
doesn't match the DB field name. We may want use opne naming scheme
for field names and another naming scheme for object properties. One
or the other may change over time.
parser
is used for special translation of the given value to the
value that should be inserted in the database. One good usage for
this is for foreign keys there the field holds the key of a record in
another table. The parser should return the related object. The
/format_value method will be used to get the actual id
by
calling the objects method id
.
types
helps /format_value.
table
is the name of the table. This is the only param that must be
given.
unless_exists
is a field name or a ref to a list of field names. If
a record is found with those fields having the values from the
$rec
, no new record will be created.
return_field
will return the value of this field if defined. If
unless_exists
find an existing record, the value of thats records
field will be returned. May be used as in the example, for returning
the id.
Returns the number of records inserted, unless return_field
is
used. If unless_exists
finds an existing record, the number of
records inserted is 0.
The usage of %FIELDTYPES, et al, in the example, demonstrates how each table should have each own Perl class set up in its own file as a module. The fields could be defined globaly for the class and used in each method dealing with the DB.
Validation of the data has not yet been integrated...
$dbix->update_wrapper( \%params )
High level for updating a record. This will compare two records to detect if anything has changed.
Params are: rec_new = hashref of name/value pairs for fields to update rec_old = hashref of the record to be updated map = hashref of translation map for interface to fieldname parser = hashref of fieldname/coderef for parsing values types = hashref of fieldname/type table = name of table key = hashref of field/value pairs to use as the record key on_update = hashref of field/value pairs to be set on update copy_data = obj to be updated with formatted values if any change
This is similar to /insert_wrapper but most of the job is done by /save_record.
Returns the number of fields changed.
copy_data
will take each field from the translated fields from
$rec_new and their corresponding formatted value, and place them in
the given object or hashref. The point is to update the object with
the given changes.
Example:
my $changes = $dbix->update_wrapper({ rec_new => $rec, rec_old => $me->get( $person->id ), types => \%FIELDTYPE, table => 'person', key => { id => $person->id, }, on_update => { updated => now(), }, fields_to_check => [values %FIELDMAP], map => \%FIELDMAP, copy_data => $person, });
$dbix->format_value_list(@value_list)
Returns a list of formatted values
Plain scalars are not modified
DateTime objects are formatted
Objects with an id method uses the id
Other objects are stringyfied.
$dbix->format_value( $type, $value ) $dbix->format_value( undef, $value )
If type is undef, uses /format_value_list.
Returns the formatted value
Handled types are string
, boolean
and date
.
Exceptions:
validation : Type $type not handled
validation : Value $valstr not a $type
$dbix->save_record( \%params )
High level for saving a record.
Params are: rec_new = hashref of name/value pairs for fields to update rec_old = hashref of the record to be updated table = name of table on_update = hashref of field/value pairs to be set on update key = hashref of field/value pairs to use as the record key keyval = alternative to give all in key param fields_to_check = which fields to check
Returns the number of changes.
If any changes are made, determined by formatting the values and comparing rec_new with rec_old, those fields are updated for the record in the table.
The types handled are string
, integer
, float
, boolean
,
date
and email
.
Exceptions:
action : Type $type not recoginzed
$dbix->report_error( \@values, @params )
If $@ is true, throws a dbi
exception, adding the calles sub name
and the list of values. Those should be the values given to dbh for
the SQL query.
If the connection may have been lost, tries to reestablish it and recalls the method with statement and values.
$dbix->rebless
Checks if there exists a Para::Frame::DBIx::... module matching the connection, for customized interface.
$dbix->table( $name )
Returns: a Para::Frame::DBIx::Table object or undef
if not
existing.
Must be implemented for the DB driver.
$dbix->tables()
Returns: A Para::Frame::List object of Para::Frame::DBIx::Table objects.
Must be implemented for the DB driver.
$dbix->bool($value)
Returns: a boolean true/false value fore use in SQL statements for the DB.
$dbix->state
Returns: a Para::Frame::DBIx::State object representing the current state.