DBIx-Class-Events

 view release on metacpan or  search on metacpan

lib/DBIx/Class/Events.pm  view on Meta::CPAN

    __PACKAGE__->load_components(qw/ InflateColumn::DateTime /);

    __PACKAGE__->table('artist_event');

    __PACKAGE__->add_columns(
        artisteventid => { data_type => 'integer', is_auto_increment => 1 },
        artistid      => { data_type => 'integer' },

        # The type of event
        event         => { data_type => 'varchar' },

        # Any other custom columns you want to store for each event.

        triggered_on => {
            data_type     => 'datetime',
            default_value => \'NOW()',
        },

        # Where we store freeform data about what happened
        details => { data_type => 'longtext' },
    );

    __PACKAGE__->set_primary_key('artisteventid');

    # You should set up automatic inflation/deflation of the details column
    # as it is used this way by "state_at" and the insert/update/delete
    # events.  Does not have to be JSON, just be able to serialize a hashref.
    {
        my $json = JSON->new->utf8;
        __PACKAGE__->inflate_column( 'details' => {
            inflate => sub { $json->decode(shift) },
            deflate => sub { $json->encode(shift) },
        } );
    }

This L<C<belongs_to>|DBIx::Class::Relationship/belongs_to>
relationship is optional,
and the examples and tests assume if it exists,
it is not a real database-enforced foreign key
that will trigger constraint violations if the thing being tracked is deleted.

    # A path back to the object that this event is for,
    # not required unlike the has_many "events" relationship above
    __PACKAGE__->belongs_to(
        'artist' => ( 'MyApp::Schema::Result::Artist', 'artistid' ) );

You probably also want an index for searching for events:

    sub sqlt_deploy_hook {
        my ( $self, $sqlt_table ) = @_;
        $sqlt_table->add_index(
            name   => 'artist_event_idx',
            fields => [ "artistid", "triggered_on", "event" ],
        );
    }

=head1 PRECONFIGURED EVENTS

Automatically creates Events for actions that modify a row.

See the L</BUGS AND LIMITATIONS> of bulk modifications on events.

=over

=item insert

Logs all columns to the C<details> column, with an C<insert> event.

=item update

Logs dirty columns to the C<details> column, with an C<update> event.

=item delete

Logs all columns to the C<details> column, with a C<delete> event.

See the L</BUGS AND LIMITATIONS> for more information about
using this method with a database enforced foreign key.

=back

=head1 METHODS

=head2 event

Inserts a new event with L</event_defaults>:

    my $new_event = $artist->event( $event => \%params );

First, the L</event_defaults> method is called to build a list of values
to set on the new event.  This method is passed the C<$event> and a reference
to C<%params>.

Then, the C<%params>, filtered for valid L</events_relationship> C<columns>,
are added to the C<create_related> arguments, overriding the defaults.

=head2 state_at

Takes a timestamp and returns the state of the thing at that timestamp as a
hash reference.  Can be either a correctly deflated string or a DateTime
object that will be deflated with C<format_datetime>.

Returns undef if the object was not C<in_storage> at the timestamp.

    my $state = $schema->resultset('Artist')->find( { name => 'David Bowie' } )
        ->state_at('2006-05-29 08:00');

An idea is to use it to recreate an object as it was at that timestamp.
Of course, default values that the database provides will not be included,
unless the L</event_defaults> method accounts for that.

    my $resurrected_object
        = $object->result_source->new( $object->state_at($timestamp) );

See ".. format a DateTime object for searching?" under L<DBIx::Class::Manual::FAQ/Searching>
for details on formatting the timestamp.

You can pass additional L<search|DBIx::Class::ResultSet/search> conditions and
attributes to this method.  This is done in context of searching the events
table:

    my $state = $object->state_at($timestamp, \%search_cond, \%search_attrs);

=head1 BUGS AND LIMITATIONS

There is no attempt to handle bulk updates or deletes.  So, any changes to the
database made by calling
L<"update"|DBIx::Class::ResultSet/update> or L<"delete"|DBIx::Class::ResultSet/delete>
will not create events the same as L<single row|DBIx::Class::Row> modifications.  Use the
L<"update_all"|DBIx::Class::ResultSet/update_all> or L<"delete_all"|DBIx::Class::ResultSet/delete_all>
methods of the C<ResultSet> if you want these triggers.

If you create the C<belongs_to> relationship
described under L</Tracking Table>
as a database-enforced foreign key
then deleting from the tracked table will fail due to those constraints.

There are three required columns on the L</events_relationship> table:
C<event>, C<triggered_on>, and C<details>.  We should eventually make those
configurable.

=head1 SEE ALSO

=over

=item L<DBIx::Class::AuditAny>

=item L<DBIx::Class::AuditLog>

=item L<DBIx::Class::Journal>

=item L<DBIx::Class::PgLog>

=back

=head1 AUTHOR

Grant Street Group <developers@grantstreet.com>

=head1 COPYRIGHT AND LICENSE

This software is Copyright (c) 2018 - 2024 by Grant Street Group.

This is free software, licensed under:

  The Artistic License 2.0 (GPL Compatible)

=cut

__END__


1;



( run in 4.014 seconds using v1.01-cache-2.11-cpan-75ffa21a3d4 )