1 package DBIx::Class::Journal;
3 use base qw/DBIx::Class/;
8 our $VERSION = '0.02_01';
10 ## On create/insert, add new entry to AuditLog
14 # my ($class, $attrs, @rest) = @_;
16 # $class->result_source->schema->_journal_schema->current_user(delete $attrs->{user_id});
18 # $class->next::method($attrs, @rest);
21 sub journal_update_or_create_log_entry
23 my ($self, $field ) = @_;
24 my $rs = $self->result_source;
25 my $s_name = $rs->source_name();
27 my $jschema = $rs->schema->_journal_schema;
29 my $al = $jschema->resultset("${s_name}AuditLog");
31 my %id = map { $_ => $self->get_column($_)} $self->primary_columns;
36 $extra{$field} = $jschema->journal_create_change->id;
39 $al->update_or_create({ %extra, %id });
44 my ($self, @args) = @_;
46 return if($self->in_storage);
48 my $res = $self->next::method(@args);
50 $self->journal_log_insert();
55 sub journal_log_insert
59 $self->journal_update_or_create_log_entry('create_id')
63 ## On delete, update delete_id of AuditLog
67 my ($self, @rest) = @_;
68 $self->next::method(@rest);
69 $self->journal_log_delete(@rest);
72 sub journal_log_delete
76 $self->journal_update_or_create_log_entry('delete_id')
77 unless $self->in_storage;
80 ## On update, copy previous row's contents to AuditHistory
84 my ($self, $upd, @rest) = @_;
85 $self->journal_log_update($upd, @rest);
86 $self->next::method($upd, @rest);
89 sub journal_log_update
91 my ($self, $upd, @rest) = @_;
95 my $s_name = $self->result_source->source_name();
96 my $ah = $self->result_source->schema->_journal_schema->resultset("${s_name}AuditHistory");
98 my $obj = $self->result_source->resultset->find( $self->ident_condition );
101 change => { changeset_id => $ah->result_source->schema->current_changeset },
108 DBIx::Class::Journal - auditing for tables managed by DBIx::Class
113 use base 'DBIx::Class::Schema';
115 __PACKAGE__->load_components(qw/+DBIx::Class::Schema::Journal/);
117 __PACKAGE__->journal_connection(['dbi:SQLite:t/var/Audit.db']);
118 __PACKAGE__->journal_user(['My::Schema::User', {'foreign.userid' => 'self.user_id'}]);
123 $schema->changeset_user($user->id);
124 my $new_artist = $schema->txn_do( sub {
125 return = $schema->resultset('Artist')->create({ name => 'Fred' });
131 The purpose of this L<DBIx::Class> component module is to create an
132 audit-trail for all changes made to the data in your database (via a
133 DBIx::Class schema). It creates changesets and assigns each
134 create/update/delete operation an id. The creation and deletion date
135 of each row is stored, as well as the previous contents of any row
138 All queries which want auditing should be called using
139 L<DBIx::Class::Schema/txn_do>, which is used to create changesets for
142 To track who did which changes, the user_id (an integer) of the
143 current user can be set, a session_id can also be set, both are
146 To access the auditing schema to look at the auditdata or revert a
147 change, use C<< $schema->_journal_schema >>.
151 The journal schema contains a number of tables.
157 Each changeset row has an auto-incremented ID, optional user_id and
158 session_id, and a set_date which defaults to the current datetime.
160 A ChangeSet has_many Changes.
164 Each change/operation done in the transaction is recorded as a row in
165 the ChangeLog table. It contains an auto-incrementing ID, the
166 changeset_id and an order column for the ordering of each change in
171 For every table in the original database that is to be audited, an
172 AuditLog table is created. Each auditlog row has an id which will
173 contain the primary key of the table it is associated with. (NB:
174 currently only supports integer-based single column PKs). The
175 create_id and delete_id fields contain the IDs of the Changes that
176 created or deleted this row.
180 For every table in the original database to be audited, an
181 AuditHistory table is created. Each row has a change_id field
182 containing the ID of the ChangeLog row. The other fields correspond to
183 all the fields from the original table. Each time a column value in
184 the original table is changed, the entire row contents before the
185 change are added as a new row in this table.
193 =item journal_connection \@connect_info
195 Set the connection information for the database to save your audit
198 Leaving this blank assumes you want to store the audit data into your current
199 database. The storage object will be shared by the regular schema and the
202 =item journal_sources \@source_names
204 Set a list of source names you would like to audit, if unset, all
207 NOTE: Currently only sources with a single-column PK are supported, so
208 use this method if you have sources with multi-column PKs.
210 =item journal_storage_type $type
212 Enter the special storage type of your journal schema if needed. See
213 L<DBIx::Class::Storage::DBI> for more information on storage types.
215 =item journal_user \@rel
217 The user_id column in the L</ChangeSet> will be linked to your user id
218 with a belongs_to relation, if this is set with the appropriate
221 =item journal_deploy_on_connect $bool
223 If set to a true value will cause C<journal_schema_deploy> to be called on
226 Not reccomended, but present for backwards compatibility.
228 =item changeset_user $user_id
230 Set the user_id for the following changeset(s). This must be an integer.
232 =item changeset_session $session_id
234 Set the session_id for the following changeset(s). This must be an integer.
236 =item txn_do $code_ref, @args
238 Overloaded L<DBIx::Class::Schema/txn_do>, this must be used to start a
239 new changeset to cover a group of changes. Each subsequent change to
240 an audited table will use the changeset_id created in the most recent
243 Currently nested C<txn_do> calls cause a single ChangeSet object to be created.
249 L<DBIx::Class> - You'll need it to use this.
253 Only single-column integer primary key'd tables are supported for auditing so far.
255 Updates made via L<DBIx::Class::ResultSet/update> are not yet supported.
257 No API for viewing or restoring changes yet.
259 Patches for the above welcome ;)
263 Jess Robinson <castaway@desert-island.me.uk>
265 Matt S. Trout <mst@shadowcatsystems.co.uk> (ideas and prodding)
269 You may distribute this code under the same terms as Perl itself.