The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Class::ReluctantORM - An ORM emphasizing prefetching

SYNOPSIS

  package Pirate;
  use base 'Class::ReluctantORM';

  Pirate->build_class(
    primary_key => 'pirate_id',  # May be an arrayref for multi-col PKs
    table => 'pirates',
    schema => 'high_seas',
    db_class => 'Some::DB::Class',
    deletable => 0,
  );
  Pirate->has_one(Ship);

  # Elsewhere...
  package main;

  # Fetch on primary key
  my $p = Pirate->fetch(123);

  # Fetch on any field (dies on no results)
  my @peeps = Pirate->fetch_by_name('Dread Pirate Roberts');

  # Same, with no dying
  my @peeps = Pirate->search_by_name('Dread Pirate Roberts');

  # Make a pirate in memory
  $matey = Pirate->new(name => 'Wesley');

  $matey->insert(); # Save to DB
  $matey->name('Dread Pirate Roberts'); # Modify in memory
  if ($matey->is_dirty) {
     # Yes, we have unsaved changes
     $matey->update(); # Commit to DB
  }

  # Try to access a related object that hasn't been fetched
  my $ship;
  eval { $ship = $matey->ship(); };
  if ($@) {
     # Splat - Class::ReluctantORM throws exceptions if you access
     # an unfetched relation
  }

  # Fetch a pirate and his related ship
  # See Class::ReluctantORM::Manual::Relationships
  my $matey = Pirate->fetch_by_name_with_ship('Wesley');

  # Or more flexibly
  my $matey = Pirate->fetch_deep(
      name => 'Wesley',
      with => { ship => {} },
  );

  # Works
  $ship = $matey->ship();

  # Lots more....

DESCRIPTION

Class::ReluctantORM, or CRO, is an ORM that uses exceptions to detect some coding practices that may lead to scalability problems while providing enhanced transparency into database accesses.

What is an ORM?

An ORM is an Object-Relational Mapping system. It treats tables in a database as classes, and rows in those tables as objects. Foreign key relationships among tables become aggregation (has-a) relationships among objects.

Well-known ORMs include Perl's DBI::Class and Rose::DB, Ruby's ActiveRecord, and Java's

Why use an ORM?

Stay in the OOP mindset

Thinking OOPishly and thinking RDBMSishly are quite different. By treating database rows as real objects, you stay in the OOP mindset. Some programmers will see a productivity gain from this.

Reduce SQL usage to the hard cases

Simple things are extremely easy, and require no SQL. Harder problems still require SQL, but you can isolate them more easily.

Schema changes are much easier

Many schema changes are detected automatically (column additions result in new methods, for example). You also have a Perl layer in which you can intercept changes at the class level, if needed.

Possible RDBMS independence

If you rely on the ORM to generate queries, it will speak a dialect specific to the database being used. You may be able to change databases later without major code changes.

Reduce code duplication

Many classes need the functionality of CRUD (create, retreive, update, delete). On WET (non-DRY) projects, many modules implement that functionality, in many places.

Reduce inconsistency

Likewise, there is no reason why 4 different modules should name their search methods 4 different things.

Why NOT use an ORM?

Opaque SQL generation

The magic that goes into turning a method call into a database query can be difficult to unravel.

Hiding queries behind methods hides costs

It is easy to accidentally hammer a database by, for example, calling a single-row-fetching method in a loop.

Difficult to rely on the ORM to generate efficient SQL

Optimizing SQL usually means making vendor or dataset specific tweaks. ORMs may make that difficult or impossible, and the stuff that they generate will usually be fairly generic.

Why use Class::ReluctantORM?

It encourages you to combine fetches

Because it is easy to detect exactly when a related, but unfetched, object is accessed (an exception is thrown), it is easy to determine exactly which fetches can be combined, and to keep those fetches trimmed down. See Class::ReluctantORM::Manual::Prefetching

Querying methods are named consistently

Developers will generally be able to tell if a method will hit the database.

A sophisticated, extensible query generation monitoring system

You can easily create monitors to watch database activity - whether you are interested in the SQL being generated, the values returned, the data volume, or the wall time. And it is easy to write your own. See Class::ReluctantORM::Manual::Monitors

It has a abstract SQL model

CRO uses an abstract SQL model using real objects to represent pieces of a SQL statement. This allows more flexibility than some other approaches. See Class::ReluctantORM::SQL.

Perl-side triggers

Run code before or after saves, retrieves, deletes, etc. Add and remove multiple triggers on each event. See "TRIGGER SUPPORT"

Mutator Filters

Apply arbitrary transformations to data upon being read or written to the object. See Class::ReluctantORM::Filter.

Why NOT use Class::ReluctantORM?

It has a tiny developer base.

You might consider DBI::Class if you are looking for the go-to, widely-used ORM with excellent plugins and commericial support, or Rose::DB if you like the scrappy underdog approach.

It is immature.

There are some missing parts, though it is in production on our sites. But it may not support your favorite RDBMS, and there are pieces that are unpretty. It also doesn't have support that you might expect it to (like Moose integration, for example).

You might not like it.

The basic idea is that it will throw an exception if you do something stupid (well, that it can detect as stupid, anyway). The idea is that you then, thoughtfully and at implementation time (not deployment time), do something less stupid. You might not care for that approach - it's a little paternalistic. Also, its advantages are fewer in a production environment (presumably you already have all of your fetches tuned at that point).

DOCUMENTATION ROADMAP

The Manual

Class::ReluctantORM::Manual Start here for a narrative introduction to CRO.

Alternate Base Classes

Most CRO model classes will inherit directly from Class::ReluctantORM. These laternate base classes offer additional functionality for special circumstances.

Class::ReluctantORM::Static - Base class for "type tables"
Class::ReluctantORM::Audited - Base class that audits database changes to a second, audit-log table
Class::ReluctantORM::SubClassByRow - Base class for instance-singleton classes, allowing behavior inheritance

Major Core Subsystems

Class::ReluctantORM::Driver - RDBMS support
Class::ReluctantORM::SQL - SQL abstraction system
Class::ReluctantORM::Relationship - Relationships between classes
Class::ReluctantORM::Monitor - Database activity monitoring
Class::ReluctantORM::Filter - Transform data on read/write to the object.
Class::ReluctantORM::Registry - Cache fetched objects by their PKs

DOCUMENTATION FOR THIS MODULE ITSELF

The remainder of this file is documentation for the Class::ReluctantORM module itself.

"CRO-GLOBAL METHODS" - methods that affect all CRO objects or classes
"MODEL CLASS CONFIGURATION" - How to configure your class
"CLASS METADATA METHODS" - Information about your class.
"CONSTRUCTORS" - various ways of creating an object
"CRUD" - create, update, and delete. Retrieve is covered under "CONSTRUCTORS".
"DIRTINESS" - detect changes to in-memory data
"FIELD ACCESSORS" - reading and writing the attributes of your objects
"RELATIONSHIP SUPPORT" - connect this class to other classes
"MONITORING SUPPORT" - install and remove monitors from CRO objects.
"TRIGGER SUPPORT" - install and remove triggers

CRO-GLOBAL METHODS

$setting = Class::ReluctantORM->get_global_option('option');

Class::ReluctantORM->set_global_option('option', 'value');

Reads or sets a global option. Global options take effect immediately, and affect all CRO classes and objects.

Some options may be set on a per-class basis - see set_class_option.

The option name provided must be on the following list:

parse_where

Boolean, default true. If true, try to convert SQL strings passed as the value of the 'where' option to search(), fetch_deep(), delete_where() and update() into Class::ReluctantORM::SQL::Where objects. (If the parsing attempt fails, see parse_where_hard for behavior.) If false, do not even attempt to parse; all strings are treated as raw_where (but SQl::Where objects you have constructed are handled normally).

You can also control this on a per-query basis using the parse_where option to fetch_deep() and others.

parse_where_hard

Boolean, default true. If true, when a Where parsing attempt fails, throw an exception. If false, instead use the SQL string as a raw_where clause, and continue.

populate_inverse_relationships

Boolean, default true. Relationships may have an inverse (for example, if a Ship has-many Pirates, the Pirate has-one Ship). So when fetching a Ship and its Pirates, we can optionally set each Pirate to have its Ship already populated, as well.

schema_cache_policy

String enum. Controls behvior of schema scanning (column listings) at startup.

NONE (default) Perform no schema caching. Columns will be listed on each table referenced in a build_class call; the scan will happen at process start (usually compile phase).
SIMPLE If a cache file exists, read it and use it for all column info. If no cache file exists, perform the scan, then write the cache file. If the database schema changes, you'll need to manually delete the cache file to regenerate it.
schema_cache_file

String absolute path to a writable file, where schema data will be cached. Ignored if schema_cache_policy is NONE. The file will be in JSON format. No default provided.

@class_names = Class::ReluctantORM->list_all_classes();

Lists all classes that are CRO derivates, and have had build_class called.

$driver_class = Class::ReluctantORM->default_driver_class();

Returns the class name of the Driver used by the most CRO subclasses.

$bool = Class::ReluctantORM->is_class_available($cro_class);

Returns a boolean indicating whether the given CRO class has been loaded yet.

Note: If passed the special value 'SCALAR', always returns true.

MODEL CLASS CONFIGURATION

$class->build_class(%args);

Sets up the class. Arguments:

dbh

The database handle used to talk to the database. This may be either a DBI handle or a Class::ReluctantORM::DBH subclass instance. You must provide either this arg or the db_class arg.

db_class

A class that knows how to connect to the database when its new() method is called with no arguments. The instance must be a Class::ReluctantORM::DBH subclass.

schema

Schema name in the database.

table

Table name in the database.

primary_key

Required. Must either be auto-populated, or you must explicitly provide value(s) when you do an insert. New in v0.4, this may either be a string (for single-column keys) or an arrayref of strings (for multi-column keys).

fields (optional, array ref or hashref)

If not provided, the $db_class->table_info will be be called to determine the field list.

You may also decouple field names from column names by passing a hashref instead of an array ref. The hashref should map class field names to table column names.

ro_fields (optional)

Unsettable fields. Default: all fields updatable.

volatile_fields (optional)

Optional arrayref of strings. Read-write accessors will be created for these fields, allowing you to store volatile information. This data will not be loaded or saved to the database, and the fields will not be listed by field_names() etc.

insertable (optional)

Default true. If present and false, insert() will throw an exception.

updatable (optional)

Default true. If present and false, update() will throw an exception.

deletable (optional)

Default true. If present and false, delete() will throw an exception.

refresh_on_update (optional)

Optional list of fields that should be refreshed after performing an UPDATE or INSERT (perhaps because they were updated by a database trigger).

registry (optional)

Name of a Class::ReluctantORM::Registry subclass to use as the Registry for this class. If not provided, defaults to Class::ReluctantORM::Registry->default_registry_class() . See Class::ReluctantORM::Registry for details.

MyClass->add_volatile_field('field_name')

Creates a volatile accessor/mutator method (getter/setter) with the given name. The field is volatile in the sense that its value is never saved to the database. Setting a volatile field does not affect dirtiness.

CLASS METADATA METHODS

$reg = CroClass->registry();

Returns the Registry associated with this CRO class, which provides an object caching mechanism. See Class::ReluctantORM::Registry.

$driver = $class->driver();

Returns the Class::ReluctantORM::Driver object that provides backend-specific functionality.

$tablename = $class->table_name();

Returns the name of the table for this class, in the case expected by the database.

$schemaname = $class->schema_name();

Returns the name of the schema for this class.

$str = $class->full_table_name();

Returns a quoted, dotted version of the name, using the quote character and name spearator that the database expects.

Postgres example: "foo_schema"."bar_table"

$colname = $class->column_name($field_name, $field_name2, ..);

Returns the database column underlying the given field.

If more than one field is given, returns a list or arrayref, depending on context.

$fieldname = $class->field_name($column_name, $column_name2,...);

Returns the object field that represents the given database column.

If more than one column is given, returns a list or arrayref, depending on context.

$fieldname = $class->first_primary_key_field();

Returns the name of the first primary key field for this class.

This is probably a bad idea - you may want to use primary_key_fields instead.

@pks = $class->primary_key_fields();

Returns the names of the primary key fields for this class. Returns an array ref in scalar context.

$bool = $o->is_field_primary_key('fieldname');

Returns true if the named field is a primary key.

$fieldname = $class->first_primary_key_column();

Returns the name of the first primary key column for this class, in database column case.

This is probably a bad idea - you may want to use primary_key_columns instead.

@pks = $class->primary_key_columns();

Returns the name of the primary key columns for this class, in database column case. Returns an array ref in scalar context.

$int = $class->primary_key_column_count();

Returns the number of primary key columns for the class.

@cols = $class->column_names();

Returns the list of database columns, in the same order as field_names.

@columns = $class->audit_columns();

Returns a list of any columns that are expected to be automatically populated as auditing data. If the class is not being audited, this list is empty. See Class::ReluctantORM::Audited.

@fields = $class->fields();

@fields = $class->field_names();

Returns a list of the fields in the class.

@fields = $class->field_names_including_relations()

Returns a merged list of both the direct fields as well fields defined via Relationships.

@fields = $class->refresh_fields();

@cols = $class->refresh_columns();

Returns a list of the fields or columns that should be refreshed on each update or insert.

@fields = $class->essential_fields();

Returns a list of the fields that are always fetched when an object of this type is fetched from the database. Normally this is the same as fields(), but some Relationships (HasLazy, for example) will modify this.

@fields = $class->essential_sql_columns($table);

Returns a list of SQL::Column objects that are always fetched when an object of this type is fetched from the database. Normally this is the same as sql_columns(), but some Relationships (HasLazy, for example) will modify this.

Optionally, pass in a SQL::Table reference to specify the Table instance to link each column to.

$bool = $class->is_static();

Returns true if the class is "static" - usually implemented via Class::ReluctantORM::Static. Such classes fetch all rows on the first fetch, and tehn cache thier results for the life of the process.

$bool = $class->updatable();

Returns true if this class permits update() to be called.

$bool = $class->deletable();

Returns true if this class permits delete() to be called.

$bool = $class->insertable();

Returns true if this class permits insert() to be called.

CONSTRUCTORS

There are three classes of constructors:

memory only

These constructors, new() and clone(), only create an object in memory. Use insert() to commit them to the database.

database fetch

These constructors, fetch() and search(), take an existing database row and turn it into an object in memory.

memory and database

The create() constructor creates a new row in the database and returns the new object.

Fetch and Search differ in their handling of empty result sets: fetch methods throw an exception if nothing is found, while search methods simply return undef or an empty list.

$o = $class->new(field1 => $value1, ...);

Creates a new object in memory only (no database contact).

$o = $class->create(field1 => $value1, ...);

Creates a new object in memory, and creates a matching row in the database.

$o = $class->fetch(123);

$o = $class->fetch(key1_name => $key1_val, key2_name => key2_val...);

Retrieves the object from the database whose primary key matches the given argument(s).

In the first form, valid only for classes with a single-column primary key, the one primary value must be provided.

In the second form, you may specify values for multi-column primary keys. Any PK columns not specified will be interpreted as null. You may specify either field names or column names; they will be interpreted first as column names, and if that fails, will be treated as field names.

If no such object exists, an Class::ReluctantORM::Exception::Data::NotFound is thrown. For a gentler approach, use the search() family.

@objects = $class->fetch_all([order => 'order_clause']);

Fetches all rows from the table, optionally ordered by the given order clause.

For pagination support, see search().

@objects = $class->fetch_deep( FIELD_NAME => $value, %common_options);

@objects = $class->fetch_deep( where => $where_obj, %common_options);

@objects = $class->fetch_deep( where => $sql_string, execargs => \@binds, parse_where => 0, %common_options);

Performs a query with broad and/or deep prefetching. The three forms offer different ways of specifying search criteria.

In the first form, provide exactly one field name with value. The search operator will be an '='.

In the second form, provide a Class::ReluctantORM::SQL::Where object. It may contain Params, which must have their bind values already set.

In the third form, provide a SQL string in a dialect that your Driver will understand. You may use '?' to represent a bind placeholder, and provide the bind values in the execargs argument. Depending on the values of the global options 'parse_where' and 'parse_where_hard', CRO may attempt to use the Driver to parse the SQL string into a Where object (which has certain advantages internally, especially for object inflation). If this fails, a ParseError exception will be thrown. You may disable this behavior with parse_where. Even if parse_where is false, the SQL string will still be mangled - we need to perform table-realiasing. Table alias macros are supported.

@objects = $class->fetch_deep( where => $clause, execargs => [], with => { subfield => {}}, hint => '', limit => 5, offset => 6, order_by => '', parse_where => 0 );

Common options:

limit

Optional integer. order_by is required if you use this (otherwise your results are nondeterministic). Limits the number of top-level objects. Due to JOINs, more rows may be actually returned. Better drivers can do this in SQL, but some drivers may be obliged to implement this in Perl. Some drivers may place restrictions on the WHERE clause if you use limit (like only permitting a where to reference the main table).

offset

Option integer, onlly permitted if limit is provided. Skip this many records.

order_by

Optional sort instructions. Provide either a Class::ReluctantORM::SQL::OrderBy, or a SQL string. You may only reference columns from the primary table. Some drivers may be obliged to implement this in Perl.

hint

Optional driver hints. See your driver documentation.

with

Prefetching instructions. See below and Class::ReluctantORM::Manual::Prefetching .

To specify the prefetch tree, provide the 'with' parameter as a hashref. Name each subfield by method_name, using an empty hashref to denote a leaf. For example, if you are calling Pirate->fetch_deep, and you want the pirate's ship and parrot to be prefetched, use with => {parrot => {}, ship => {}}. To get the ship's home port as well, use with => {parrot => {}, ship => { home_port => {}}} .

It is an error to pass unrecognized parameters to this method.

In list context, all results are returned as a list. In scalar context, only the first top-level result is returned. If the query results in empty results, an exception is thrown. See search_deep for an exceptionless alternative.

$object = $class->search($id);

@objects = $class->search(where => $clause, execargs => [execargs], order_by => $clause, limit => 5, offset => 3);

In the first form, acts as a non-fatal fetch(). You may only use this form if your class has a single-column primary key.

In the second form, full-fledged search facility. ou In either form, returns all results as a list in array context, or first result in scalar context. In the case of no results, returns an empty list in list context or undef in scalar context.

The where clause is the only required option. Use column names, not field names (though they are usually the same). Do not include the word 'WHERE'. You may use placeholders ('?'), so long as you include the execargs argument as well, which should be an arrayref of your arguments.

Supports pagination.

$o = $class->search_by_FIELD($value);

@objects = $class->search_by_FIELD($value);

Similar to fetch_by_FIELD, but returns undef or an empty list when no results are available, rather than throwing an exception.

@objects = $class->search_deep( FIELD_NAME => $value, with => { subfield => {}}, hint => '', limit => 5, offset => 6, order_by => '' );

@objects = $class->search_deep( where => $clause, execargs => [], with => { subfield => {}}, hint => '', limit => 5, offset => 6, order_by => '' );

Operates identically to fetch_deep, but does not throw an exception if no results are found.

$pirate->fetch_deep_overlay(with => \%with);

Pirate->fetch_deep_overlay(with => \%with, objects => \@pirates);

Given an existing, already-fetched object, performs an afterthought fetch - returning to the database to fetch additional related objects.

Other methods allow you to do this on a per-relation-basis (ie, $pirate->fetch_ship()) or to fetch deeply, starting with one relation ($ship->pirates->fetch_deep(with => {booties => {}})) . This method, however, acts on the parent object, allowing you to fetch accross multiple relations in one query.

In the first form, one query is performed to "re-fetch" a copy of the object, then the original is merged with the copy.

In the second form, multiple objects may be re-fetched with one query.

While merging, the fresh copy from the database wins all conflicts. Additionally, if you re-fetch over a relation you have modified, the changes are lost. Finally, there is nothing stopping you from fetching a "shallower" tree than you originally fetched.

PRIMARY KEYS

$key = $o->id();

$key_href = $o->id();

@keys = $o->id();

Returns the primary key value(s) for this object. If $o->is_inserted() is false, this will return undef.

In the first form, (scalar context), if the class has only one primary key column, the primary key value is returned. If the object has not been inserted, undef is returned.

In the second form (scalar context), if the class a multi-column primary key, a hashref is returned with the primary keys listed by their field names. If the object has not been inserted, undef is returned.

In the third form, (list context), the primary key values are returned as a list, guarenteed to be in proper PK definition order. If the object has not been inserted, an empty list is returned (NOT a list of undefs, which could be confused with an all-NULL primary key)

Use $class->primary_key_fields to get the names of the primary key fields.

$key = $o->primary_key();

$key_href = $o->primary_key();

@keys = $o->primary_key();

$key = $o->primary_keys();

$key_href = $o->primary_keys();

@keys = $o->primary_keys();

primary_key() and primary_keys() are aliases for id().

$bool = $obj->has_all_primary_keys_defined();

Returns true if all primary key columns have a defined value.

If this is true, we can reliably identify this object in a unique way.

CRUD

$o->insert();

Commits a newly created object into the database.

If the class was built with 'refresh_on_update' fields, these fields are fetched, using a single query for the insert and the fetch. The primary key is always fetched.

If the object already has been inserted, dies.

$o->update();

Commits any changes to an object to the database, and clears the dirty flag.

If the class was built with 'refresh_on_update' fields, these fields are fetched, using a single query for the update and the fetch.

If the class was built with the updatable flag false, this always dies.

If the object is not dirty, does nothing.

If the object already not been inserted, dies.

$o->save();

Convenience method. Calls either insert() or update(), depending on is_inserted. Does nothing if the object was not dirty.

$o->delete();

Deletes the corresponding row from the database.

If the class was built with the deletable flag false, this always dies.

If the object has not been inserted, dies.

$class->delete_where(where => '...', execargs => [ ... ]);

Delete arbitrary rows from the database. Does not affect objects already fetched.

If the class was built with the deletable flag false, this always dies.

'where' may be a SQL string, or a Class::ReluctantORM::SQL::Where object. If where is a sql string and contains '?' characters, you must also provide the execargs option with bindings.

DIRTINESS

"Dirtiness" refers to whether the data in-memory has been modified since being read from the database. If so, we know we need to save that data, and call it "dirty".

$bool = $o->is_dirty();

Returns true if the object has been modified since it was thawed from the database, or if it has never been inserted at all.

$bool = $o->is_field_dirty('field_name');

Checks an individual field for dirtiness.

@fields = $o->dirty_fields();

@cols = $o->dirty_columns();

Returns a list of fields or columns that are due for an update. Fields get added to this list whenever you call a mutator.

$bool = $o->is_inserted();

Returns true if the object originated from the database, or has been inserted into the database since its creation.

FIELD ACCESSORS

These methods correspond to data attributes (member variables) on the OO side, and table columns on the relational side.

At startup, as each model class calls build_class, CRO will list the columns on your table and create a method for each column.

Two caveats are in order if you are in a long-running process like mod_perl. First, this column detection only happens once, at compile time, so adding a column while running is safe, but to see the column in your datamodel, you'll need to restart. Secondly, since the running code expects the columns to always be there, renaming or deleting columns may be a breaking change (of course, if you're using those accessors or mutators, that's a breaking change anyway). The concern is that the problem will not be detected until the code hits the database.

Primitive aggregate functionality is provided, but unless your needs are simple, you will be a sad little panda.

$value = $obj->foo_column()

$obj->foo_column($foo_value)

To read the value, just call the method with no arguments. The value will be passed through any Filters, then returned.

To set the value, call the method with the new value. CRO will pass the new value through any Filters, then update the object with the value. The data is not saved to the database until you call save() or update().

To set a column to NULL, pass undef as the value.

$number = $class->count_of_foo(where => $where, execargs => \@args)

$number = $class->avg_of_foo(where => $where, execargs => \@arg)

.. etc ..

For each column, methods are created on first use that have the name <AGGREGATE>_of_<COLUMN>. The list of aggregate functions is determined by your Driver; but you do get a handful by default - see Class::ReluctantORM::SQL::Function.

You may optionally provide a where clause, with optional execargs, as for the search() methods.

FILTER SUPPORT

These methods provide support for transforming the value of a field when it is being read from an object, or being written to the object.

One common use of this is to escape all HTML entities, for example.

$obj->append_filter($filter)

See Class::ReluctantORM::Filter.

$class->attach_class_filter($filter)

See Class::ReluctantORM::Filter.

$obj->set_filters(...)

See Class::ReluctantORM::Filter.

$obj->clear_filters()

See Class::ReluctantORM::Filter.

$obj->remove_filter(...)

See Class::ReluctantORM::Filter.

@filters = $obj->read_filters_on_field(...)

See Class::ReluctantORM::Filter.

@filters = $obj->write_filters_on_field(...)

See Class::ReluctantORM::Filter.

$val = $obj->raw_field_value('field');

$obj->raw_field_value('field', $newval);

Gets or sets the raw, internal value of a field. This method bypasses the filtering mechanism.

RELATIONSHIP SUPPORT

$rel = $class->relationships('field');

$rel_by_name_href = $class->relationships();

@rels = $class->relationships();

Accesses information about the relationships this class has with other Class::ReluctantORM classes.

In the first form, returns a Class::ReluctantORM::Relationship object (or a subclass thereof), for the given field. For example, you might say:

  $rel = Pirate->relationships('ship');

In the second form (scalar context), returns a hashref of all relationships the class participates in, keyed by field name.

In the third form (list context), returns an array of all relationships the class participates in.

@relnames = $class->relationship_names();

Returns the names of all relationships on the class. These are the method names used to access the related object or collection.

$bool = $o->is_relation_fetched('relname');

$bool = $o->is_field_fetched('fieldname');

Returns true or false, depending on whether the named field or relation has been fetched. If true, you may call the accessor without rish of a FetchRequired exception.

$class->clone_relationship($rel);

Copies a relationship to this class, so that this class is the linking class on the new relationship. The linked class remains the same.

For this to work, this class must have the same foreign keys that the orginal linking class used.

This is useful when you are using table-based inheritance (for example, as under PostgreSQL) and you want your inheriting class to have the same relationships as the parent. Then you can just do:

  foreach my $rel (Parent->relationships) {
     Child->clone_relationship($rel);
  }

MONITORING SUPPORT

Class::ReluctantORM->install_global_monitor($mon);

Installs a monitor that will be used on all Class::ReluctantORM queries.

$mon should be a Class::ReluctantORM::Monitor.

@mons = Class::ReluctantORM->global_monitors();

Returns a list of globally applicable monitors.

Class::ReluctantORM->remove_global_monitors();

Removes all globally applicable monitors.

Class::ReluctantORM->remove_global_monitor($mon);

Removes one global monitor.

MyClass->install_class_monitor($mon);

Installs a monitor that will only monitor this specific subclass. The monitor is actually attached to the driver of this class.

@mons = MyClass->class_monitors();

Lists all monitors specific to this class.

MyClass->remove_class_monitors();

Removes all class-specific monitors.

TRIGGER SUPPORT

Class::ReluctantORM supports Perl-side triggers. (You are also free to implement db-side triggers, of course.)

A trigger is a coderef that will be called before or after certain events. The args will be the CRO object, followed by the name of the trigger event.

Triggers are assigned at the class level. You can assign multiple triggers to the event by making repeated calls to add_trigger. They will be called in the order they were added.

The following events are currently supported:

after_retrieve
before_insert, after_insert
before_update, after_update
before_delete, after_delete
before_save, after_save
before_refresh, after_refresh

Before/after save is a little unusual - it is called within save(), and either the insert or update triggers will be called as well. The order is:

  1. before_save

  2. before_insert OR before_update

  3. after_insert OR after_update

  4. after_save

MyClass->add_trigger('event', $coderef);

Arranges for $coderef to be called whenever 'event' occurs. $coderef will be passed the CRO object and the event name as the two arguments.

remove_trigger('event', $codref);

Removes the given trigger from the event.

MyClass->remove_all_triggers();

MyClass->remove_all_triggers('event');

In the first form, removes all triggers from all events.

In the second form, removes all triggers from the given event.

@trigs = MyClass->list_triggers('event');

Lists all triggers from the given event, in the order they will be applied.

AUTHOR

Clinton Wolfe (clwolfe@cpan.org) 2008-2012

With extensive real-world usage from the fine folks at OmniTI (www.omniti.com).

COPYRIGHT

Copyright OmniTI 2012. All Rights Reserved.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

BUGS

Let's track them in RT, shall we. https://rt.cpan.org/Dist/Browse.html?Name=Class-ReluctantORM

1 POD Error

The following errors were encountered while parsing the POD:

Around line 321:

'=item' outside of any '=over'