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);
23 $self->result_source->schema->_journal_schema;
28 my ($self, @args) = @_;
30 return if($self->in_storage);
32 my $res = $self->next::method(@args);
34 $self->journal_log_insert();
39 sub journal_log_insert
43 if ( $self->in_storage ) {
44 my $j = $self->_journal_schema;
45 my $change_id = $j->journal_create_change()->id;
46 $j->journal_update_or_create_log_entry( $self, create_id => $change_id );
47 $j->journal_record_in_history( $self, audit_change_id => $change_id );
51 ## On delete, update delete_id of AuditLog
55 my ($self, @rest) = @_;
56 $self->next::method(@rest);
57 $self->journal_log_delete(@rest);
60 sub journal_log_delete
64 unless ($self->in_storage) {
65 my $j = $self->_journal_schema;
66 $j->journal_update_or_create_log_entry( $self, delete_id => $j->journal_create_change->id );
70 ## On update, copy previous row's contents to AuditHistory
74 my ($self, $upd, @rest) = @_;
75 $self->journal_log_update($upd, @rest);
76 $self->next::method($upd, @rest);
79 sub journal_log_update
81 my ($self, $upd, @rest) = @_;
85 my $j = $self->_journal_schema;
87 my $change = $j->journal_create_change;
88 my $prev = $self->result_source->resultset->find( $self->ident_condition );
89 $j->journal_record_in_history( $prev, audit_change_id => $change );
95 DBIx::Class::Journal - auditing for tables managed by DBIx::Class
100 use base 'DBIx::Class::Schema';
102 __PACKAGE__->load_components(qw/+DBIx::Class::Schema::Journal/);
104 __PACKAGE__->journal_connection(['dbi:SQLite:t/var/Audit.db']);
105 __PACKAGE__->journal_user(['My::Schema::User', {'foreign.userid' => 'self.user_id'}]);
110 $schema->changeset_user($user->id);
111 my $new_artist = $schema->txn_do( sub {
112 return = $schema->resultset('Artist')->create({ name => 'Fred' });
118 The purpose of this L<DBIx::Class> component module is to create an
119 audit-trail for all changes made to the data in your database (via a
120 DBIx::Class schema). It creates changesets and assigns each
121 create/update/delete operation an id. The creation and deletion date
122 of each row is stored, as well as the previous contents of any row
125 All queries which want auditing should be called using
126 L<DBIx::Class::Schema/txn_do>, which is used to create changesets for
129 To track who did which changes, the user_id (an integer) of the
130 current user can be set, a session_id can also be set, both are
133 To access the auditing schema to look at the auditdata or revert a
134 change, use C<< $schema->_journal_schema >>.
138 The journal schema contains a number of tables.
144 Each changeset row has an auto-incremented ID, optional user_id and
145 session_id, and a set_date which defaults to the current datetime.
147 A ChangeSet has_many Changes.
151 Each change/operation done in the transaction is recorded as a row in
152 the ChangeLog table. It contains an auto-incrementing ID, the
153 changeset_id and an order column for the ordering of each change in
158 For every table in the original database that is to be audited, an
159 AuditLog table is created. Each auditlog row has an id which will
160 contain the primary key of the table it is associated with. (NB:
161 currently only supports integer-based single column PKs). The
162 create_id and delete_id fields contain the IDs of the Changes that
163 created or deleted this row.
167 For every table in the original database to be audited, an
168 AuditHistory table is created. Each row has a change_id field
169 containing the ID of the ChangeLog row. The other fields correspond to
170 all the fields from the original table. Each time a column value in
171 the original table is changed, the entire row contents before the
172 change are added as a new row in this table.
180 =item journal_connection \@connect_info
182 Set the connection information for the database to save your audit
185 Leaving this blank assumes you want to store the audit data into your current
186 database. The storage object will be shared by the regular schema and the
189 =item journal_sources \@source_names
191 Set a list of source names you would like to audit, if unset, all
194 NOTE: Currently only sources with a single-column PK are supported, so
195 use this method if you have sources with multi-column PKs.
197 =item journal_storage_type $type
199 Enter the special storage type of your journal schema if needed. See
200 L<DBIx::Class::Storage::DBI> for more information on storage types.
202 =item journal_user \@rel
204 The user_id column in the L</ChangeSet> will be linked to your user id
205 with a belongs_to relation, if this is set with the appropriate
208 =item journal_deploy_on_connect $bool
210 If set to a true value will cause C<journal_schema_deploy> to be called on
213 Not reccomended, but present for backwards compatibility.
215 =item changeset_user $user_id
217 Set the user_id for the following changeset(s). This must be an integer.
219 =item changeset_session $session_id
221 Set the session_id for the following changeset(s). This must be an integer.
223 =item txn_do $code_ref, @args
225 Overloaded L<DBIx::Class::Schema/txn_do>, this must be used to start a
226 new changeset to cover a group of changes. Each subsequent change to
227 an audited table will use the changeset_id created in the most recent
230 Currently nested C<txn_do> calls cause a single ChangeSet object to be created.
236 L<DBIx::Class> - You'll need it to use this.
240 Only single-column integer primary key'd tables are supported for auditing so far.
242 Updates made via L<DBIx::Class::ResultSet/update> are not yet supported.
244 No API for viewing or restoring changes yet.
246 Patches for the above welcome ;)
250 Jess Robinson <castaway@desert-island.me.uk>
252 Matt S. Trout <mst@shadowcatsystems.co.uk> (ideas and prodding)
256 You may distribute this code under the same terms as Perl itself.