NAME
Basset::Object::Persistent - subclass of Basset::Object that allows objects to be easily stored into a relational database. Presently only supports MySQL, but that may change in the future.
AUTHOR
Jim Thomason, jim@jimandkoka.com
SYNOPSIS
(no synopsis, this is an abstract super class that should never be instantiated directly, it should be subclassed for all persistent objects and used through them)
DESCRIPTION
Basset::Object is the uber module in my Perl world. All objects should decend from Basset::Object. It handles defining attributes, error handling, construction, destruction, and generic initialization. It also talks to Basset::Object::Conf to allow conf file use.
But, some objects cannot simply be recreated constantly every time a script runs. Sometimes you need to store the data in an object between uses so that you can recreate an object in the same form the last time you left it. Storing user information, for instance.
Basset::Object::Persistent allows you to do that transparently and easily. Persistent objects need to define several pieces of additional information to allow them to commit to the database, including their table definitions. Once these items are defined, you'll have access to the load and commit methods to allow you to load and store the objects in a database.
It is assumed that an object is stored in the database in a primary table. The primary table contains a set of columns named the same as object attributes. The attributes are stored in those columns.
Some::Package->add_attr('foo');
my $obj = Some::Package->new();
$obj->foo('bar');
$obj->commit();
in the database, the 'foo' column will be set to 'bar'.
ATTRIBUTES
- loaded
-
boolean flag 1/0.
This flag tells you whether or not the objects you are operating on has been loaded from a database or initially created at this time and not loaded. This flag is set internally, and you should only read it.
- loading
-
read only boolean flag 1/0.
This flag is usually used internally, it keeps track of whether or not the object is currently in the process of loading from the database. It will always be zero unless the object is loading. This flag is set internally, and you should only read it.
- loaded_from_cache
-
boolean flag, 1/0.
This flag tells you whether this object was loaded from the object cache. For efficiency's sake, you can specify a path to an object cache directory and store objects in the cache in addition to committing them to the database. You will then be able to laod the object from the cache and avoid the over-head of going to the database.
This flag specifies if the object came from the cache. This flag is set internally, and you should only read it.
- committing
-
read only boolean flag 1/0.
This flag is usually used internally, it keeps track of whether or not the object is currently in the process of committing to the database. It will always be zero unless the object is committing. This flag is set internally, and you should only read it.
- committed
-
Flag, N/0.
This flag tells you whether this object has been committed during this instantiation. It will not keep track of whether an object has been committed before this instantiation. The value is either 0 (no commits during this instantiation) or N, where N is a positive integer number containing the number of times that this object has been committed during this instantiation. This flag is set internally, and you should only read it.
$object->commit(); if ($object->committed){ print "Yay, committed!"; } else { print "Could not commit : " . $object->errstring . "\n"; };
- deleting
-
read only boolean flag 1/0.
This flag is usually used internally, it keeps track of whether or not the object is currently in the process of being deleted from the database. It will always be zero unless the object is deleting. This flag is set internally, and you should only read it.
- deleted
-
Boolean flag, 1/0.
When an object is deleted via the ->delete method, this flag is set to 1. Otherwise, it is 0. This is the only change that is made to an object when it is deleted, so this is the way to determine if your delete was successful. This flag is set internally, and you should only read it.
$object->delete(); if ($object->deleted){ print "Yay, deleted!"; } else { print "Could not delete : " . $object->errstring . "\n"; };
- cache_object
-
Boolean flag, 1/0.
This is a flag that governs whether object caching is enabled or disabled. Set it to 1 to enable object caching, set it to 0 to disable object caching.
If this is 0, you must specify a cache_object_dir.
This should be set in your conf file.
- cache_object_dir
-
This attribute specifies the location where cached objects should be stored. Should be an absolute path ending in a trailing slash.
This should be set in your conf file.
define package Basset::Object::Persistent cache_object = 1 cache_object_dir = /path/to/cached/objects/
- arbitrary_selectables
-
This should be set in the conf file. This is a regular expression that specifies which queries arbitary_sql should expect to return data. A good value for MySQL is: (show|select|desc|set)
- force_insert
-
Boolean flag. 1/0. Trickles to subclasses.
Your objects may be transactional in nature such that you always want to keep a record of them no matter how often they've changed. In that case, you can specify the force_insert flag.
Care must be taken with this flag to ensure you never violate primary key constraints. Also, you may not use auto generated ids, for obvious reasons.
METHODS
- _keyed_accessor
-
This is an accessor designed to be specified with add_attr. For example,
Basset::User->add_attr(['user_group', '_keyed_accessor'], 'Basset::Group');
That would specify that if you have a user object, you can only specify values to your user_group attribute that would successfully load into a Basset::Group object.
You can shut off the key validation if you're positive your value is valid
$user->user_group($group_id); #validates $user->user_group($group_id, 'valid'); #does not validate
Also note that the validation does not occur when the object is loading. It is assumed that if the key made it into the database, it's valid.
- add_primarytable
-
add_primarytable is a class method that takes a hash as an argument, which is used as a constructor call for a Basset::DB::Table object (or whatever you've specified as your table type object)
__PACKAGE__->add_primarytable( 'name' => 'transaction', 'primary_column' => 'id', 'autogenerated' => 1, 'definition' => { 'id' => 'SQL_INTEGER', 'account' => 'SQL_INTEGER', 'paidby' => 'SQL_INTEGER', 'category' => 'SQL_INTEGER', 'day' => 'SQL_DATE', 'amount' => 'SQL_DECIMAL', 'description' => 'SQL_VARCHAR', } );
See Basset::DB::Table for more information. This table is the primary table where the object's data is stored.
This method is deprecated. Use add_tables instead.
- perl_read_translation
-
extra_select gives you the ability to select additional information from a table. And the definition gives you the columns you have in that table. But, that information may not necessarily be in optimal form for your usage. Say, for example, that your table isn't normalized, and you're embedding an array into a column, 'foo'. In your table, you may have:
+----------+ | foo | +----------+ | A:B:C:D:E| +----------+
Now that delimited string is great for your database, but when you get it back into perl, it'd probably be nice to have an actual array. That's where perl_read_translation comes into play.
__PACKAGE__->perl_read_translation( { "foo" => 'reader_function' } );
When the object is being loaded, the function read_function will be called as a class method (whatever class of object you are loading into) and passed 2 arguments - the name of the column being read (which is usually also the name of the accessor into which the value is placed), and the value being read. In this case, it would be:
$class->reader_function('foo', 'A:B:C:D:E');
An example reader_function could be this:
sub reader_function { my $class = shift; my $method = shift; my $value = shift; return [split(/:/, $value)]; };
Be aware of the fact that you do not yet have an object, so you cannot call any object methods on anything. If you need to alter an attributes value based upon the value of another attribute, you'll have to do things the old fashioned way by subclassing load and zipping in there yourself. perl_read_translation is only useful for alterring individual snippets of data.
The format of the hash is simply "method_name" => coderef.
If no re-write is specified for a given method, then the value that was read from the database will be returned.
It's important to note here that extra_select and db_write_translation are internal to Basset::DB::Table. You don't need to do anything different in your code to use them. However, perl_read_translation and perl_write_translation are a different matter. Basset::Object::Persistent respects the methods and uses them, but if you write your own load or commit methods, you'll need to call either import_from_db or export_to_db, as appropriate.
- perl_write_translation
-
The inverse function for perl_read_translation. Same reasoning - sometimes you need to change the form of your data before writing it to your table. Using the earlier example, we'll assume you have an attribute 'foo' that contains an arrayref. Your table is not normalized, so you want to store the value in a single column.
__PACKAGE__->perl_write_translation( { 'method' => { 'A' => 'writer_function' } } );
During commit, the function specified will be called as an object method, and handed two arguments - the name of the method and the method's current value.
$obj->writer_function('foo', [qw(A B C D E)]);
An example writer_function could be this:
sub writer_fuction { my $self = shift; my $method = shift; my $value = shift; return join(":", @$value); };
Be aware of the fact that while methods defined in perl_write_translation are called as object methods, methods defined in perl_read_translation are called as class methods. So it is recommended against using values from multiple attributes to serialize into one attribute value, unless you really know what you're doing. Otherwise, it could be difficult to read the information back out.
The format of the hash is simply "method_name" => {"query_type" => coderef}, where "query_type" follows the same rules as those defined for db_write_translation.
If no re-write is specified for a given method, then the value that is presently contained in the method will be written to the database.
It's important to note here that extra_select and db_write_translation are internal to Basset::DB::Table. You don't need to do anything different in your code to use them. However, perl_read_translation and perl_write_translation are a different matter. Basset::Object::Persistent respects the methods and uses them, but if you write your own load or commit methods, you'll need to call either import_from_db or export_to_db, as appropriate.
- export_to_db
-
This is a method you usually won't have to care about, unless you're writing your own low-level commit method to take the place of the uber method in Basset::Object::Persistent.
Takes 3 arguments.
$obj->export_to_db($method, $value, $type)); $obj is the object being committed $method is the method being called $value is the value that is to be written to the databse $type is the query type, either 'I', 'U', 'R', 'D', or 'A'
This will return the appropriate value to write to the database. If there is no re-write defined for the method in perl_write_translation, then the value $value will be returned. If there is a re-write, then that value will be returned instead, depending upon the output of the re-write method.
Note that $method is not always necessarily a method.
This method uses the re-write values defined by perl_write_translation
- import_from_db
-
This is a method you usually won't have to care about, unless you're writing your own low-level load method to take the place of the uber method in Basset::Object::Persistent.
Takes 4 arguments.
$class->import_from_db($method, $value); $class is the class of the object being created $method is the method being called $value is the value that was read from the databse
This will return the appropriate value to hand into the attribute. If there is no re-write defined for the method in perl_read_translation, then the value $value will be returned. If there is a re-write, then that value will be returned instead, depending upon the output of the re-write method.
Note that $method is not always necessarily a method.
This is associated with the values defined in perl_read_translation
- add_tables
-
add_tables is a class method that takes a list of tables as its arguments, which are the tables associated with this object when it is stored to the database.
__PACKAGE__->add_primarytable( __PACKAGE__->factory( 'type' => 'table', 'name' => 'transaction', 'primary_column' => 'id', 'autogenerated' => 1, 'definition' => { 'id' => 'SQL_INTEGER', 'account' => 'SQL_INTEGER', 'paidby' => 'SQL_INTEGER', 'category' => 'SQL_INTEGER', 'day' => 'SQL_DATE', 'amount' => 'SQL_DECIMAL', 'description' => 'SQL_VARCHAR', } ) );
See Basset::DB::Table for more information.
- primary_table
-
Returns the first table associated with the given object.
- relationships
-
This is a class attribute that internally stores the relationships used by this class. Specify new relationships with has_a or has_many.
- should_be_deleted
-
This is used to flag an object that has been auto-vivified and is tied to a parent object. You should rarely need to set, access, or worry about this flag directly.
- should_be_committed
-
This is used to flag an object that has been auto-vivified and is tied to a parent object. You should rarely need to set, access, or worry about this flag directly.
- instantiated_relationships
-
Internal hash that keeps track of which relationships for a given object have been instantiated. Check for instantiation via the is_instantiated method instead.
- cental_load_cache
-
if the use_central_load_cache parameter is set in the conf file, then objects will use a centralized loading cache, stored here. This is internal only.
- _deleted_relationships
-
Internal method. Keeps track of instantiated associated objects that were subsequently deleted. No looky, no touchy.
- is_instantiated
-
Boolean operator. Given an attribute, returns true if it is an associated attribute and has been instantiated, false if it has not been.
- instantiate
-
In the abstract, this is simple. Takes an attribute and an optional set of clauses, then instantiates that object.
$obj->instantiate('foo');
Now $obj->foo will contain whatever the instantiated list of information is, as defined when it was set up with the has_a or has_many call. Alternatively, you can pass in a set of clauses to restrict the objects loaded.
$obj->instantiate('foo', { 'where' => 'status_id = 1' });
Will instantiate the 'foo' attribute only with the objects that have a status_id of 1, anything else will simply not be loaded.
Note that you should only instantiate an attribute that is defined has having an instantiating parameter of 'manual' (as opposed to 'lazy' ) and this is due to encapsulation reasons.
Lazy objects are not instantiated until the attribute holding them is accessed, but then they are instantiated automatically.
Manual objects are the ones that you want to worry about. In those cases, the instantiate method is basically a shortcut to insulate you from needing to take extra steps and know the class involved.
Say that a user has_many classes. You could do this:
use Some::Class; use Some::User; my $user = Some::User->load(1); my $classes = Some::Class->load_where('user_id' => $user->id);
or this
use Some::User; my $user = Some::User->load(1); my $classes = $user->instantiate('classes');
- uninstantiate
- has_a
-
has_a defines relationship between objects. "An object 'has_a' different object". The has_a method is simply a wrapper around has_many, passing in a key of undef and setting the singleton flag to 1.
- _instantiating_accessor
-
If a relationship is defined in has_many with instantiating -> lazy, then the associated objects will be populated automagically, but not until the attribute is accessed. _instantiating_accessor internally handles all of that.
- has_many
-
All right, now we finally get to define relationships. The has_many parameter needs two values, the attribute and its class.
Some::Class->has_many( 'wibbles' => 'Some::Other::Class' );
That will create an accessor for 'wibbles' and associate it with "Some::Other::Class". You could then instantiate it from an object:
$someObject->instantiate('wibbles');
And populate all of your wibbles data.
has_many takes an optional (but recommended!) 3rd argument, the options hash. Several options are supported.
- key
-
If loading up multiple associated objects (a cat "has_many" paws), then they will by default appear in an arbitrarily ordered arrayref containing all of the data. But, there are times when you want to load up all of the data and quickly associate objects associated with particular attributes. In that case, pass in the key parameter.
Some::Class->has_many( 'wibbles' => 'Some::Other::Class', { 'key' => 'foo' } );
Then your data will be populated into a hashref, with the associated objects' "foo" attributes serving as their keys.
- instantiating
-
This item should be one of 2 values - 'manual' or 'lazy'
Some::Class->has_many( 'wibbles' => 'Some::Other::Class', { 'instantiating' => 'manual' } );
- lazy
-
lazily instantiated objects will automatically come into being when the associated attribute of the owning object is accessed for the first time. This is the default.
- manual
-
manually instantiated objects will never automatically come into being. you will have to explicitly call 'instantiate' yourself.
- singleton
-
If the singleton flag is set, then it is known that this attribute is associated with a single other object, and consequently will just hold a reference to that object itself (not in an arrayref or hashref)
Some::Class->has_many( 'wibbles' => 'Some::Other::Class', { 'singleton' => 1 } );
- clauses
-
the clauses hashref is the same sort of clauses hashref to be handed into the loader. in fact, it is handed into the loader when the associated objects are instantiated.
Some::Class->has_many( 'wibbles' => 'Some::Other::Class', { 'clauses' => { 'where' => 'status_id = 1' } } );
- accessibility
-
This governs encapsulation. Associating objects with other objects is good, but you don't always want the user of the class to know that other objects are involved. You should set the accessibility flag to 'private' if the associated object will never be accessed outside of the class that defines it. These classes should probably be inlined (or at least privatedly declared inside another package)
Some::Class->has_many( 'wibbles' => 'Some::Other::Class', { 'accessibility' => 'private' } );
Default value is 'public';
Making an associated object private shuts off its ability to commit or delete itself. its changes only go in when its parent object is committed or deleted.
- relationship_key
-
Sometimes, you may have an object that references two objects in a different table. You may know that every Car has a primary_driver and a secondary_driver. So you define your relationship:
Car->primary_table->references( { 'primary_driver' => 'driver.id', 'secondary_driver' => 'driver.id' } );
But you wouldn't be able to establith relationships for those items, since instantiate would try to load an object using both of those values.
Car->has_a( 'primary_driver' => 'Driver' );
Would try to load where driver.id = car.primary_driver_id and driver.id = car.secondary_driver_id. So it would only work in the edge case when they're the same driver, which is not your intent.
The solution is to explicitly define which key you'd like to join on.
Car->has_a( 'primary_driver' => 'Driver', { 'relationship_key' => 'primary_driver' } ); Car->has_a( 'secondary_driver' => 'Driver', { 'relationship_key' => 'secondary_driver' } );
- transform
-
See the 'transform' flag in the load_all method for info.
- foreign_has_a
-
If you have a has_many relationship, then presumably your foreign class has a has_a relationship with you. You can declare that relationship here. This has two advantages.
1) It allows you to autmatically populate the foreign object's has_a property with yourself upon setting the has_many.
2) If the foreign class references you with multiple columns (say, obj_id_1 and obj_id_2), then the foreign has_a has defined the relationship key to use. Specifying the foreign_has_a here uses those same relationship keys.
- create_add_to_method
-
Mainly used internally when setting up has_many relationships. When you create a has_many relationship, you automatically get an add_to* method.
Some::Store->has_many( 'bagels' => 'Some::Bagel::Class' ); my $store->add_to_bagels( 'type' => 'chocolate chip', 'id' => '17738' );
Is equivalent to:
my $bagel = Some::Bagel::Class->new( 'type' => 'chocolate chip', 'id' =>
- commit_relationships
-
Used internally to commit all associated objects for a given object, only used for private objects
$obj->commit_relationships
- delete_relationships
-
Used internally to delete all associated objects for a given object.
$obj->delete_relationships
only used for private objects
- is_relationship
-
Given an attribute, returns true if it is a relationship, false if not.
if ($obj->is_relationship("some_attribute")) { #do interesting thing }
- relationship_columns
-
Takes a relationship as an argument, returns a list of two arrayrefs - the referencing columns (yours) and the foreign columns (columns in the foreign table)
my ($referencing, $foreign) = $self->relationship_columns($relationship);
I can't think of a reason you'd ever want to call this directly.
- primary_identifier
-
Returns the single, unique primary identifier of the object.
my $id = $obj->primary_identifier;
If an object as composite keys, this method will return an error by default. You can pass the 'want composite' flag to get back an arrayref of all primary keys.
my $idref = $obj->primary_identifier('all');
- commit
-
There is a lot of internal magic here which I'll decline to get into at the moment. Suffice to say, that ->commit() will store your object in the database, and that all of the Right Things will happen during the commit.
$object->commit(); if ($object->committed){ print "Success!\n"; } else { print "Failure : " . $object->errstring . "\n"; };
- load_from_db
-
Sometimes, when using cache_object, you may still wish to load the object from the database instead of from cache. In those cases, call load_from_db() instead of load().
Internally, this is done by temporarily flipping off caching, loading, then turning it back on (or leaving it off if it was off previously).
- writable_method
-
Given a method name, returns true if the value of this method will be written out to disk on the next commit, and false if it will not be written out.
my $output = $object->writable_method('id'); if ($output) { print "object will store id\n"; } else { print "object will not store id\n"; }
- load
-
the load method loads an object from the database (or the cache if cache_object is true). The arguments passed must be the primary_column specified in your primary table, in that order.
__PACKAGE__->add_primarytable( . . . 'primary_column' => 'id' ); my $obj = Some::Package->load($id); __PACKAGE__->add_primarytable( . . . 'primary_column' => [qw(foo bar baz)] ); my $obj = Some::Package->load($foo, $bar, $baz);
The arguments passed must be in the same order they were defined.
Returns an error if no object found that matches
- load_or_new
-
Does what it sounds like, it tries to load an object, and if it fails, it creates a new empty object instead. Basically, this allows some lazy object creation for things like stateless applications (such as cgis) that don't know in advance what they're operating on, and don't really care. So you can try to load an object if values were passed back to you, and if they weren't then you create an automatically create a new one for yourself.
- load_many
-
Convenience method. If you have a class that only uses one primary column (a unique ID, for instance) and you want to load certain objects with given IDs, you can use load_many.
my $objects = $self->load_many(1,2,3,4,5);
- load_next
- create
-
Convenience method. Instantiates a brand new object and then immediately commits it to the database.
- load_all
-
load_all loads all objects of a given package and returns them in an arrayref.
my $objects = Some::Package->load_all();
load_all optionally takes an arbitrary number of arguments, where the first is a hashref that defines a set of constraints and the rest are column values to bind to those constraints.
my $objects = Some::Package->load_all( { 'where' => 'name = ? and company = ?', 'order by' => 'id' }, 'Jim', 'FooFram' );
Will return an arrayref containing all objects with a name of "Jim" and a company of "FooFram"
A list of all valid constraints is provided in the Basset::DB::Table object.
Note that load_all is faster than loading objects individually, since it combines its SQL to minimize the number of queries. However, all queries dones internally to auto-instantiated relationships will still be performed one at a time, and not in aggregate.
Returns an empty arrayref if no objects found.
The loader can also accept various 'flag' attributes passed in the constraints hash. The flags will not be passed onto the SQL generator.
- iterator
-
The iterator flag allows you to load up objects in sequence using load_next.
my $objs = Some::Class->load_all(); foreach my $o (@$objs) { $o->do_something; };
is equivalent to:
Some::Class->load_all({'iterator' => 1}); while (my $o = Some::Class->load_nex) { $o->do_something; };
The advantage is that you won't have all of the objects in memory at one time. Note that if you subsequently call a load* method in the same class that you will wipe out the current iterator.
- constructor
-
A hashref of constructor args. As data is loaded from the database, objects will be created and initialized with the data loaded. But sometimes you need to load objects and populate in new values or override existing values with new ones. That's where the constructor comes in. It will override the values of those attributes in the database with new ones.
my $objs = Some::Class->load_all( { 'constructor' => { 'foo' => 'bar' } } );
Now all objects in $objs will have their foo attribute set to 'bar'
- singleton
-
Sometimes, you build up a complicated query but know that you'll only get back one object. If you pass in the 'singleton' flag, then you'll only get back a single object instead of an arrayref containing a single object.
- transform
-
Will transform the loaded object into one of its related objects declared via a has_a or has_many relationship.
Some::User->has_a('pelican' => 'Some::Pelican'); my $pelican = Some::User->load_all({'where' => 'user_id = ?', 'transform' => 'pelican'});
Directly using this as a loader flag is dubious at best, it is most useful with relationships.
- exists
-
Query to quickly determine if a given object (or set of objects) exists in the database. The objects will not be loaded. Returns a count of the number of objects that exist.
my $itsthere = Basset::User->exists(1); #user id 1 exists in the database
- delete
-
This will delete an object from the database
$object->delete();
The object itself will not be affected, except for the fact that its deleted flag will be set.
- load_by_user
-
This is a convenience method for objects that frequently do a load_all for a particular user.
It's just a wrapper to:
$class->load_all({"where" => "user = ?"}, $user_id);
- load_where
-
Simple wrapper around load_all. Takes key/value pairs.
my $users = Some::Class->load_where( 'user' => 3, 'location' => 'mountains', 'weather' => 'sunny' );
This is exactly equivalent to:
my $users = Some::Class->load_all( { 'where' => 'user = ? and location = ? and weather = ?' }, 3, 'mountains', 'sunny' );
It just looks prettier and hides more of the SQL.
Even better, you can also stick in an array for multiple value loads.
my $users = Some::Class->load_where( 'state' => 'PA', 'last_name' => [qw(Smith Jones Johnson)] );
Is exactly the same as:
my $users = Some::Class->load_all( { 'where' => 'last_name in (?,?,?) and state = ?' }, qw(Smith Jones Johnson), 'PA', );
There is an alternative syntax, you may pass in one arrayref and one hashref. The arrayref becomes your where clause, the second contains additional loader args (such as 'order by', 'limit', etc.)
my $users = Some::Class->load_where( #where array [ 'state' => 'PA', 'last_name' => [qw(Smith Jones Johnson)] ], #extra loader hash { 'order by' => 'state desc', }, );
Is exactly the same as:
my $users = Some::Class->load_all( { 'where' => 'last_name in (?,?,?) and state = ?', 'order by' => state desc', }, qw(Smith Jones Johnson), 'PA', );
- arbitrary_sql
-
The arbitrary_sql method does what it sounds like, it executes arbitrary sql code. You're expected to pass at least one parameter:
query => 'some sql query'; #such as select col1, col2 from table1
If you want to bind any variables to the query, put them in the vars parameter:
query => 'select count(*) from table where id = ?', vars => '7'
Normally, you'd pass in an arrayref to vars, but if it's just one, you can skip it
vars => '7' or vars => ['7'] query => 'select count(*) from table where id = ? and type = ?', vars => ['7', 'animal']
Binding is done without SQL types, unless you pass in a Basset::DB::Table object and the columns as well, which contains the column types:
my $t = Basset::DB::Table->new( {table definitions} ); table => $t cols => ['id', 'type']
Insertion queries (insert, update, etc.) will return 1 upon success
If you're running a select, show, set, or desc query, then you end up loading data. It will always be returned in an arrayref containing the rows. Normally, each row is a hashref, loaded with the ->fetchrow_hashref method from DBI. You can also choose to load into an array, then pass in into:
'into' => 'array'
If you pass anything other than 'into' => 'array', then 'into' => 'hash' is assumed.
my $data = $class->arbitrary_sql( 'query' => 'select id, name from names where id in (?, ?) and name in (?, ?)', 'vars' => [qw(7 8 Jim Koka)], ); foreach my $h (@$data){ print {$_->{id} . " : " . $_->name . "\n"} sort keys %$h; };
Alternatively, if you're memory conscious, you can pass in the 'iterator' flag. This will return the actual executed statement handle, so you can call fetchrow_array, fetchrow_hashref, etc. on it yourself.
my $sth = $class->arbitrary_sql( 'query' => 'select id, name from names where id in (?, ?) and name in (?, ?)', 'vars' => [qw(7 8 Jim Koka)], 'iterator' => 1, );
Another example:
my $rc = $class->arbitrary_sql( 'query' => 'insert into names (id, name) values (?,?)', 'vars' => ['18', 'Jim 3'], 'table => $names_table, 'cols' => [qw(id name)] ); # $rc == 1
- driver
-
The driver method is just a shortcut wrapper for Basset::DB->new(); Only give it the same arguments in the same format as you would give to Basset::DB->new() itself. The driver object returned will be cached here for all time, unless you explicitly wipe it out or set it to something else.
If the driver hasn't been accessed in the last 5 minutes, then it pings the database handle before returning the driver to ensure that it's still live. If the ping fails and the driver has no transaction stack, then you transparently just get back a new driver.
But if the ping fails AND the driver had an active transaction stack, then you get back an error. Calling ->driver again will create a new handle, but you would presumably have an error condition to deal with.
- begin
-
Database transactions are stack based. ->begin adds onto the stack, ->end removes from the stack. See Basset::DB for more info.
You may now begin and end your transaction as normal. Please be aware of the fact that in the current implementation, beginning a transaction locks the database driver for ALL objects in the system.
You don't need to begin if you're only committing a single object - individual classes are expected to do their own locking, stack handling, unlocking, etc. as necessary. You will need to begin and end if you're doing multiple commits of different objects (or if you're writing your own module). For example,
my $user = Basset::User->load(1); my $user2 = Basset::User->load(2); $user->begin(); #start up a transaction stack $user->name('Jim'); #set user's name, doesn't need to be in the transaction $user2->name('Koka'); #set user's name, doesn't need to be in the transaction $user->commit(); #doesn't actually commit to the database, it's in a transaction $user2->commit(); #doesn't actually commit to the database, it's in a transaction $user->end(); #closes the transaction stack, now commits
See Basset::DB for more information about begin, end, fail, etc.
- end
-
Database transactions are stack based. ->begin adds onto the stack, ->end removes from the stack. See Basset::DB for more info.
- fail
-
Database transactions are stack based. ->fail is a shortcut to shutdown and rollback your transaction
- finish
-
Database transactions are stack based. ->finish is a shortcut to immediately finish your transaction
- wipe
-
Database transactions are stack based. ->wipe clears out your transaction stack.
- fatalerror
-
Setting a fatalerror message causes your transaction to fail. Note that you must explicitly pass a defined value for the transaction stack to be wiped.
If you need to unfail a failed transaction (say, you know how to recover from the error), then you should call unfail on the driver and continue.
$driver->unfail(); # interesting things
- setup
-
The setup method is called immediately after the object is loaded and initialized in load_all. Basset::Object::Persistent's method is empty and does nothing. It's designed to be used in subclasses in locations where you need to alter something in an object after it's loaded from the database and set up properly. Say if you do further initialization or load in from an object or something.
This is effectively analogous to Basset::DB::Table's perl_read_translation method, but applicable to the object as a whole, not merely to an individual table
- cleanup
-
The cleanup method is called immediately before the object is committed in commit. Basset::Object::Persistent's method is empty and does nothing. It's designed to be used in subclasses in locations where you need to alter something in an object immediately before it's committed to the database.
This is effectively analogous to Basset::DB::Table's perl_write_translation method, but applicable to the object as a whole, not merely to an individual table
- add_grouptable
-
This is a wrapper around Basset::Group's (or whichever group class you're using) subclass method.
Presently the only limitation is that a given object may belong to an arbitrary (but fixed) number of groups, but not an unlimited amount, the number must be predefined. This will change in the future.
You'll need to hand in the attribute name, table name, and any improvements to the stock definition.
Some::Class->add_grouptable( 'attribute' => 'category', 'table' => 'some_class_category', 'improvement' => { #improvements } );
Where tablename is the name of the group table, and attribute is the object attribute containing the id of the group. So, for our user and company example, it would be:
Some::User->add_grouptable( 'attribute' => 'company', 'table' => 'company_names' );
Assuming that there is a table named "company_names" in the system and that the Some::User class has an attribute called "company" within which to store the company information.
If you wish to group by multiple things, simply add multiple group tables.
Some::User->add_grouptable('attribute' => 'employee_type', 'table' => 'employment_type'); Some::User->add_grouptable('attribute' => 'tax_bracket', 'table' => 'tax_bracket');
Naturally, you will need the appropriate attributes and tables defined.
Additionally, if desired, you may improve the group definition:
Some::User->add_grouptable( 'attribute' => 'employee_type', 'table' => 'employment_type', 'improvement' => { 'company' => 'SQL_INTEGER' } );
Will assume that the grouptable has an additional column, 'company', which is used for further isolation of a given group. Allows you to associate particular employee_types with a particular company, in this case. Note that this is NOT designed to be used as subcategorization, it's used for refinement as to exactly who owns a group. You can make a subgroup by specifying the 'super' attribute of the group.
See Basset::Group for more info.
- add_group
-
This is a wrapper around getting the proper Basset::Group subclass, and then calling create for the new object.
See Basset::Group for more info.
- load_group
-
This is a wrapper around getting the proper Basset::Group subclass, and then loading the ID you want.
See Basset::Group for more info.
- load_all_groups_by_name
-
This is a wrapper around getting the proper Basset::Group subclass, and then loading the group you want by its name.
See Basset::Group for more info.
- groupclass
-
This is a wrapper around getting the proper Basset::Group subclass, and then loading the ID you want.
See Basset::Group for more info.
#=item init # #Nothing you need to worry about, Basset::Object::Persistent just intercepts init and makes sure that loaded and committed are specified first, #so that objects may rely upon them being set before the start of the initialization process. Then end up getting re-specified by the #super method, but that's of no consequence. # #=cut
#=pod
2 POD Errors
The following errors were encountered while parsing the POD:
- Around line 472:
=over without closing =back
- Around line 4107:
=over without closing =back