Skip site navigation (1)Skip section navigation (2)

FreeBSD Manual Pages

  
 
  

home | help
DBIx::Class::Helper::RUserOContributedxPerlss::Helper::Row::OnColumnChange(3)

NAME
       DBIx::Class::Helper::Row::OnColumnChange	- Do things when the values of
       a column	change

SYNOPSIS
	package	MyApp::Schema::Result::Account;

	use parent 'DBIx::Class::Core';

	__PACKAGE__->load_components(qw(Helper::Row::OnColumnChange));

	__PACKAGE__->table('Account');

	__PACKAGE__->add_columns(
	   id => {
	      data_type		=> 'integer',
	      is_auto_increment	=> 1,
	   },
	   amount => {
	      data_type		 => 'float',
	      keep_storage_value => 1,
	   },
	);
	sub on_column_change_allow_override_args { 1 }

	__PACKAGE__->before_column_change(
	  amount => {
	     method   => 'bank_transfer',
	     txn_wrap => 1,
	  }
	);

	sub bank_transfer {
	  my ($self, $old_value, $new_value) = @_;

	  my $delta = abs($old_value - $new_value);
	  if ($old_value < $new_value) {
	     Bank->subtract($delta)
	  } else {
	     Bank->add($delta)
	  }
	}

	1;

       or with DBIx::Class::Candy:

	package	MyApp::Schema::Result::Account;

	use DBIx::Class::Candy -components => ['Helper::Row::OnColumnChange'];

	table 'Account';

	column id => {
	   data_type	     =>	'integer',
	   is_auto_increment =>	1,
	};

	column amount => {
	   data_type	      => 'float',
	   keep_storage_value => 1,
	};
	sub on_column_change_allow_override_args { 1 }

	before_column_change amount => {
	   method   => 'bank_transfer',
	   txn_wrap => 1,
	};

	sub bank_transfer {
	  my ($self, $old_value, $new_value) = @_;

	  my $delta = abs($old_value - $new_value);
	  if ($old_value < $new_value) {
	     Bank->subtract($delta)
	  } else {
	     Bank->add($delta)
	  }
	}

	1;

DESCRIPTION
       This module codifies a pattern that I've	used in	a number of projects,
       namely that of doing something when a column changes it's value in the
       database.  It leverages DBIx::Class::Helper::Row::StorageValues for
       passing in the $old_value, which	do not have to use.  If	you leave the
       "keep_storage_value" out	of the column definition it will just pass
       "undef" in as the $old_value.  Also note	the "txn_wrap" option.	This
       allows you to specify that you want the call to "update"	and the	call
       to the method you requested to be wrapped in a transaction.  If you end
       up calling more than one	method due to multiple column change methods
       and more	than one specify "txn_wrap" it will still only wrap once.

       I've gone to great lengths to ensure that order is preserved, so
       "before"	and "around" changes are called	in order of definition and
       "after" changes are called in reverse order.

       To be clear, the	change methods only get	called if the value will be
       changed after "update" runs.  It	correctly looks	at the current value
       of the column as	well as	the arguments passed to	"update".

CANDY EXPORTS
       If used in conjunction with DBIx::Class::Candy this component will
       export:

       before_column_change
       around_column_change
       after_column_change

NO SURPRISE RACE CONDITIONS
       One thing that should be	made totally clear is that the column change
       callbacks are in	effect only once in a given update.  If	you expect to
       be able to do something weird like calling one of the callbacks which
       changes a value with an accessor	which calls a callback etc etc,	you
       probably	just need to write some	code to	do that	yourself.  This	helper
       is specifically made with the aim of reacting to	changes	immediately
       before they hit the database.

METHODS
   before_column_change
	__PACKAGE__->before_column_change(
	  col_name => {
	     method   => 'method', # <-- anything that can be called as	a method
	     txn_wrap => 1,	   # <-- true if you want it to	be wrapped in a	txn
	  }
	);

       Note: the arguments passed to "method" will be "$self, $old_value,
       $new_value".

   after_column_change
	__PACKAGE__->after_column_change(
	  col_name => {
	     method   => 'method', # <-- anything that can be called as	a method
	     txn_wrap => 1,	   # <-- true if you want it to	be wrapped in a	txn
	  }
	);

       Note: the arguments passed to "method" will be "$self, $new_value,
       $new_value". (Because the old value has been changed.)

   around_column_change
	__PACKAGE__->around_column_change(
	  col_name => {
	     method   => 'method', # <-- anything that can be called as	a method
	     txn_wrap => 1,	   # <-- true if you want it to	be wrapped in a	txn
	  }
	);

       Note: the arguments passed to "method" will be "$self, $next,
       $old_value, $new_value".

       Around is subtly	different than the other two callbacks.	 You must call
       $next in	your method or it will not work	at all.	 A silly example of
       how this	is done	could be:

	sub around_change_name {
	  my ($self, $next, $old, $new)	= @_;

	  my $govt_records = $self->govt_records;

	  $next->();

	  $govt_records->update({ name => $new });
	}

       Note: the above code implies a weird database schema.  I	haven't
       actually	seen a time when I've needed around yet, but it	seems like
       there is	a use-case.

       Also Note: you don't get	to change the args to $next.  If you think you
       should be able to, you probably don't understand	what this component is
       for.  That or you know something	I don't	(equally likely.)

   on_column_change_allow_override_args
       This is a method	that allows a user to circumvent a strange bug in the
       initial implementation.	Basically, if the user wanted, she could use
       "before_column_change" to override the value of a given column before
       "update"	gets called, thus replacing the	value.	Unfortunately this
       worked in the case of accessors setting the value, but not if the user
       had used	an argument to "update".  To be	clear, if you want the
       following to actually replace the value:

	__PACKAGE__->before_column_change(
	   name	=> {
	      method   => sub {
		 my ($self, $old, $new)	= @_;

		 $self->name(uc	$new);
	      },
	   },
	);

       you will	need to	define this in your result class:

	sub on_column_change_allow_override_args { 1 }

       If for some reason you need the old style, a default of false is
       already set.  If	you are	painted	in the corner and need both, you can
       create an accessor and set it yourself to change	the behavior:

	__PACKAGE__->mk_group_accessors(inherited => 'on_column_change_allow_override_args');
	...
	$obj->on_column_change_allow_override_args(1); # works the new way

AUTHOR
       Arthur Axel "fREW" Schmidt <frioux+cpan@gmail.com>

COPYRIGHT AND LICENSE
       This software is	copyright (c) 2020 by Arthur Axel "fREW" Schmidt.

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

perl v5.32.1			  2DBIx::Class::Helper::Row::OnColumnChange(3)

NAME | SYNOPSIS | DESCRIPTION | CANDY EXPORTS | NO SURPRISE RACE CONDITIONS | METHODS | AUTHOR | COPYRIGHT AND LICENSE

Want to link to this manual page? Use this URL:
<https://www.freebsd.org/cgi/man.cgi?query=DBIx::Class::Helper::Row::OnColumnChange&sektion=3&manpath=FreeBSD+13.0-RELEASE+and+Ports>

home | help