NAME¶
DBIx::SearchBuilder::Record - Superclass for records loaded by SearchBuilder
SYNOPSIS¶
package MyRecord;
use base qw/DBIx::SearchBuilder::Record/;
sub _Init {
my $self = shift;
my $DBIxHandle =
shift; # A DBIx::SearchBuilder::Handle::foo object for your database
$self->_Handle($DBIxHandle);
$self->Table("Users");
}
# Tell Record what the primary keys are
sub _PrimaryKeys {
return ['id'];
}
# Preferred and most efficient way to specify fields attributes in a derived
# class, used by the autoloader to construct Attrib and SetAttrib methods.
# read: calling $Object->Foo will return the value of this record's Foo column
# write: calling $Object->SetFoo with a single value will set Foo's value in
# both the loaded object and the database
sub _ClassAccessible {
{
Tofu => { 'read' => 1, 'write' => 1 },
Maz => { 'auto' => 1, },
Roo => { 'read' => 1, 'auto' => 1, 'public' => 1, },
};
}
# A subroutine to check a user's password without returning the current value
# For security purposes, we didn't expose the Password method above
sub IsPassword {
my $self = shift;
my $try = shift;
# note two __s in __Value. Subclasses may muck with _Value, but
# they should never touch __Value
if ( $try eq $self->__Value('Password') ) {
return (1);
}
else {
return (undef);
}
}
# Override DBIx::SearchBuilder::Create to do some checking on create
sub Create {
my $self = shift;
my %fields = (
UserId => undef,
Password => 'default', #Set a default password
@_
);
# Make sure a userid is specified
unless ( $fields{'UserId'} ) {
die "No userid specified.";
}
# Get DBIx::SearchBuilder::Record->Create to do the real work
return (
$self->SUPER::Create(
UserId => $fields{'UserId'},
Password => $fields{'Password'},
Created => time
)
);
}
DESCRIPTION¶
DBIx::SearchBuilder::Record is designed to work with DBIx::SearchBuilder.
What is it trying to do.¶
DBIx::SearchBuilder::Record abstracts the agony of writing the common and
generally simple SQL statements needed to serialize and De-serialize an object
to the database. In a traditional system, you would define various methods on
your object 'create', 'find', 'modify', and 'delete' being the most common. In
each method you would have a SQL statement like:
select * from table where value='blah';
If you wanted to control what data a user could modify, you would have to do
some special magic to make accessors do the right thing. Etc. The problem with
this approach is that in a majority of the cases, the SQL is incredibly simple
and the code from one method/object to the next was basically the same.
<trumpets>
Enter, DBIx::SearchBuilder::Record.
With::Record, you can in the simple case, remove all of that code and replace it
by defining two methods and inheriting some code. Its pretty simple, and
incredibly powerful. For more complex cases, you can, gasp, do more
complicated things by overriding certain methods. Lets stick with the simple
case for now.
The two methods in question are '_Init' and '_ClassAccessible', all they really
do are define some values and send you on your way. As you might have guessed
the '_' suggests that these are private methods, they are. They will get
called by your record objects constructor.
- '_Init'
- Defines what table we are talking about, and set a variable to store the
database handle.
- '_ClassAccessible
- Defines what operations may be performed on various data selected from the
database. For example you can define fields to be mutable, or immutable,
there are a few other options but I don't understand what they do at this
time.
And really, thats it. So lets have some sample code.
An Annotated Example¶
The example code below makes the following assumptions:
- •
- The database is 'postgres',
- •
- The host is 'reason',
- •
- The login name is 'mhat',
- •
- The database is called 'example',
- •
- The table is called 'simple',
- •
- The table looks like so:
id integer not NULL, primary_key(id),
foo varchar(10),
bar varchar(10)
First, let's define our record class in a new module named
"Simple.pm".
000: package Simple;
001: use DBIx::SearchBuilder::Record;
002: @ISA = (DBIx::SearchBuilder::Record);
This should be pretty obvious, name the package, import ::Record and then define
ourself as a subclass of ::Record.
003:
004: sub _Init {
005: my $this = shift;
006: my $handle = shift;
007:
008: $this->_Handle($handle);
009: $this->Table("Simple");
010:
011: return ($this);
012: }
Here we set our handle and table name, while its not obvious so far, we'll see
later that $handle (line: 006) gets passed via ::Record::new when a new
instance is created. Thats actually an important concept, the DB handle is not
bound to a single object but rather, its shared across objects.
013:
014: sub _ClassAccessible {
015: {
016: Foo => { 'read' => 1 },
017: Bar => { 'read' => 1, 'write' => 1 },
018: Id => { 'read' => 1 }
019: };
020: }
What's happening might be obvious, but just in case this method is going to
return a reference to a hash. That hash is where our columns are defined, as
well as what type of operations are acceptable.
021:
022: 1;
Like all perl modules, this needs to end with a true value.
Now, on to the code that will actually *do* something with this object. This
code would be placed in your Perl script.
000: use DBIx::SearchBuilder::Handle;
001: use Simple;
Use two packages, the first is where I get the DB handle from, the latter is the
object I just created.
002:
003: my $handle = DBIx::SearchBuilder::Handle->new();
004: $handle->Connect( 'Driver' => 'Pg',
005: 'Database' => 'test',
006: 'Host' => 'reason',
007: 'User' => 'mhat',
008: 'Password' => '');
Creates a new DBIx::SearchBuilder::Handle, and then connects to the database
using that handle. Pretty straight forward, the password '' is what I use when
there is no password. I could probably leave it blank, but I find it to be
more clear to define it.
009:
010: my $s = Simple->new($handle);
011:
012: $s->LoadById(1);
LoadById is one of four 'LoadBy' methods, as the name suggests it searches for
an row in the database that has id='0'. ::SearchBuilder has, what I think is a
bug, in that it current requires there to be an id field. More reasonably it
also assumes that the id field is unique. LoadById($id) will do undefined
things if there is >1 row with the same id.
In addition to LoadById, we also have:
- LoadByCol
- Takes two arguments, a column name and a value. Again, it will do
undefined things if you use non-unique things.
- LoadByCols
- Takes a hash of columns=>values and returns the *first* to match. First
is probably lossy across databases vendors.
- LoadFromHash
- Populates this record with data from a DBIx::SearchBuilder. I'm currently
assuming that DBIx::SearchBuilder is what we use in cases where we expect
> 1 record. More on this later.
Now that we have a populated object, we should do something with it! ::Record
automagically generates accessos and mutators for us, so all we need to do is
call the methods. Accessors are named <Field>(), and Mutators are named
Set<Field>($). On to the example, just appending this to the code from
the last example.
013:
014: print "ID : ", $s->Id(), "\n";
015: print "Foo : ", $s->Foo(), "\n";
016: print "Bar : ", $s->Bar(), "\n";
Thats all you have to to get the data, now to change the data!
017:
018: $s->SetBar('NewBar');
Pretty simple! Thats really all there is to it. Set<Field>($) returns a
boolean and a string describing the problem. Lets look at an example of what
will happen if we try to set a 'Id' which we previously defined as read only.
019: my ($res, $str) = $s->SetId('2');
020: if (! $res) {
021: ## Print the error!
022: print "$str\n";
023: }
The output will be:
>> Immutable field
Currently Set<Field> updates the data in the database as soon as you call
it. In the future I hope to extend ::Record to better support transactional
operations, such that updates will only happen when "you" say so.
Finally, adding a removing records from the database. ::Record provides a Create
method which simply takes a hash of key=>value pairs. The keys exactly map
to database fields.
023: ## Get a new record object.
024: $s1 = Simple->new($handle);
025: $s1->Create('Id' => 4,
026: 'Foo' => 'Foooooo',
027: 'Bar' => 'Barrrrr');
Poof! A new row in the database has been created! Now lets delete the object!
028:
029: $s1 = undef;
030: $s1 = Simple->new($handle);
031: $s1->LoadById(4);
032: $s1->Delete();
And its gone.
For simple use, thats more or less all there is to it. In the future, I hope to
exapand this HowTo to discuss using container classes, overloading, and what
ever else I think of.
METHOD NAMING¶
Each method has a lower case alias; '_' is used to separate words. For example,
the method "_PrimaryKeys" has the alias "_primary_keys".
METHODS¶
new¶
Instantiate a new record object.
Returns this row's primary key.
primary_keys¶
PrimaryKeys¶
Return a hash of the values of our primary keys for this function.
_Accessible KEY MODE¶
Private method.
Returns undef unless "KEY" is accessible in "MODE" otherwise
returns "MODE" value
_PrimaryKeys¶
Return our primary keys. (Subclasses should override this, but our default is
that we have one primary key, named 'id'.)
_ClassAccessible¶
An older way to specify fields attributes in a derived class. (The current
preferred method is by overriding "Schema"; if you do this and don't
override "_ClassAccessible", the module will generate an appropriate
"_ClassAccessible" based on your "Schema".)
Here's an example declaration:
sub _ClassAccessible {
{
Tofu => { 'read'=>1, 'write'=>1 },
Maz => { 'auto'=>1, },
Roo => { 'read'=>1, 'auto'=>1, 'public'=>1, },
};
}
ReadableAttributes¶
Returns an array of the attributes of this class defined as "read"
=> 1 in this class' _ClassAccessible datastructure
WritableAttributes¶
Returns an array of the attributes of this class defined as "write"
=> 1 in this class' _ClassAccessible datastructure
__Value¶
Takes a field name and returns that field's value. Subclasses should never
override __Value.
_Value¶
_Value takes a single column name and returns that column's value for this row.
Subclasses can override _Value to insert custom access control.
_Set¶
_Set takes a single column name and a single unquoted value. It updates both the
in-memory value of this column and the in-database copy. Subclasses can
override _Set to insert custom access control.
_Canonicalize PARAMHASH¶
This routine massages an input value (VALUE) for FIELD into something that's
going to be acceptable.
Takes
- FIELD
- VALUE
- FUNCTION
Takes:
- FIELD
- VALUE
- FUNCTION
Returns a replacement VALUE.
_Validate FIELD VALUE¶
Validate that VALUE will be an acceptable value for FIELD.
Currently, this routine does nothing whatsoever.
If it succeeds (which is always the case right now), returns true. Otherwise
returns false.
TruncateValue KEY VALUE¶
Truncate a value that's about to be set so that it will fit inside the database'
s idea of how big the column is.
(Actually, it looks at SearchBuilder's concept of the database, not directly
into the db).
_Object¶
_Object takes a single column name and an array reference. It creates new object
instance of class specified in _ClassAccessable structure and calls LoadById
on recently created object with the current column value as argument. It uses
the array reference as the object constructor's arguments. Subclasses can
override _Object to insert custom access control or define default contructor
arguments.
Note that if you are using a "Schema" with a "REFERENCES"
field, this is unnecessary: the method to access the column's value will
automatically turn it into the appropriate object.
Load¶
Takes a single argument, $id. Calls LoadById to retrieve the row whose primary
key is $id
LoadByCol¶
Takes two arguments, a column and a value. The column can be any table column
which contains unique values. Behavior when using a non-unique value is
undefined
LoadByCols¶
Takes a hash of columns and values. Loads the first record that matches all
keys.
The hash's keys are the columns to look at.
The hash's values are either: scalar values to look for OR has references which
contain 'operator' and 'value'
LoadById¶
Loads a record by its primary key. Your record class must define a single
primary key column.
LoadByPrimaryKeys¶
Like LoadById with basic support for compound primary keys.
LoadFromHash¶
Takes a hashref, such as created by DBIx::SearchBuilder and populates this
record's loaded values hash.
_LoadFromSQL QUERYSTRING @BIND_VALUES¶
Load a record as the result of an SQL statement
Create¶
Takes an array of key-value pairs and drops any keys that aren't known as
columns for this recordtype
Delete¶
Delete this record from the database. On failure return a Class::ReturnValue
with the error. On success, return 1;
Table¶
Returns or sets the name of the current Table
_Handle¶
Returns or sets the current DBIx::SearchBuilder::Handle object
AUTHOR¶
Jesse Vincent, <jesse@fsck.com>
Enhancements by Ivan Kohler, <ivan-rt@420.am>
Docs by Matt Knopp <mhat@netlag.com>
SEE ALSO¶
DBIx::SearchBuilder