File Coverage

blib/lib/DBIx/SearchBuilder/Record.pm
Criterion Covered Total %
statement 343 382 89.7
branch 120 160 75.0
condition 43 67 64.1
subroutine 53 54 98.1
pod 15 16 93.7
total 574 679 84.5


line stmt bran cond sub pod time code
1             package DBIx::SearchBuilder::Record;
2              
3 20     20   2201595 use strict;
  20         279  
  20         686  
4 20     20   123 use warnings;
  20         48  
  20         790  
5              
6 20     20   130 use vars qw($AUTOLOAD);
  20         38  
  20         1045  
7 20     20   8354 use Class::ReturnValue;
  20         208101  
  20         2087  
8 20     20   10032 use Encode qw();
  20         168366  
  20         538  
9              
10 20     20   7660 use DBIx::SearchBuilder::Util qw/ sorted_values /;
  20         64  
  20         7286  
11              
12             =head1 NAME
13              
14             DBIx::SearchBuilder::Record - Superclass for records loaded by SearchBuilder
15              
16             =head1 SYNOPSIS
17              
18             package MyRecord;
19             use base qw/DBIx::SearchBuilder::Record/;
20              
21             sub _Init {
22             my $self = shift;
23             my $DBIxHandle = shift; # A DBIx::SearchBuilder::Handle::foo object for your database
24              
25             $self->_Handle($DBIxHandle);
26             $self->Table("Users");
27             }
28              
29             # Tell Record what the primary keys are
30             sub _PrimaryKeys {
31             return ['id'];
32             }
33              
34             # Preferred and most efficient way to specify fields attributes in a derived
35             # class, used by the autoloader to construct Attrib and SetAttrib methods.
36              
37             # read: calling $Object->Foo will return the value of this record's Foo column
38             # write: calling $Object->SetFoo with a single value will set Foo's value in
39             # both the loaded object and the database
40             sub _ClassAccessible {
41             {
42             Tofu => { 'read' => 1, 'write' => 1 },
43             Maz => { 'auto' => 1, },
44             Roo => { 'read' => 1, 'auto' => 1, 'public' => 1, },
45             };
46             }
47              
48             # A subroutine to check a user's password without returning the current value
49             # For security purposes, we didn't expose the Password method above
50             sub IsPassword {
51             my $self = shift;
52             my $try = shift;
53              
54             # note two __s in __Value. Subclasses may muck with _Value, but
55             # they should never touch __Value
56              
57             if ( $try eq $self->__Value('Password') ) {
58             return (1);
59             }
60             else {
61             return (undef);
62             }
63             }
64              
65             # Override DBIx::SearchBuilder::Create to do some checking on create
66             sub Create {
67             my $self = shift;
68             my %fields = (
69             UserId => undef,
70             Password => 'default', #Set a default password
71             @_
72             );
73              
74             # Make sure a userid is specified
75             unless ( $fields{'UserId'} ) {
76             die "No userid specified.";
77             }
78              
79             # Get DBIx::SearchBuilder::Record->Create to do the real work
80             return (
81             $self->SUPER::Create(
82             UserId => $fields{'UserId'},
83             Password => $fields{'Password'},
84             Created => time
85             )
86             );
87             }
88              
89             =head1 DESCRIPTION
90              
91             DBIx::SearchBuilder::Record is designed to work with DBIx::SearchBuilder.
92              
93              
94             =head2 What is it trying to do.
95              
96             DBIx::SearchBuilder::Record abstracts the agony of writing the common and generally
97             simple SQL statements needed to serialize and De-serialize an object to the
98             database. In a traditional system, you would define various methods on
99             your object 'create', 'find', 'modify', and 'delete' being the most common.
100             In each method you would have a SQL statement like:
101              
102             select * from table where value='blah';
103              
104             If you wanted to control what data a user could modify, you would have to
105             do some special magic to make accessors do the right thing. Etc. The
106             problem with this approach is that in a majority of the cases, the SQL is
107             incredibly simple and the code from one method/object to the next was
108             basically the same.
109              
110            
111              
112             Enter, DBIx::SearchBuilder::Record.
113              
114             With Record, you can in the simple case, remove all of that code and
115             replace it by defining two methods and inheriting some code. It's pretty
116             simple, and incredibly powerful. For more complex cases, you can
117             do more complicated things by overriding certain methods. Let's stick with
118             the simple case for now.
119              
120             The two methods in question are L and L. All they
121             really do are define some values and send you on your way. As you might
122             have guessed the '_' means that these are private methods.
123             They will get called by your record object's constructor.
124              
125             =over 4
126              
127             =item '_Init'
128              
129             Defines what table we are talking about, and set a variable to store
130             the database handle.
131              
132             =item '_ClassAccessible
133              
134             Defines what operations may be performed on various data selected
135             from the database. For example you can define fields to be mutable,
136             or immutable, there are a few other options but I don't understand
137             what they do at this time.
138              
139             =back
140              
141             And really, that's it. So let's have some sample code.
142              
143             =head2 An Annotated Example
144              
145             The example code below makes the following assumptions:
146              
147             =over 4
148              
149             =item *
150              
151             The database is 'postgres',
152              
153             =item *
154              
155             The host is 'reason',
156              
157             =item *
158              
159             The login name is 'mhat',
160              
161             =item *
162              
163             The database is called 'example',
164              
165             =item *
166              
167             The table is called 'simple',
168              
169             =item *
170              
171             The table looks like so:
172              
173             id integer not NULL, primary_key(id),
174             foo varchar(10),
175             bar varchar(10)
176              
177             =back
178              
179             First, let's define our record class in a new module named "Simple.pm".
180              
181             000: package Simple;
182             001: use DBIx::SearchBuilder::Record;
183             002: @ISA = (DBIx::SearchBuilder::Record);
184              
185             This should be pretty obvious, name the package, import ::Record and then
186             define ourself as a subclass of ::Record.
187              
188             003:
189             004: sub _Init {
190             005: my $this = shift;
191             006: my $handle = shift;
192             007:
193             008: $this->_Handle($handle);
194             009: $this->Table("Simple");
195             010:
196             011: return ($this);
197             012: }
198              
199             Here we set our handle and table name. While it's not obvious so far, we'll
200             see later that $handle (line: 006) gets passed via C<::Record::new> when a
201             new instance is created. That's actually an important concept: the DB handle
202             is not bound to a single object but rather, it is shared across objects.
203              
204             013:
205             014: sub _ClassAccessible {
206             015: {
207             016: Foo => { 'read' => 1 },
208             017: Bar => { 'read' => 1, 'write' => 1 },
209             018: Id => { 'read' => 1 }
210             019: };
211             020: }
212              
213             What's happening might be obvious, but just in case this method is going to
214             return a reference to a hash. That hash is where our columns are defined,
215             as well as what type of operations are acceptable.
216              
217             021:
218             022: 1;
219              
220             Like all perl modules, this needs to end with a true value.
221              
222             Now, on to the code that will actually *do* something with this object.
223             This code would be placed in your Perl script.
224              
225             000: use DBIx::SearchBuilder::Handle;
226             001: use Simple;
227              
228             Use two packages, the first is where I get the DB handle from, the latter
229             is the object I just created.
230              
231             002:
232             003: my $handle = DBIx::SearchBuilder::Handle->new();
233             004: $handle->Connect( 'Driver' => 'Pg',
234             005: 'Database' => 'test',
235             006: 'Host' => 'reason',
236             007: 'User' => 'mhat',
237             008: 'Password' => '');
238              
239             Creates a new DBIx::SearchBuilder::Handle, and then connects to the database using
240             that handle. Pretty straight forward, the password '' is what I use
241             when there is no password. I could probably leave it blank, but I find
242             it to be more clear to define it.
243              
244             009:
245             010: my $s = Simple->new($handle);
246             011:
247             012: $s->LoadById(1);
248              
249             LoadById is one of four 'LoadBy' methods, as the name suggests it searches
250             for an row in the database that has id='0'. ::SearchBuilder has, what I
251             think is a bug, in that it current requires there to be an id field. More
252             reasonably it also assumes that the id field is unique. LoadById($id) will
253             do undefined things if there is >1 row with the same id.
254              
255             In addition to LoadById, we also have:
256              
257             =over 4
258              
259             =item LoadByCol
260              
261             Takes two arguments, a column name and a value. Again, it will do
262             undefined things if you use non-unique things.
263              
264             =item LoadByCols
265              
266             Takes a hash of columns=>values and returns the *first* to match.
267             First is probably lossy across databases vendors.
268              
269             =item LoadFromHash
270              
271             Populates this record with data from a DBIx::SearchBuilder. I'm
272             currently assuming that DBIx::SearchBuilder is what we use in
273             cases where we expect > 1 record. More on this later.
274              
275             =back
276              
277             Now that we have a populated object, we should do something with it! ::Record
278             automagically generates accessos and mutators for us, so all we need to do
279             is call the methods. Accessors are named (), and Mutators are named
280             Set($). On to the example, just appending this to the code from
281             the last example.
282              
283             013:
284             014: print "ID : ", $s->Id(), "\n";
285             015: print "Foo : ", $s->Foo(), "\n";
286             016: print "Bar : ", $s->Bar(), "\n";
287              
288             That's all you have to to get the data. Now to change the data!
289              
290             017:
291             018: $s->SetBar('NewBar');
292              
293             Pretty simple! That's really all there is to it. Set($) returns
294             a boolean and a string describing the problem. Let's look at an example of
295             what will happen if we try to set a 'Id' which we previously defined as
296             read only.
297              
298             019: my ($res, $str) = $s->SetId('2');
299             020: if (! $res) {
300             021: ## Print the error!
301             022: print "$str\n";
302             023: }
303              
304             The output will be:
305              
306             >> Immutable field
307              
308             Currently Set updates the data in the database as soon as you call
309             it. In the future I hope to extend ::Record to better support transactional
310             operations, such that updates will only happen when "you" say so.
311              
312             Finally, adding a removing records from the database. ::Record provides a
313             Create method which simply takes a hash of key=>value pairs. The keys
314             exactly map to database fields.
315              
316             023: ## Get a new record object.
317             024: $s1 = Simple->new($handle);
318             025: $s1->Create('Id' => 4,
319             026: 'Foo' => 'Foooooo',
320             027: 'Bar' => 'Barrrrr');
321              
322             Poof! A new row in the database has been created! Now let's delete the
323             object!
324              
325             028:
326             029: $s1 = undef;
327             030: $s1 = Simple->new($handle);
328             031: $s1->LoadById(4);
329             032: $s1->Delete();
330              
331             And it's gone.
332              
333             For simple use, that's more or less all there is to it. In the future, we
334             hope to expand this how-to to discuss using container classes, overloading,
335             etc.
336              
337             =head1 METHOD NAMING
338              
339             Each method has a lower case alias; '_' is used to separate words.
340             For example, the method C<_PrimaryKeys> has the alias C<_primary_keys>.
341              
342             =head1 METHODS
343              
344             =cut
345              
346              
347              
348             =head2 new
349              
350             Instantiate a new record object.
351              
352             =cut
353              
354              
355             sub new {
356 598     598 1 27651 my $proto = shift;
357              
358 598   33     2099 my $class = ref($proto) || $proto;
359 598         1166 my $self = {};
360 598         1196 bless ($self, $class);
361 598         2069 $self->_Init(@_);
362              
363 598         1494 return $self;
364             }
365              
366              
367             # Not yet documented here. Should almost certainly be overloaded.
368             sub _Init {
369 34     34   75 my $self = shift;
370 34         61 my $handle = shift;
371 34         120 $self->_Handle($handle);
372             }
373              
374              
375             =head2 id
376              
377             Returns this row's primary key.
378              
379             =cut
380              
381              
382              
383             *id = \&Id;
384              
385             sub Id {
386 472     472 0 14864 my $pkey = $_[0]->_PrimaryKey();
387 472         1455 return $_[0]->{'values'}->{ $pkey };
388             }
389              
390              
391             =head2 primary_keys
392              
393             =head2 PrimaryKeys
394              
395             Return a hash of the values of our primary keys for this function.
396              
397             =cut
398              
399              
400              
401              
402             sub PrimaryKeys {
403 84     84 1 182 my $self = shift;
404 84         155 return map { $_ => $self->{'values'}->{lc $_} } @{$self->_PrimaryKeys};
  84         523  
  84         236  
405             }
406              
407              
408              
409              
410             sub DESTROY {
411 598     598   38352 return 1;
412             }
413              
414              
415             sub AUTOLOAD {
416 40     40   2203 my $self = $_[0];
417              
418 20     20   180 no strict 'refs';
  20         55  
  20         55701  
419 40         409 my ($Attrib) = ( $AUTOLOAD =~ /::(\w+)$/o );
420              
421 40 100       186 if ( $self->_Accessible( $Attrib, 'read' ) ) {
    100          
    100          
    100          
    100          
    100          
422 20     521   181 *{$AUTOLOAD} = sub { return ( $_[0]->_Value($Attrib) ) };
  20         127  
  521         22608  
423 20         120 goto &$AUTOLOAD;
424             }
425             elsif ( $self->_Accessible( $Attrib, 'record-read') ) {
426 1     3   16 *{$AUTOLOAD} = sub { $_[0]->_ToRecord( $Attrib, $_[0]->__Value($Attrib) ) };
  1         5  
  3         123  
427 1         7 goto &$AUTOLOAD;
428             }
429             elsif ( $self->_Accessible( $Attrib, 'foreign-collection') ) {
430 1     2   11 *{$AUTOLOAD} = sub { $_[0]->_CollectionValue( $Attrib ) };
  1         7  
  2         15  
431 1         6 goto &$AUTOLOAD;
432             }
433             elsif ( $AUTOLOAD =~ /.*::[sS]et_?(\w+)/o ) {
434 9         40 $Attrib = $1;
435              
436 9 100       30 if ( $self->_Accessible( $Attrib, 'write' ) ) {
    100          
    100          
437 6         34 *{$AUTOLOAD} = sub {
438 23     23   1271 return ( $_[0]->_Set( Field => $Attrib, Value => $_[1] ) );
439 6         57 };
440 6         31 goto &$AUTOLOAD;
441             } elsif ( $self->_Accessible( $Attrib, 'record-write') ) {
442 1         5 *{$AUTOLOAD} = sub {
443 2     2   580 my $self = shift;
444 2         5 my $val = shift;
445              
446 2 100       20 $val = $val->id if UNIVERSAL::isa($val, 'DBIx::SearchBuilder::Record');
447 2         25 return ( $self->_Set( Field => $Attrib, Value => $val ) );
448 1         7 };
449 1         7 goto &$AUTOLOAD;
450             }
451             elsif ( $self->_Accessible( $Attrib, 'read' ) ) {
452 1     1   5 *{$AUTOLOAD} = sub { return ( 0, 'Immutable field' ) };
  1         6  
  1         5  
453 1         6 goto &$AUTOLOAD;
454             }
455             else {
456 1         5 return ( 0, 'Nonexistant field?' );
457             }
458             }
459             elsif ( $AUTOLOAD =~ /.*::(\w+?)_?[oO]bj$/o ) {
460 2         8 $Attrib = $1;
461 2 100       7 if ( $self->_Accessible( $Attrib, 'object' ) ) {
462 1         6 *{$AUTOLOAD} = sub {
463 1     1   9 return (shift)->_Object(
464             Field => $Attrib,
465             Args => [@_],
466             );
467 1         8 };
468 1         7 goto &$AUTOLOAD;
469             }
470             else {
471 1         5 return ( 0, 'No object mapping for field' );
472             }
473             }
474              
475             #Previously, I checked for writability here. but I'm not sure that's the
476             #right idea. it breaks the ability to do ValidateQueue for a ticket
477             #on creation.
478              
479             elsif ( $AUTOLOAD =~ /.*::[vV]alidate_?(\w+)/o ) {
480 6         22 $Attrib = $1;
481              
482 6     15   32 *{$AUTOLOAD} = sub { return ( $_[0]->_Validate( $Attrib, $_[1] ) ) };
  6         25  
  15         74  
483 6         27 goto &$AUTOLOAD;
484             }
485              
486             # TODO: if autoload = 0 or 1 _ then a combination of lowercase and _ chars,
487             # turn them into studlycapped phrases
488              
489             else {
490 1         3 my ( $package, $filename, $line );
491 1         5 ( $package, $filename, $line ) = caller;
492              
493 1         13 die "$AUTOLOAD Unimplemented in $package. ($filename line $line) \n";
494             }
495              
496             }
497              
498              
499              
500             =head2 _Accessible KEY MODE
501              
502             Private method.
503              
504             Returns undef unless C is accessible in C otherwise returns C value
505              
506             =cut
507              
508              
509             sub _Accessible {
510 1074     1074   1657 my $self = shift;
511 1074         1679 my $attr = shift;
512 1074   100     2611 my $mode = lc(shift || '');
513              
514 1074         2579 my $attribute = $self->_ClassAccessible(@_)->{$attr};
515 1074 100       10728 return unless defined $attribute;
516 316         1222 return $attribute->{$mode};
517             }
518              
519              
520              
521             =head2 _PrimaryKeys
522              
523             Return our primary keys. (Subclasses should override this, but our default is that we have one primary key, named 'id'.)
524              
525             =cut
526              
527             sub _PrimaryKeys {
528 666     666   1088 my $self = shift;
529 666         1693 return ['id'];
530             }
531              
532              
533             sub _PrimaryKey {
534 526     526   934 my $self = shift;
535 526         1151 my $pkeys = $self->_PrimaryKeys();
536 526 50 33     2361 die "No primary key" unless ( ref($pkeys) eq 'ARRAY' and $pkeys->[0] );
537 526 50       1253 die "Too many primary keys" unless ( scalar(@$pkeys) == 1 );
538 526         1457 return $pkeys->[0];
539             }
540              
541              
542             =head2 _ClassAccessible
543              
544             An older way to specify fields attributes in a derived class.
545             (The current preferred method is by overriding C; if you do
546             this and don't override C<_ClassAccessible>, the module will generate
547             an appropriate C<_ClassAccessible> based on your C.)
548              
549             Here's an example declaration:
550              
551             sub _ClassAccessible {
552             {
553             Tofu => { 'read'=>1, 'write'=>1 },
554             Maz => { 'auto'=>1, },
555             Roo => { 'read'=>1, 'auto'=>1, 'public'=>1, },
556             };
557             }
558              
559             =cut
560              
561              
562             sub _ClassAccessible {
563 53     53   110 my $self = shift;
564              
565 53 50       291 return $self->_ClassAccessibleFromSchema if $self->can('Schema');
566              
567             # XXX This is stub code to deal with the old way we used to do _Accessible
568             # It should never be called by modern code
569              
570 0         0 my %accessible;
571 0         0 while ( my $col = shift ) {
572             $accessible{$col}->{lc($_)} = 1
573 0         0 foreach split(/[\/,]/, shift);
574             }
575 0         0 return(\%accessible);
576             }
577              
578             sub _ClassAccessibleFromSchema {
579 53     53   106 my $self = shift;
580              
581 53         112 my $accessible = {};
582 53         160 foreach my $key ($self->_PrimaryKeys) {
583 53         266 $accessible->{$key} = { 'read' => 1 };
584             };
585              
586 53         198 my $schema = $self->Schema;
587              
588 53         451 for my $field (keys %$schema) {
589 104 100       310 if ($schema->{$field}{'TYPE'}) {
    50          
590 53         159 $accessible->{$field} = { 'read' => 1, 'write' => 1 };
591             } elsif (my $refclass = $schema->{$field}{'REFERENCES'}) {
592 51 100       222 if (UNIVERSAL::isa($refclass, 'DBIx::SearchBuilder::Record')) {
    50          
593 40 50       121 if ($field =~ /(.*)_id$/) {
594 0         0 $accessible->{$field} = { 'read' => 1, 'write' => 1 };
595 0         0 $accessible->{$1} = { 'record-read' => 1, 'column' => $field };
596             } else {
597 40         132 $accessible->{$field} = { 'record-read' => 1, 'record-write' => 1 };
598             }
599             } elsif (UNIVERSAL::isa($refclass, 'DBIx::SearchBuilder')) {
600 11         54 $accessible->{$field} = { 'foreign-collection' => 1 };
601             } else {
602 0         0 warn "Error: $refclass neither Record nor Collection";
603             }
604             }
605             }
606              
607 53         236 return $accessible;
608             }
609              
610              
611             sub _ToRecord {
612 3     3   9 my $self = shift;
613 3         9 my $field = shift;
614 3         8 my $value = shift;
615              
616 3 50       11 return unless defined $value;
617              
618 3         11 my $schema = $self->Schema;
619 3   33     39 my $description = $schema->{$field} || $schema->{$field . "_id"};
620              
621 3 50       9 die "Can't get schema for $field on $self" unless $description;
622              
623 3 50       8 return unless $description;
624              
625 3 50       7 return $value unless $description->{'REFERENCES'};
626              
627 3         8 my $classname = $description->{'REFERENCES'};
628              
629 3 50       12 return unless UNIVERSAL::isa($classname, 'DBIx::SearchBuilder::Record');
630              
631             # XXX TODO FIXME perhaps this is not what should be passed to new, but it needs it
632 3         9 my $object = $classname->new( $self->_Handle );
633 3         35 $object->LoadById( $value );
634 3         25 return $object;
635             }
636              
637             sub _CollectionValue {
638 2     2   6 my $self = shift;
639              
640 2         5 my $method_name = shift;
641 2 50       9 return unless defined $method_name;
642              
643 2         9 my $schema = $self->Schema;
644 2         18 my $description = $schema->{$method_name};
645 2 50       12 return unless $description;
646              
647 2         7 my $classname = $description->{'REFERENCES'};
648              
649 2 50       13 return unless UNIVERSAL::isa($classname, 'DBIx::SearchBuilder');
650              
651 2         15 my $coll = $classname->new( Handle => $self->_Handle );
652              
653 2         12 $coll->Limit( FIELD => $description->{'KEY'}, VALUE => $self->id);
654              
655 2         12 return $coll;
656             }
657              
658             # sub {{{ ReadableAttributes
659              
660             =head2 ReadableAttributes
661              
662             Returns an array of the attributes of this class defined as "read" => 1 in this class' _ClassAccessible datastructure
663              
664             =cut
665              
666             sub ReadableAttributes {
667 1     1 1 6 my $self = shift;
668 1         4 my $ca = $self->_ClassAccessible();
669 1 50       13 my @readable = grep { $ca->{$_}->{'read'} or $ca->{$_}->{'record-read'} } sort keys %{$ca};
  4         16  
  1         11  
670 1         12 return (@readable);
671             }
672              
673              
674              
675             =head2 WritableAttributes
676              
677             Returns an array of the attributes of this class defined as "write" => 1 in this class' _ClassAccessible datastructure
678              
679             =cut
680              
681             sub WritableAttributes {
682 1     1 1 3 my $self = shift;
683 1         4 my $ca = $self->_ClassAccessible();
684 1 100       12 my @writable = grep { $ca->{$_}->{'write'} || $ca->{$_}->{'record-write'} } sort keys %{$ca};
  4         16  
  1         7  
685 1         16 return @writable;
686             }
687              
688              
689              
690              
691             =head2 __Value
692              
693             Takes a field name and returns that field's value. Subclasses should never
694             override __Value.
695              
696             =cut
697              
698              
699             sub __Value {
700 699     699   1328 my $self = shift;
701 699         1191 my $field = lc shift;
702              
703 699   33     1369 $field = $self->_Accessible($field, "column") || $field;
704              
705 699 100       3208 return $self->{'values'}{$field} if $self->{'fetched'}{$field};
706 3         7 $self->{'fetched'}{$field} = 1;
707              
708 3         12 my %pk = $self->PrimaryKeys;
709 3 50       18 return undef if grep !defined, values %pk;
710              
711 3         11 my $query = "SELECT $field FROM ". $self->QuotedTableName
712             ." WHERE ". join " AND ", map "$_ = ?", sort keys %pk;
713 3 100       12 my $sth = $self->_Handle->SimpleQuery( $query, sorted_values(%pk) ) or return undef;
714 2         111 return $self->{'values'}{$field} = ($sth->fetchrow_array)[0];
715             }
716              
717             =head2 _Value
718              
719             _Value takes a single column name and returns that column's value for this row.
720             Subclasses can override _Value to insert custom access control.
721              
722             =cut
723              
724              
725             sub _Value {
726 529     529   852 my $self = shift;
727 529         1036 return ($self->__Value(@_));
728             }
729              
730              
731              
732             =head2 _Set
733              
734             _Set takes a single column name and a single unquoted value.
735             It updates both the in-memory value of this column and the in-database copy.
736             Subclasses can override _Set to insert custom access control.
737              
738             =cut
739              
740              
741             sub _Set {
742 26     26   899 my $self = shift;
743 26         86 return ($self->__Set(@_));
744             }
745              
746              
747              
748              
749             sub __Set {
750 26     26   48 my $self = shift;
751              
752 26         127 my %args = (
753             'Field' => undef,
754             'Value' => undef,
755             'IsSQL' => undef,
756             @_
757             );
758              
759 26         89 $args{'Column'} = delete $args{'Field'};
760 26         60 $args{'IsSQLFunction'} = delete $args{'IsSQL'};
761              
762 26         128 my $ret = Class::ReturnValue->new();
763              
764 26 100       205 unless ( $args{'Column'} ) {
765 1         4 $ret->as_array( 0, 'No column specified' );
766 1         17 $ret->as_error(
767             errno => 5,
768             do_backtrace => 0,
769             message => "No column specified"
770             );
771 1         17 return ( $ret->return_value );
772             }
773 25         70 my $column = lc $args{'Column'};
774              
775             # XXX: OLD behaviour, no_undefs_in_set will go away
776 25 50 66     107 if ( !defined $args{'Value'} && $self->{'no_undefs_in_set' } ) {
777 0         0 $ret->as_array( 0, "No value passed to _Set" );
778 0         0 $ret->as_error(
779             errno => 2,
780             do_backtrace => 0,
781             message => "No value passed to _Set"
782             );
783 0         0 return ( $ret->return_value );
784             }
785              
786 25 100       71 if ( defined $args{'Value'} ) {
787 17 100 66     77 if ( $args{'Value'} eq '' &&
      100        
788             ( $self->_Accessible( $args{'Column'}, 'is_numeric' )
789             || ($self->_Accessible( $args{'Column'}, 'type' ) || '') =~ /INT/i ) )
790             {
791 3         11 $args{'Value'} = 0;
792             }
793             }
794             else {
795 8 100       29 if ( $self->_Accessible( $args{Column}, 'no_nulls' ) ) {
796 4         21 my $default = $self->_Accessible( $args{Column}, 'default' );
797              
798 4 100       15 if ( defined $default ) {
799 2         7 $args{'Value'} = $default;
800             }
801             else {
802 2         15 $ret->as_array( 0, 'Illegal value for non-nullable field ' . $args{'Column'} . ": undef/null value provided and no default specified by class" );
803             $ret->as_error(
804             errno => 3,
805             do_backtrace => 0,
806 2         40 message => "Illegal value for non-nullable field " . $args{'Column'} . ": undef/null value provided and no default specified by class"
807             );
808 2         41 return ( $ret->return_value );
809             }
810             }
811             }
812              
813             # First, we truncate the value, if we need to.
814 23         91 $args{'Value'} = $self->TruncateValue( $args{'Column'}, $args{'Value'} );
815              
816 23         227 my $current_value = $self->__Value($column);
817              
818 23 100 100     226 if (
      100        
      100        
      100        
819             ( !defined $args{'Value'} && !defined $current_value )
820             || ( defined $args{'Value'}
821             && defined $current_value
822             && ( $args{'Value'} eq $current_value ) )
823             )
824             {
825 3         17 $ret->as_array( 0, "That is already the current value" );
826 3         50 $ret->as_error(
827             errno => 1,
828             do_backtrace => 0,
829             message => "That is already the current value"
830             );
831 3         57 return ( $ret->return_value );
832             }
833              
834 20         57 my $method = "Validate" . $args{'Column'};
835 20 100       104 unless ( $self->$method( $args{'Value'} ) ) {
836 1         18 $ret->as_array( 0, 'Illegal value for ' . $args{'Column'} );
837             $ret->as_error(
838             errno => 3,
839             do_backtrace => 0,
840 1         17 message => "Illegal value for " . $args{'Column'}
841             );
842 1         16 return ( $ret->return_value );
843             }
844              
845 19         86 $args{'Table'} = $self->Table();
846 19         71 $args{'PrimaryKeys'} = { $self->PrimaryKeys() };
847              
848             # The blob handling will destroy $args{'Value'}. But we assign
849             # that back to the object at the end. this works around that
850 19         46 my $unmunged_value = $args{'Value'};
851              
852 19 50       56 unless ( $self->_Handle->KnowsBLOBs ) {
853             # Support for databases which don't deal with LOBs automatically
854 0         0 my $ca = $self->_ClassAccessible();
855 0         0 my $key = $args{'Column'};
856 0 0       0 if ( $ca->{$key}->{'type'} =~ /^(text|longtext|clob|blob|lob)$/i ) {
857 0         0 my $bhash = $self->_Handle->BLOBParams( $key, $ca->{$key}->{'type'} );
858 0         0 $bhash->{'value'} = $args{'Value'};
859 0         0 $args{'Value'} = $bhash;
860             }
861             }
862              
863              
864 19         48 my $val = $self->_Handle->UpdateRecordValue(%args);
865 19 50       113 unless ($val) {
866             my $message =
867             $args{'Column'}
868             . " could not be set to "
869 0 0       0 . ( defined $args{'Value'} ? $args{'Value'} : 'undef' ) . ".";
870 0         0 $ret->as_array( 0, $message);
871 0         0 $ret->as_error(
872             errno => 4,
873             do_backtrace => 0,
874             message => $message
875             );
876 0         0 return ( $ret->return_value );
877             }
878             # If we've performed some sort of "functional update"
879             # then we need to reload the object from the DB to know what's
880             # really going on. (ex SET Cost = Cost+5)
881 19 50       79 if ( $args{'IsSQLFunction'} ) {
882 0         0 $self->Load( $self->Id );
883             }
884             else {
885 19         95 $self->{'values'}->{"$column"} = $unmunged_value;
886             }
887 19         124 $ret->as_array( 1, "The new value has been set." );
888 19         634 return ( $ret->return_value );
889             }
890              
891             =head2 _Canonicalize PARAMHASH
892              
893             This routine massages an input value (VALUE) for FIELD into something that's
894             going to be acceptable.
895              
896             Takes
897              
898             =over
899              
900             =item FIELD
901              
902             =item VALUE
903              
904             =item FUNCTION
905              
906             =back
907              
908              
909             Takes:
910              
911             =over
912              
913             =item FIELD
914              
915             =item VALUE
916              
917             =item FUNCTION
918              
919             =back
920              
921             Returns a replacement VALUE.
922              
923             =cut
924              
925             sub _Canonicalize {
926 0     0   0 my $self = shift;
927 0         0 my $field = shift;
928              
929              
930              
931             }
932              
933              
934             =head2 _Validate FIELD VALUE
935              
936             Validate that VALUE will be an acceptable value for FIELD.
937              
938             Currently, this routine does nothing whatsoever.
939              
940             If it succeeds (which is always the case right now), returns true. Otherwise returns false.
941              
942             =cut
943              
944              
945              
946              
947             sub _Validate {
948 15     15   32 my $self = shift;
949 15         32 my $field = shift;
950 15         27 my $value = shift;
951              
952             #Check type of input
953             #If it's null, are nulls permitted?
954             #If it's an int, check the # of bits
955             #If it's a string,
956             #check length
957             #check for nonprintables
958             #If it's a blob, check for length
959             #In an ideal world, if this is a link to another table, check the dependency.
960 15         47 return(1);
961             }
962              
963              
964              
965             =head2 TruncateValue KEY VALUE
966              
967             Truncate a value that's about to be set so that it will fit inside the database'
968             s idea of how big the column is.
969              
970             (Actually, it looks at SearchBuilder's concept of the database, not directly into the db).
971              
972             =cut
973              
974             sub TruncateValue {
975 263     263 1 554 my $self = shift;
976 263         466 my $key = shift;
977 263         454 my $value = shift;
978              
979             # We don't need to truncate empty things.
980 263 100       696 return undef unless defined $value;
981              
982 247         670 my $metadata = $self->_ClassAccessible->{$key};
983 247 100       2031 return $value unless $metadata;
984              
985 246         424 my $truncate_to;
986 246 100 66     2385 if ( $metadata->{'length'} && !$metadata->{'is_numeric'} ) {
    100 100        
987 3         12 $truncate_to = int $metadata->{'length'};
988             }
989             elsif ($metadata->{'type'} && $metadata->{'type'} =~ /char\((\d+)\)/ ) {
990 130         504 $truncate_to = $1;
991             }
992 246 100       828 return $value unless $truncate_to;
993              
994             # return asap if length in bytes is smaller than limit
995 20 100   20   199 return $value if $truncate_to >= do { use bytes; length $value };
  20         53  
  20         143  
  133         259  
  133         889  
996              
997 5 50       21 if ( Encode::is_utf8($value) ) {
998 0         0 return Encode::decode_utf8(
999             substr( Encode::encode_utf8( $value ), 0, $truncate_to ),
1000             Encode::FB_QUIET(),
1001             );
1002             }
1003             else {
1004             # XXX: if it's not UTF-8 then why do we convert it to?
1005 5         26 return Encode::encode_utf8( Encode::decode_utf8 (
1006             substr( $value, 0, $truncate_to ), Encode::FB_QUIET(),
1007             ) );
1008             }
1009             }
1010              
1011              
1012             =head2 _Object
1013              
1014             _Object takes a single column name and an array reference.
1015             It creates new object instance of class specified in _ClassAccessable
1016             structure and calls LoadById on recently created object with the
1017             current column value as argument. It uses the array reference as
1018             the object constructor's arguments.
1019             Subclasses can override _Object to insert custom access control or
1020             define default constructor arguments.
1021              
1022             Note that if you are using a C with a C field,
1023             this is unnecessary: the method to access the column's value will
1024             automatically turn it into the appropriate object.
1025              
1026             =cut
1027              
1028             sub _Object {
1029 1     1   3 my $self = shift;
1030 1         8 return $self->__Object(@_);
1031             }
1032              
1033             sub __Object {
1034 1     1   4 my $self = shift;
1035 1         7 my %args = ( Field => '', Args => [], @_ );
1036              
1037 1         17 my $field = $args{'Field'};
1038 1         7 my $class = $self->_Accessible( $field, 'object' );
1039              
1040             # Globs magic to be sure that we call 'eval "require $class"' only once
1041             # because eval is quite slow -- cubic@acronis.ru
1042 20     20   4498 no strict qw( refs );
  20         51  
  20         36057  
1043 1         3 my $vglob = ${ $class . '::' }{'VERSION'};
  1         10  
1044 1 50 50     18 unless ( $vglob && *$vglob{'SCALAR'} ) {
1045 0         0 eval "require $class";
1046 0 0       0 die "Couldn't use $class: $@" if ($@);
1047 0 0 0     0 unless ( $vglob && *$vglob{'SCALAR'} ) {
1048 0         0 *{ $class . "::VERSION" } = '-1, By DBIx::SearchBuilder';
  0         0  
1049             }
1050             }
1051              
1052 1         5 my $object = $class->new( @{ $args{'Args'} } );
  1         7  
1053 1         8 $object->LoadById( $self->__Value($field) );
1054 1         8 return $object;
1055             }
1056              
1057              
1058              
1059              
1060             # load should do a bit of overloading
1061             # if we call it with only one argument, we're trying to load by reference.
1062             # if we call it with a passel of arguments, we're trying to load by value
1063             # The latter is primarily important when we've got a whole set of record that we're
1064             # reading in with a recordset class and want to instantiate objefcts for each record.
1065              
1066             =head2 Load
1067              
1068             Takes a single argument, $id. Calls LoadById to retrieve the row whose primary key
1069             is $id
1070              
1071             =cut
1072              
1073              
1074              
1075             sub Load {
1076 48     48 1 19467 my $self = shift;
1077 48         229 return $self->LoadById(@_);
1078             }
1079              
1080              
1081             =head2 LoadByCol
1082              
1083             Takes two arguments, a column and a value. The column can be any table column
1084             which contains unique values. Behavior when using a non-unique value is
1085             undefined
1086              
1087             =cut
1088              
1089             sub LoadByCol {
1090 2     2 1 18 my $self = shift;
1091 2         5 return $self->LoadByCols(@_);
1092             }
1093              
1094              
1095              
1096             =head2 LoadByCols
1097              
1098             Takes a hash of columns and values. Loads the first record that matches all
1099             keys.
1100              
1101             The hash's keys are the columns to look at.
1102              
1103             The hash's values are either: scalar values to look for
1104             OR has references which contain 'operator' and 'value'
1105              
1106             =cut
1107              
1108              
1109             sub LoadByCols {
1110 58     58 1 179 my $self = shift;
1111 58         209 my %hash = (@_);
1112 58         119 my (@bind, @phrases);
1113 58         259 foreach my $key (sort keys %hash) {
1114 60 100 100     313 if (defined $hash{$key} && $hash{$key} ne '') {
1115 58         139 my $op;
1116             my $value;
1117 58         131 my $function = "?";
1118 58 100       161 if (ref $hash{$key} eq 'HASH') {
1119 1         4 $op = $hash{$key}->{operator};
1120 1         3 $value = $hash{$key}->{value};
1121 1   50     7 $function = $hash{$key}->{function} || "?";
1122             } else {
1123 57         115 $op = '=';
1124 57         113 $value = $hash{$key};
1125             }
1126              
1127 58         214 push @phrases, "$key $op $function";
1128 58         180 push @bind, $value;
1129             }
1130             else {
1131 2         7 push @phrases, "($key IS NULL OR $key = ?)";
1132 2         7 my $meta = $self->_ClassAccessible->{$key};
1133 2   50     26 $meta->{'type'} ||= '';
1134             # TODO: type checking should be done in generic way
1135 2 100 66     24 if ( $meta->{'is_numeric'} || $meta->{'type'} =~ /INT|NUMERIC|DECIMAL|REAL|DOUBLE|FLOAT/i ) {
1136 1         4 push @bind, 0;
1137             } else {
1138 1         3 push @bind, '';
1139             }
1140             }
1141             }
1142              
1143 58         276 my $QueryString = "SELECT * FROM ".$self->QuotedTableName." WHERE ".
1144             join(' AND ', @phrases) ;
1145 58         216 return ($self->_LoadFromSQL($QueryString, @bind));
1146             }
1147              
1148              
1149              
1150              
1151             =head2 LoadById
1152              
1153             Loads a record by its primary key. Your record class must define a single primary key column.
1154              
1155             =cut
1156              
1157              
1158             sub LoadById {
1159 54     54 1 201 my ($self, $id) = @_;
1160 54 100       245 return $self->LoadByCols( $self->_PrimaryKey, defined $id? $id: 0 );
1161             }
1162              
1163              
1164              
1165              
1166             =head2 LoadByPrimaryKeys
1167              
1168             Like LoadById with basic support for compound primary keys.
1169              
1170             =cut
1171              
1172              
1173              
1174             sub LoadByPrimaryKeys {
1175 3     3 1 22 my $self = shift;
1176 3 100       13 my $data = (ref $_[0] eq 'HASH')? $_[0]: {@_};
1177              
1178 3         8 my %cols=();
1179 3         5 foreach (@{$self->_PrimaryKeys}) {
  3         9  
1180 3 100       15 return (0, "Missing PK field: '$_'") unless defined $data->{$_};
1181 2         6 $cols{$_}=$data->{$_};
1182             }
1183 2         7 return ($self->LoadByCols(%cols));
1184             }
1185              
1186              
1187              
1188              
1189             =head2 LoadFromHash
1190              
1191             Takes a hashref, such as created by DBIx::SearchBuilder and populates this record's
1192             loaded values hash.
1193              
1194             =cut
1195              
1196              
1197              
1198             sub LoadFromHash {
1199 410     410 1 627 my $self = shift;
1200 410         602 my $hashref = shift;
1201              
1202 410         1362 foreach my $f ( keys %$hashref ) {
1203 930         2274 $self->{'fetched'}{lc $f} = 1;
1204             }
1205              
1206 410         834 $self->{'values'} = $hashref;
1207 410         921 return $self->id();
1208             }
1209              
1210              
1211              
1212             =head2 _LoadFromSQL QUERYSTRING @BIND_VALUES
1213              
1214             Load a record as the result of an SQL statement
1215              
1216             =cut
1217              
1218              
1219              
1220              
1221             sub _LoadFromSQL {
1222 62     62   169 my $self = shift;
1223 62         121 my $QueryString = shift;
1224 62         177 my @bind_values = (@_);
1225              
1226 62         161 my $sth = $self->_Handle->SimpleQuery( $QueryString, @bind_values );
1227              
1228             #TODO this only gets the first row. we should check if there are more.
1229              
1230 62 100       264 return ( 0, "Couldn't execute query: ".$self->_Handle->dbh->errstr ) unless $sth;
1231              
1232 61         2740 $self->{'values'} = $sth->fetchrow_hashref;
1233 61         406 $self->{'fetched'} = {};
1234 61 50 66     302 if ( !$self->{'values'} && $sth->err ) {
1235 0         0 return ( 0, "Couldn't fetch row: ". $sth->err );
1236             }
1237              
1238 61 100       182 unless ( $self->{'values'} ) {
1239 3         45 return ( 0, "Couldn't find row" );
1240             }
1241              
1242             ## I guess to be consistant with the old code, make sure the primary
1243             ## keys exist.
1244              
1245 58 100       238 if( grep { not defined } $self->PrimaryKeys ) {
  116         373  
1246 1         64 return ( 0, "Missing a primary key?" );
1247             }
1248              
1249 57         122 foreach my $f ( keys %{$self->{'values'}} ) {
  57         312  
1250 143         392 $self->{'fetched'}{lc $f} = 1;
1251             }
1252 57         1167 return ( 1, "Found Object" );
1253              
1254             }
1255              
1256              
1257              
1258              
1259              
1260             =head2 Create
1261              
1262             Takes an array of key-value pairs and drops any keys that aren't known
1263             as columns for this recordtype
1264              
1265             =cut
1266              
1267              
1268              
1269             sub Create {
1270 152     152 1 5247 my $self = shift;
1271 152         532 my %attribs = @_;
1272              
1273 152         303 my ($key);
1274 152         603 foreach $key ( keys %attribs ) {
1275              
1276 239 100       838 if ( $self->_Accessible( $key, 'record-write' ) ) {
1277             $attribs{$key} = $attribs{$key}->id
1278 3 100       65 if UNIVERSAL::isa( $attribs{$key},
1279             'DBIx::SearchBuilder::Record' );
1280             }
1281              
1282 239 100       738 if ( defined $attribs{$key} ) {
1283 226 50 33     728 if ( $attribs{$key} eq '' &&
      66        
1284             ( $self->_Accessible( $key, 'is_numeric' )
1285             || ($self->_Accessible( $key, 'type' ) || '') =~ /INT/i ) )
1286             {
1287 1         3 $attribs{$key} = 0;
1288             }
1289             }
1290             else {
1291 13 100       55 $attribs{$key} = $self->_Accessible( $key, 'default' )
1292             if $self->_Accessible( $key, 'no_nulls' );
1293             }
1294              
1295             #Truncate things that are too long for their datatypes
1296 239         850 $attribs{$key} = $self->TruncateValue( $key => $attribs{$key} );
1297              
1298             }
1299 152 50       522 unless ( $self->_Handle->KnowsBLOBs ) {
1300              
1301             # Support for databases which don't deal with LOBs automatically
1302 0         0 my $ca = $self->_ClassAccessible();
1303 0         0 foreach $key ( keys %attribs ) {
1304 0         0 my $type = $ca->{$key}->{'type'};
1305 0 0 0     0 next unless $type && $type =~ /^(text|longtext|clob|blob|lob)$/i;
1306              
1307 0         0 my $bhash = $self->_Handle->BLOBParams( $key, $type );
1308 0         0 $bhash->{'value'} = $attribs{$key};
1309 0         0 $attribs{$key} = $bhash;
1310             }
1311             }
1312 152         401 return ( $self->_Handle->Insert( $self->Table, %attribs ) );
1313             }
1314              
1315              
1316             =head2 Delete
1317              
1318             Delete this record from the database. On failure return a Class::ReturnValue with the error. On success, return 1;
1319              
1320             =cut
1321              
1322             *delete = \&Delete;
1323              
1324             sub Delete {
1325 2     2 1 716 $_[0]->__Delete;
1326             }
1327              
1328             sub __Delete {
1329 2     2   10 my $self = shift;
1330              
1331             #TODO Check to make sure the key's not already listed.
1332             #TODO Update internal data structure
1333              
1334             ## Constructs the where clause.
1335 2         10 my @bind=();
1336 2         15 my %pkeys=$self->PrimaryKeys();
1337 2         7 my $where = 'WHERE ';
1338 2         13 foreach my $key (sort keys %pkeys) {
1339 2         13 $where .= $key . "=?" . " AND ";
1340 2         10 push (@bind, $pkeys{$key});
1341             }
1342              
1343 2         19 $where =~ s/AND\s$//;
1344 2         9 my $QueryString = "DELETE FROM ". $self->QuotedTableName . ' ' . $where;
1345 2         10 my $return = $self->_Handle->SimpleQuery($QueryString, @bind);
1346              
1347 2 50       40 if (UNIVERSAL::isa($return, 'Class::ReturnValue')) {
1348 0         0 return ($return);
1349             } else {
1350 2         62 return(1);
1351             }
1352             }
1353              
1354              
1355              
1356              
1357              
1358             =head2 Table
1359              
1360             Returns or sets the name of the current Table
1361              
1362             =cut
1363              
1364              
1365              
1366             sub Table {
1367 778     778 1 3554 my $self = shift;
1368 778 100       1792 if (@_) {
1369 564         1526 $self->{'table'} = shift;
1370             }
1371 778         2085 return ($self->{'table'});
1372             }
1373              
1374             =head2 QuotedTableName
1375              
1376             Returns the name of current Table, or the table provided as an argument, including any quoting
1377             based on yje Handle's QuoteTableNames flag and driver method.
1378              
1379             =cut
1380              
1381             sub QuotedTableName {
1382 63     63 1 165 my ($self, $name) = @_;
1383 63 50       188 unless ($name) {
1384 63 100       235 return $self->{'_quoted_table'} if defined $self->{'_quoted_table'};
1385 58 50       161 $self->{'_quoted_table'}
1386             = $self->_Handle->QuoteTableNames ? $self->_Handle->QuoteName( $self->Table ) : $self->Table;
1387 58         323 return $self->{'_quoted_table'};
1388             }
1389 0 0       0 return $self->_Handle->QuoteTableNames ? $self->_Handle->QuoteName($name) : $name;
1390             }
1391              
1392             =head2 _Handle
1393              
1394             Returns or sets the current DBIx::SearchBuilder::Handle object
1395              
1396             =cut
1397              
1398              
1399             sub _Handle {
1400 1071     1071   2998 my $self = shift;
1401 1071 100       2309 if (@_) {
1402 598         1178 $self->{'DBIxHandle'} = shift;
1403             }
1404 1071         2958 return ($self->{'DBIxHandle'});
1405             }
1406              
1407              
1408             if( eval { require capitalization } ) {
1409             capitalization->unimport( __PACKAGE__ );
1410             }
1411              
1412             1;