Subject: | passing custom params to save to foreign keys |
I have a need to pass custom params during a save to foreign keys. Attached is a patch with some documentation on how to use it and a new test file.
Subject: | custom-params-to-fk-on-save.diff |
Index: lib/Rose/DB/Object.pm
===================================================================
--- lib/Rose/DB/Object.pm (revision 2313)
+++ lib/Rose/DB/Object.pm (working copy)
@@ -20,6 +20,8 @@
our $Debug = 0;
+our @valid_save_params = (qw(changes_only prepare_cached cascade));
+
#
# Object data
#
@@ -537,7 +539,7 @@
my %code_args =
map { ($_ => $args{$_}) } grep { exists $args{$_} }
- qw(changes_only prepare_cached cascade);
+ @valid_save_params;
#
# Do pre-save stuff
@@ -2431,6 +2433,15 @@
If an insert was performed and the primary key is a single column that supports auto-generated values, then the object accessor for the primary key column will contain the auto-generated value. See the L<Serial and Auto-Incremented Columns|/"Serial and Auto-Incremented Columns"> section for more information.
+It is possible to set custom params that are passed to foreign keys on save if those params are added to @valid_save_params.
+
+Example:
+
+ push @Rose::DB::Object::valid_save_params, 'my_custom_param';
+
+ # now able to call ->save(my_custom_param => 'my value');
+
+
=item B<update [PARAMS]>
Update the current object in the database table. This method should only be used when you're absolutely sure that you want to B<force> the current object to be updated, rather than inserted. It is recommended that you use the L<save|/save> method instead of this one in most circumstances. The L<save|/save> method will "do the right thing," executing an insert or update as appropriate for the current situation.
Subject: | db-object-relationship-custom-params.t |
#!/usr/bin/perl -w
use strict;
use Test::More 'no_plan'; #tests => 1604;
BEGIN
{
require 't/test-lib.pl';
use_ok('Rose::DB::Object');
use_ok('Rose::DB::Object::Manager');
}
our($HAVE_SQLITE);
use FindBin qw($Bin);
#
# SQLite
#
SKIP: foreach my $db_type ('sqlite')
{
skip("SQLite tests", 466) unless($HAVE_SQLITE);
Rose::DB->default_type($db_type);
my $o = MySQLiteObject->new(name => 'John', id => 1);
ok(ref $o && $o->isa('MySQLiteObject'), "new() 1 - $db_type");
ok($o->other_obj({k1 => 1}), "set a foreign key");
push @Rose::DB::Object::valid_save_params, 'custom_valid';
ok($o->save(custom => 1, custom_valid => 1), "save with a custom variables");
}
BEGIN
{
#
# SQLite
#
my $dbh;
eval
{
$dbh = Rose::DB->new('sqlite_admin')->retain_dbh()
or die Rose::DB->error;
};
if(!$@ && $dbh)
{
our $HAVE_SQLITE = 1;
# Drop existing table and create schema, ignoring errors
{
local $dbh->{'RaiseError'} = 0;
local $dbh->{'PrintError'} = 0;
$dbh->do('DROP TABLE rose_db_object_test');
$dbh->do('DROP TABLE rose_db_object_other');
}
$dbh->do(<<"EOF");
CREATE TABLE rose_db_object_other
(
k1 INT NOT NULL,
name VARCHAR(32),
UNIQUE(k1)
)
EOF
$dbh->do(<<"EOF");
CREATE TABLE rose_db_object_test
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
name VARCHAR(32) NOT NULL,
fk1 INT,
FOREIGN KEY (fk1) REFERENCES rose_db_object_other (k1)
)
EOF
$dbh->disconnect;
# Create test subclasses
package MySQLCustomBaseObject;
our @ISA = qw(Rose::DB::Object);
sub save
{
my ($self, %args) = @_;
if (ref($self) eq "MySQLiteObject")
{
# main class, does it have the custom variable?
Test::More::ok(exists $args{custom}, "custom property exists in main class");
}
if (ref($self) eq "MySQLiteOtherObject")
{
Test::More::ok(!exists $args{custom}, "custom property does not exist in foreign class");
}
Test::More::ok(exists $args{custom_valid}, "defined custom property exists in every class");
return $self->SUPER::save(%args);
}
package MySQLiteOtherObject;
our @ISA = qw(MySQLCustomBaseObject);
sub init_db { Rose::DB->new('sqlite') }
MySQLiteOtherObject->meta->table('rose_db_object_other');
MySQLiteOtherObject->meta->columns
(
name => { type => 'varchar'},
k1 => { type => 'int' },
);
MySQLiteOtherObject->meta->primary_key_columns(qw(k1));
MySQLiteOtherObject->meta->initialize;
package MySQLiteObject;
use Rose::DB::Object::Helpers qw(has_loaded_related);
our @ISA = qw(MySQLCustomBaseObject);
sub init_db { Rose::DB->new('sqlite') }
MySQLiteObject->meta->table('rose_db_object_test');
MySQLiteObject->meta->columns
(
'name',
id => { primary_key => 1 },
fk1 => { type => 'int' },
);
MySQLiteObject->meta->add_foreign_keys
(
other_obj =>
{
class => 'MySQLiteOtherObject',
rel_type => 'one to one',
key_columns =>
{
fk1 => 'k1',
},
},
);
MySQLiteObject->meta->initialize;
}
};
END
{
# Delete test table
if($HAVE_SQLITE)
{
# SQLite
my $dbh = Rose::DB->new('sqlite_admin')->retain_dbh()
or die Rose::DB->error;
$dbh->do('DROP TABLE rose_db_object_test');
$dbh->do('DROP TABLE rose_db_object_other');
$dbh->disconnect;
}
}