NAME Class::Persist - Persistency framework for objects SYNOPSIS package My::Person; use base qw( Class::Persist ); Class::Persist->dbh( $dbh ); __PACKAGE__->simple_db_spec( first_name => 'CHAR(30)', last_name => 'CHAR(30)', address => "My::Address", # has_a relationship phones => [ "My::Phone" ], # has_many relationship ); my $person = My::Person->new( first_name => "Dave" ); $person->addesss( My::Address->new ); $person->store; DESCRIPTION Provides the framework to persist the objects in a DB in a Class::DBI style. The main difference between Class::Persist and Class::DBI is that Class::DBI provides an object wrapper around a row in a database, typically a database that already exists. The purpose of Class::Persist is to store an object, or a tree / collection of objects in a database, without worrying too much about what the database looks like. The other difference is that it's possible to have a Class::Persist object that does not come from a database - Class::DBI objects always represent an existing db row. Class::Persist is not Pixie or another 'magic' persistence layer - the properties of your object go into real database columns, and you need to know a bit about databases, with the attendant advantage that you can use SQL to search the database for objects. USAGE In its simplest form, to make a package persistable, inherit from Class::Persist, and call simple_db_spec on your package name to tell Class::Persist what bits of your object you would like to store, and what sort of DB fields you would like to store them in. Use the setup_DB_infrastructure and create_table methods to create the database tables for your objects in the setup code for your application. Then, call dbh on the Class::Persist package to set the global database connection for all Class::Persist objects, and your objects are now persistable. package My::Foo; use base qw( Class::Persist ); Class::Persist->dbh( $dbh ); My::Foo->simple_db_spec( name => "CHAR(30)" ); If you need to have more than one Class::Persist database, you can subclass Class::Persist through a middle class that defines your application-specific database connection, and have your persistable classes inherit from that: package My::Persistable; use base qw( Class::Persist ); My::Persistable->dbh( $dbh ); package My::Bar use base qw( My::Persistable ); My::Bar->simple_db_spec( name => "CHAR(30)" ); Objects will be assigned a table name automatically based on their class name - if you prefer to choose table names explicitly, use the db_table method. My::Bar->db_table( "table_bar" ); Subclassing You can subclass other persistable objects to create new objects, which share the properties of their superclasses, and can add fields. They are stored in seperate database tables, but inherit the column types from their superclass. package My::Baz; use base qw( My::Bar ); My::Baz->simple_db_spec( height => "INT" ); Relationships You can trivially define relationships with other classes by putting a class name in the 'data type' part of the db spec. My::Wallace->simple_db_spec( bar => "My::Bar" ); my $wallace = My::Wallace->new; my $bar = My::Bar->new; $wallace->bar( $bar ); $wallace->store; An object can have a listref of other persistable objects stored in a property, by passing a listref with the property name as the data type. My::Grommit->simple_db_spec( bars => ["My::Bar"] ); my $grommit = My::Grommit->new; my $bar = My::Bar->new; push @{ $grommit->bar }, $bar; or $grommit->bar->push( $bar ); See the documentation for simple_db_spec for more details of how to define relationships. Setup To create the database tables for the objects you have defined, call create_table on each of them in turn. My::Foo->create_table; My::Bar->create_table; My::Baz->create_table; If you subsequently change the layout of the database by changing the object spec, you will have to either delete and re-create the tables, losing all the data, or change the table definition yourself manually. Storing and retrieving objects All objects inheriting from Class::Persist have an 'oid' method that returns the unique id of the object, this will be a UUID - a unique non-guessable 36 character string. The simplest way to retrieve an object is by its oid: my $bar = My::Bar->load( $oid ); Alternatively, the two-parameter version of load is useful if you can rely on some other unique value in the object: my $bar = My::Bar->load( name => "The Founder's Arms" ); You can search for objects using methods of varying sophistication # get all the bars. my @bars = My::Bar->get_all; # get bars where the 'color' property is equal to 'Green' my @green_bars = My::Bar->search( color => "Green" ); # get bars where the color contains an 'e' my @some_bars = My::Bar->sql("color like ?", "%e%"); See the search, sql and advanced_search methods for increasingly complicated ways of searching the database for objects. OBJECT CREATION The standard way of creating an object is with the new() method. new() optionally takes a hash of key/value pairs to populate the initial state of the object. PROPERTIES All Class::Persist subclasses inherit certain properties from the superclass. creation_date() A DateTime object for when this object was originally created. Should be considered read-only. timestamp() A DateTime object that represents the last time this object was stored into the database. owner() If the object is owned, ie it is the target of a has_a, has_many, etc relationship, the owner method will return the object's owner. RETRIEVING OBJECTS load( id ), load( key => value ) Loads an object from the database. Can be used in two different ways. My::Class->load( $id ) Loads the unique item of the class My::Class with the oid $id. My::Classs->load( foo => "Bar" ) Loads the first item of class My::Classs where the property 'foo' is equal to 'bar'. get_all() Returns a list of all the objects of this class in the database. search( column => "value" ) Takes a hash of attribute=>value pairs. Values of undef become IS NULL tests. Returns a list of objects in the database of this class which match these criteria. my $pears = Fruit->search( shape => 'pear' ); The special parameter 'order_by' will not be used as part of the search, but will order the results by that column. my $sorted_pears = Fruit->search( shape => 'pear', order_by => 'size' ); sql( sql, [placeholder values] ) Free-form search based on a SQL query. Returns a list of objects from the database for each row of the passed SQL 'WHERE' clause. You can use placeholders in this string, passing the values for the placeholders as the 2nd, etc, params Person->sql("name LIKE '%ob%' AND age > ? ORDER BY height", $min_age) advanced_search( ... ) when search() isn't good enough, and even sql() isn't good enough, you want advanced_search. You pass a complete SQL statement that will return a number of rows. It is assumed that the left-most column will contain oids. These oids will be inflated from the database and returned in a list. As with the sql method, you can use placeholders and pass the values as the remaining parameters. People->advanced_sql(' SELECT artist.oid FROM artist,track WHERE track.artist_name = artist.name AND track.length > ? ORDER BY artist.name', 100 ); This will be slower than sql - there will be another SQL query on the db for every row returned. That's life. There is much scope here for optimization - the simplest thing to do might be to return a list of proxies instead.. Also consider that the SQL statement you're passing will be just thrown at the database. You can call Object->advanced_sql('DROP DATABASE people') and bad things will happen. This is, of course, almost equally true for the sql method, but it's easier to break things with this one. OBJECT METHODS store() Store the object in DB and all objects within, whether it is a new object or an update. Storing an object will collapse all its relationships with other Class::Persist object into proxies. delete() Deletes the object and returns true if successful. It will delete recursively all objects within. deleteThis() Deletes the object from the DB, and returns true if successful. Does not delete recursively any objects within, so this method will leave orphans. revert() revert an object back to its state in the database. You will lose any changes you've made to it since the last store. This is recursive - all children of the object will be reverted as well. Throws a Class::Persist::Error::Revert if the object you're trying to revert isn't stored in the database. revertThis() Revert only this object to its DB state, not any of its children. clone( new_owner ) Deep-clones the object - any child objects will also be cloned. All new objects will have new oids, and will not be stored in the database. Unlike delete and revert, clone is not depth-first. new_owner will own the newly cloned object, if passed. If not, the new object will have no owner. cloneThis( [new_owner] ) Clones just this Class::Persist object, not any of its children. new_owner will own the newly cloned object, if passed. If not, the new object will have no owner. validate() Returns true if the object is in a good, consistent state and can be stored. The default implementation just returns true - Override this method if you want to make sure your objects are consistent before storing. Returning 0 from this will cause the store() method to fail. unique() Returns true if the current object is unique, ie there is no other row in the database that has the same value as this object. The query that is used to check for uniqueness is defined by the unique_params method. Only checked for unstored objects - objects that have come from the database are presumed to be unique. RELATIONSHIPS AND CLASS SETUP Classes can have relationships with each other. The simplest way to define a class and its relationships is with the simple_db_spec method, but if you want more control you can use the more specific functions. simple_db_spec( column => "type", ... ) The simplest of specifying the database spec, combining the field list, has_a and has_many relationships and the database spec in one command. Person::Foot->simple_db_spec( digits => 'INT', name => 'CHAR(10)', leg => 'Person::Leg', hairs => [ 'Person::Leg::Hair' ], grown_on => "DateTime", ); For each column as the keys of the passed hash, specify a simple DB field with a DB type, a has_a relationship with a class name, and a has_many relationship with a listref continain a single element - the class name. This will also automatically create a name for the database table, if you don't want to supply one yourself. The name will be based on the package name. Any fields defined as BLOB, LONGBLOB or similar types will automatically be declared as binary fields - see binary_fields. Finally, defining a field type as 'DateTime' will let you store a DateTime object in that field, which will be stringified to yyyy-mm-ddThh::mm::ss in the database column. db_table( [table] ) Get or set the name of the table that this class will be stored in. If you don't set it explicitly, and use simple_db_spec, a table name based on the package name will be generated automatically for you. Alternatively, you can set it specifically. If you don't use the simple_db_spec method, you must explicitly set a table name. db_fields( @fields ) Instead of using simple_db_spec, you can tell Class::Persist which columns in the table are to store properties, and set up the relationships manually, using db_fields, has_a, has_many, etc. db_fields defines the fields in the DB that will store scalar values. My::Foo->db_fields(qw( foo bar baz )); Only define the fields that this particular subclass adds using this function - the db_fields_all function can be used to get a list of all fields that the object will provide, those from this class and all its superclasses. db_fields_all() Returns a list of all db fields that this class and all its superclasses use. binary_fields( @fields ) By default, all properties of a Class::Persist object are assumed to contain a UTF8 string. If you want to put binary data into the database, you must explicitly declare a field to contain binary data using this functions. My::Foo->db_field(qw( foo bar baz )); My::Foo->binary_fields(qw( foo )); binary_fields_all() returns all binary fields of this object and its superclasses. has_a( $method => $class ) Class method. Defines a has_a relationship with another class. Person::Body->has_a( head => "Person::Head" ); my $nose = $body->head->nose; Allows you to store references to other Class::Persist objects. They will be serialised when stored in the database. has_a_all() Returns a hashref of all the has_a relationships a given class has, from itself and its superclasses. weak_reference( one, two, three ) Sets the list of references from this object that should be considered 'weak'. Weak references will not be recursed into when storing, deleting, etc, and objects on the other end of them won't have their 'owner' fields set. This lets you use a field to point into some other part of an object tree without worrying about nasty loops. weak_reference_all Returns a hashref, the keys of which are the fields with weak references of this class and it's superclasses. has_many( $method => $class ) Class method. Defines a one to many relationship with another class. Person::Body->has_many( arms => 'Person::Arm' ); my $number_of_arms = $body->arms->count; Allows you to manipulate a number of other Class::Persist objects that are associated with this one. This method will return a Class::Persist::Proxy::Container that handles the child objects, it provides push, pop, count, etc, methods to add and remove objects from the list. my $left_arm = Person::Arm->new; $body->arms->push( $left_arm ); has_many_all() Returns a hashref of all the has_many relationships a given class has, from itself and its superclasses. might_have( $method => $class ) Call on a class to define a might_have relationship between that class and another class: My::Bar->might_have( jukebox => My::Jukebox ); A might_have relationship differs from a has_a relationship in that, for has_a, there is a field in the parent table that points to the child object. For might_have, the owner field of the child object points to the parent, and the child object will have an 'owner' accessor that points at the parent. TODO - logically, has_a relationships should also provide an owner method to the child class. Objects on the other end of this relationship will be stored when the parent object is stored. might_have_all() For a given class, returns (not sets) a hashref of all of its might_have relationships, including those of its parent classes. unique_params() SQL query and binding params used to check unicity of object in DB db_fields_spec() SQL to specificy the database columns needed to store the attributes of this class - all parent class(es) columns are aggregated and used to build an SQL create table statement. Override this to specify the columns used by your class, if you want Class::Persist to be able to create your table for you. Remember to call the superclass db_fields_spec as well, though. sub db_fields_spec( shift->SUPER::db_fields_spec, 'Colour VARCHAR(63)', 'Mass VARCHAR(63)', ); db_fields_spec_all() DATABASE MANAGEMENT create_table() Create the table for this class in the database. drop_table() Drop the table for this class. STORING MORE COMPLEX OBJECTS It may be that you want to put a complex object, say a hashref, into a db field. for a given db field name, there are two hooks: db_inflate_{name} and db_deflate_{name} that are called when we inflate/deflate an object from the database. db_inflate_{name}(db_value) is called when we inflate from the database, and is passed as its only parameter the value of the DB column - this is undef if the column value is NULL. The function should set up the object according to this db field - this will probably entail calling 'set(field,val)'. db_deflate_{name} is called when we want to store the object back in to the db, and should return the value that should go into the DB column {name}. An example is probably best here. package Example; __PACKAGE__->simple_db_spec( hash => "text", # we'll deflate a hash here ); sub db_inflate_hash { my ($self, $db_value) = @_; # empty db column means empty hash return $self->set( hash => {} ) unless $db_value; # values in the db are key\tvalue\tkey\tvalue my (%hash) = split(/\t/, $db_value); # store the inflated hash in the object return $self->set( hash => \%hash ); } sub db_deflate_hash { my $self = shift; # get the hash from the object my %hash = %{ $self->get('hash') } # no hash? put nothing in the db return undef unless %hash; # store the hash in the DB as tab-seperated key/value pairs return join("\t", @%hash); } (Obviously this is a simple example - we should do something smarter to make sure there are no blessed objects in the hash, etc, etc.) This object will now have a db-persisted hashref in its 'hash' slot. These hooks are only supported for 'normal' db fields - defined with the db_fields() accessor or declared as simple types in simple_db_spec(). Using them to hook has_a, has_many or other complex relationships is not advised. STORAGE IMPLEMENTATION DETAILS The binary_fields accessor is there for a reason - there is a very strong implicit assumption that everything you want to put into Class::Persist is either a text string, in which case it will be stored in the database as a series of UTF8 octets, or a lumb of binary data, in which case it will go into the DB as-is, but you must flag it as such. Class::Persist does not use any db-specific character set tools, such as the utf-8 support in mysql 4.1, because I want to do things the same across all databases where possible - in this case, that meast that we assume the DB stores the exact bytes that we give it, and will give them back. Class::Persist handles the encoding and decoding from utf8, so you can store any valid perl string and will get back something that is at least equivalent. BUGS The API isn't yet stabilised, so please keep an eye on the Changes file where incompatible changes will be noted. Storing invalid perl strings in the database, for instance using "_utf8_on" to flip the utf8 bit on a non-utf8 string, will break. Horribly. Don't Do It. Making recursive loops in the object tree is very easy. However, it'll lead to recursive storing and pain. Again, not a good idea. It'll be fixed soon, I hope. an object with more than parent-child relationship with a particular subclass is going to act very strangely, ie a has_a => "Some::Class" and a has_many => "Some::Class". Not sure what to do about that one. AUTHORS Nicholas Clark Pierre Denis Tom Insam Richard Clamp This module was influnced by James Duncan and Piers Cawley's Pixie object persistence framework, and Class::DBI, by Michael Schwern and Tony Bowden (amongst many others), as well as suggestions from various people within Fotango. COPYRIGHT Copyright 2004 Fotango. All Rights Reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.