File Coverage

blib/lib/DBIx/SearchBuilder/Record.pm
Criterion Covered Total %
statement 343 384 89.3
branch 121 164 73.7
condition 45 81 55.5
subroutine 53 54 98.1
pod 15 16 93.7
total 577 699 82.5


line stmt bran cond sub pod time code
1             package DBIx::SearchBuilder::Record;
2              
3 20     20   3409069 use strict;
  20         45  
  20         955  
4 20     20   111 use warnings;
  20         52  
  20         1332  
5              
6 20     20   144 use vars qw($AUTOLOAD);
  20         43  
  20         1265  
7 20     20   7888 use Class::ReturnValue;
  20         268068  
  20         3045  
8 20     20   9220 use Encode qw();
  20         275254  
  20         999  
9              
10 20     20   10587 use DBIx::SearchBuilder::Util qw/ sorted_values /;
  20         62  
  20         9158  
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 1487     1487 1 33792 my $proto = shift;
357              
358 1487   33     5240 my $class = ref($proto) || $proto;
359 1487         2732 my $self = {};
360 1487         2933 bless ($self, $class);
361 1487         4931 $self->_Init(@_);
362              
363 1487         3649 return $self;
364             }
365              
366              
367             # Not yet documented here. Should almost certainly be overloaded.
368             sub _Init {
369 34     34   57 my $self = shift;
370 34         50 my $handle = shift;
371 34         101 $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 1579     1579 0 18256 my $pkey = $_[0]->_PrimaryKey();
387 1579         4429 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 85     85 1 302 my $self = shift;
404 85         165 return map { $_ => $self->{'values'}->{lc $_} } @{$self->_PrimaryKeys};
  85         670  
  85         374  
405             }
406              
407              
408              
409              
410             sub DESTROY {
411 1487     1487   125140 return 1;
412             }
413              
414              
415             sub AUTOLOAD {
416 42     42   3389 my $self = $_[0];
417              
418 20     20   165 no strict 'refs';
  20         45  
  20         68330  
419 42         413 my ($Attrib) = ( $AUTOLOAD =~ /::(\w+)$/o );
420              
421 42 100       288 if ( $self->_Accessible( $Attrib, 'read' ) ) {
    100          
    100          
    100          
    100          
    100          
422 22     1629   116 *{$AUTOLOAD} = sub { return ( $_[0]->_Value($Attrib) ) };
  22         144  
  1629         33162  
423 22         107 goto &$AUTOLOAD;
424             }
425             elsif ( $self->_Accessible( $Attrib, 'record-read') ) {
426 1     3   5 *{$AUTOLOAD} = sub { $_[0]->_ToRecord( $Attrib, $_[0]->__Value($Attrib) ) };
  1         8  
  3         133  
427 1         6 goto &$AUTOLOAD;
428             }
429             elsif ( $self->_Accessible( $Attrib, 'foreign-collection') ) {
430 1     2   5 *{$AUTOLOAD} = sub { $_[0]->_CollectionValue( $Attrib ) };
  1         7  
  2         16  
431 1         5 goto &$AUTOLOAD;
432             }
433             elsif ( $AUTOLOAD =~ /.*::[sS]et_?(\w+)/o ) {
434 9         33 $Attrib = $1;
435              
436 9 100       29 if ( $self->_Accessible( $Attrib, 'write' ) ) {
    100          
    100          
437 6         37 *{$AUTOLOAD} = sub {
438 23     23   2561 return ( $_[0]->_Set( Field => $Attrib, Value => $_[1] ) );
439 6         33 };
440 6         30 goto &$AUTOLOAD;
441             } elsif ( $self->_Accessible( $Attrib, 'record-write') ) {
442 1         8 *{$AUTOLOAD} = sub {
443 2     2   558 my $self = shift;
444 2         6 my $val = shift;
445              
446 2 100       15 $val = $val->id if UNIVERSAL::isa($val, 'DBIx::SearchBuilder::Record');
447 2         12 return ( $self->_Set( Field => $Attrib, Value => $val ) );
448 1         7 };
449 1         6 goto &$AUTOLOAD;
450             }
451             elsif ( $self->_Accessible( $Attrib, 'read' ) ) {
452 1     1   4 *{$AUTOLOAD} = sub { return ( 0, 'Immutable field' ) };
  1         6  
  1         4  
453 1         4 goto &$AUTOLOAD;
454             }
455             else {
456 1         4 return ( 0, 'Nonexistant field?' );
457             }
458             }
459             elsif ( $AUTOLOAD =~ /.*::(\w+?)_?[oO]bj$/o ) {
460 2         7 $Attrib = $1;
461 2 100       6 if ( $self->_Accessible( $Attrib, 'object' ) ) {
462 1         7 *{$AUTOLOAD} = sub {
463 1     1   10 return (shift)->_Object(
464             Field => $Attrib,
465             Args => [@_],
466             );
467 1         5 };
468 1         5 goto &$AUTOLOAD;
469             }
470             else {
471 1         53 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         23 $Attrib = $1;
481              
482 6     15   88 *{$AUTOLOAD} = sub { return ( $_[0]->_Validate( $Attrib, $_[1] ) ) };
  6         32  
  15         96  
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         2 my ( $package, $filename, $line );
491 1         3 ( $package, $filename, $line ) = caller;
492              
493 1         9 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 2204     2204   3475 my $self = shift;
511 2204         3548 my $attr = shift;
512 2204   100     5281 my $mode = lc(shift || '');
513              
514 2204         5479 my $attribute = $self->_ClassAccessible(@_)->{$attr};
515 2204 100       23066 return unless defined $attribute;
516 338         1432 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 1775     1775   2772 my $self = shift;
529 1775         4427 return ['id'];
530             }
531              
532              
533             sub _PrimaryKey {
534 1634     1634   2691 my $self = shift;
535 1634         3396 my $pkeys = $self->_PrimaryKeys();
536 1634 50 33     7253 die "No primary key" unless ( ref($pkeys) eq 'ARRAY' and $pkeys->[0] );
537 1634 50       3698 die "Too many primary keys" unless ( scalar(@$pkeys) == 1 );
538 1634         3879 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   77 my $self = shift;
564              
565 53 50       317 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   80 my $self = shift;
580              
581 53         85 my $accessible = {};
582 53         136 foreach my $key ($self->_PrimaryKeys) {
583 53         245 $accessible->{$key} = { 'read' => 1 };
584             };
585              
586 53         173 my $schema = $self->Schema;
587              
588 53         460 for my $field (keys %$schema) {
589 104 100       287 if ($schema->{$field}{'TYPE'}) {
    50          
590 53         171 $accessible->{$field} = { 'read' => 1, 'write' => 1 };
591             } elsif (my $refclass = $schema->{$field}{'REFERENCES'}) {
592 51 100       221 if (UNIVERSAL::isa($refclass, 'DBIx::SearchBuilder::Record')) {
    50          
593 40 50       137 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         142 $accessible->{$field} = { 'record-read' => 1, 'record-write' => 1 };
598             }
599             } elsif (UNIVERSAL::isa($refclass, 'DBIx::SearchBuilder')) {
600 11         40 $accessible->{$field} = { 'foreign-collection' => 1 };
601             } else {
602 0         0 warn "Error: $refclass neither Record nor Collection";
603             }
604             }
605             }
606              
607 53         221 return $accessible;
608             }
609              
610              
611             sub _ToRecord {
612 3     3   9 my $self = shift;
613 3         8 my $field = shift;
614 3         6 my $value = shift;
615              
616 3 50       26 return unless defined $value;
617              
618 3         11 my $schema = $self->Schema;
619 3   33     27 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       9 return unless $description;
624              
625 3 50       9 return $value unless $description->{'REFERENCES'};
626              
627 3         8 my $classname = $description->{'REFERENCES'};
628              
629 3 50       29 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         13 my $object = $classname->new( $self->_Handle );
633 3         15 $object->LoadById( $value );
634 3         21 return $object;
635             }
636              
637             sub _CollectionValue {
638 2     2   6 my $self = shift;
639              
640 2         4 my $method_name = shift;
641 2 50       8 return unless defined $method_name;
642              
643 2         8 my $schema = $self->Schema;
644 2         18 my $description = $schema->{$method_name};
645 2 50       4 return unless $description;
646              
647 2         5 my $classname = $description->{'REFERENCES'};
648              
649 2 50       12 return unless UNIVERSAL::isa($classname, 'DBIx::SearchBuilder');
650              
651 2         6 my $coll = $classname->new( Handle => $self->_Handle );
652              
653 2         15 $coll->Limit( FIELD => $description->{'KEY'}, VALUE => $self->id);
654              
655 2         10 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 2 my $self = shift;
668 1         4 my $ca = $self->_ClassAccessible();
669 1 50       9 my @readable = grep { $ca->{$_}->{'read'} or $ca->{$_}->{'record-read'} } sort keys %{$ca};
  5         11  
  1         6  
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         5 my $ca = $self->_ClassAccessible();
684 1 100       15 my @writable = grep { $ca->{$_}->{'write'} || $ca->{$_}->{'record-write'} } sort keys %{$ca};
  5         22  
  1         8  
685 1         13 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             This method doesn't do any extra work to modify or normalize the encoding of
697             the field's value. Different databases and database drivers have different ways
698             of handling encoding on returned values. For example, L automatically
699             marks values as UTF-8 if C is set to C. Review the documentation
700             for the database driver you are using and test to make sure you handle special
701             characters in returned content.
702              
703             =cut
704              
705              
706             sub __Value {
707 1807     1807   2975 my $self = shift;
708 1807         3149 my $field = lc shift;
709              
710 1807   33     3917 $field = $self->_Accessible($field, "column") || $field;
711              
712 1807 100       8725 return $self->{'values'}{$field} if $self->{'fetched'}{$field};
713 3         8 $self->{'fetched'}{$field} = 1;
714              
715 3         10 my %pk = $self->PrimaryKeys;
716 3 50       14 return undef if grep !defined, values %pk;
717              
718 3         12 my $query = "SELECT $field FROM ". $self->QuotedTableName
719             ." WHERE ". join " AND ", map "$_ = ?", sort keys %pk;
720 3 100       10 my $sth = $self->_Handle->SimpleQuery( $query, sorted_values(%pk) ) or return undef;
721 2         101 return $self->{'values'}{$field} = ($sth->fetchrow_array)[0];
722             }
723              
724             =head2 _Value
725              
726             _Value takes a single column name and returns that column's value for this row.
727             Subclasses can override _Value to insert custom access control.
728              
729             =cut
730              
731              
732             sub _Value {
733 1637     1637   2929 my $self = shift;
734 1637         3543 return ($self->__Value(@_));
735             }
736              
737              
738              
739             =head2 _Set
740              
741             _Set takes a single column name and a single unquoted value.
742             It updates both the in-memory value of this column and the in-database copy.
743             Subclasses can override _Set to insert custom access control.
744              
745             =cut
746              
747              
748             sub _Set {
749 26     26   960 my $self = shift;
750 26         110 return ($self->__Set(@_));
751             }
752              
753              
754              
755              
756             sub __Set {
757 26     26   57 my $self = shift;
758              
759 26         189 my %args = (
760             'Field' => undef,
761             'Value' => undef,
762             'IsSQL' => undef,
763             @_
764             );
765              
766 26         96 $args{'Column'} = delete $args{'Field'};
767 26         103 $args{'IsSQLFunction'} = delete $args{'IsSQL'};
768              
769 26         173 my $ret = Class::ReturnValue->new();
770              
771 26 100       232 unless ( $args{'Column'} ) {
772 1         6 $ret->as_array( 0, 'No column specified' );
773 1         13 $ret->as_error(
774             errno => 5,
775             do_backtrace => 0,
776             message => "No column specified"
777             );
778 1         13 return ( $ret->return_value );
779             }
780 25         95 my $column = lc $args{'Column'};
781              
782             # XXX: OLD behaviour, no_undefs_in_set will go away
783 25 50 66     136 if ( !defined $args{'Value'} && $self->{'no_undefs_in_set' } ) {
784 0         0 $ret->as_array( 0, "No value passed to _Set" );
785 0         0 $ret->as_error(
786             errno => 2,
787             do_backtrace => 0,
788             message => "No value passed to _Set"
789             );
790 0         0 return ( $ret->return_value );
791             }
792              
793 25 100       139 if ( defined $args{'Value'} ) {
794 17 100 66     89 if ( $args{'Value'} eq '' &&
      100        
795             ( $self->_Accessible( $args{'Column'}, 'is_numeric' )
796             || ($self->_Accessible( $args{'Column'}, 'type' ) || '') =~ /INT/i ) )
797             {
798 3         10 $args{'Value'} = 0;
799             }
800             }
801             else {
802 8 100       37 if ( $self->_Accessible( $args{Column}, 'no_nulls' ) ) {
803 4         16 my $default = $self->_Accessible( $args{Column}, 'default' );
804              
805 4 100       18 if ( defined $default ) {
806 2         6 $args{'Value'} = $default;
807             }
808             else {
809 2         14 $ret->as_array( 0, 'Illegal value for non-nullable field ' . $args{'Column'} . ": undef/null value provided and no default specified by class" );
810             $ret->as_error(
811             errno => 3,
812             do_backtrace => 0,
813 2         41 message => "Illegal value for non-nullable field " . $args{'Column'} . ": undef/null value provided and no default specified by class"
814             );
815 2         48 return ( $ret->return_value );
816             }
817             }
818             }
819              
820             # First, we truncate the value, if we need to.
821 23         114 $args{'Value'} = $self->TruncateValue( $args{'Column'}, $args{'Value'} );
822              
823 23         138 my $current_value = $self->__Value($column);
824              
825 23 100 100     252 if (
      100        
      100        
      100        
826             ( !defined $args{'Value'} && !defined $current_value )
827             || ( defined $args{'Value'}
828             && defined $current_value
829             && ( $args{'Value'} eq $current_value ) )
830             )
831             {
832 3         20 $ret->as_array( 0, "That is already the current value" );
833 3         76 $ret->as_error(
834             errno => 1,
835             do_backtrace => 0,
836             message => "That is already the current value"
837             );
838 3         73 return ( $ret->return_value );
839             }
840              
841 20         84 my $method = "Validate" . $args{'Column'};
842 20 100       126 unless ( $self->$method( $args{'Value'} ) ) {
843 1         20 $ret->as_array( 0, 'Illegal value for ' . $args{'Column'} );
844             $ret->as_error(
845             errno => 3,
846             do_backtrace => 0,
847 1         19 message => "Illegal value for " . $args{'Column'}
848             );
849 1         23 return ( $ret->return_value );
850             }
851              
852 19         111 $args{'Table'} = $self->Table();
853 19         87 $args{'PrimaryKeys'} = { $self->PrimaryKeys() };
854              
855             # The blob handling will destroy $args{'Value'}. But we assign
856             # that back to the object at the end. this works around that
857 19         51 my $unmunged_value = $args{'Value'};
858              
859 19 50       61 unless ( $self->_Handle->KnowsBLOBs ) {
860             # Support for databases which don't deal with LOBs automatically
861 0         0 my $ca = $self->_ClassAccessible();
862 0         0 my $key = $args{'Column'};
863 0 0 0     0 if ( ( $ca->{$key}->{'type'} // '' ) =~ /^(text|longtext|clob|longblob|blob|lob)$/i ) {
864 0         0 my $bhash = $self->_Handle->BLOBParams( $key, $ca->{$key}->{'type'} );
865 0 0 0     0 if ( ref($bhash) eq 'HASH'
      0        
866             && ( ( defined $args{'Value'} && length $args{'Value'} ) || $self->_Handle->HasSupportForEmptyString ) )
867             {
868 0         0 $bhash->{'value'} = $args{'Value'};
869 0         0 $args{'Value'} = $bhash;
870             }
871             }
872             }
873              
874              
875 19         55 my $val = $self->_Handle->UpdateRecordValue(%args);
876 19 50       174 unless ($val) {
877             my $message =
878             $args{'Column'}
879             . " could not be set to "
880 0 0       0 . ( defined $args{'Value'} ? $args{'Value'} : 'undef' ) . ".";
881 0         0 $ret->as_array( 0, $message);
882 0         0 $ret->as_error(
883             errno => 4,
884             do_backtrace => 0,
885             message => $message
886             );
887 0         0 return ( $ret->return_value );
888             }
889             # If we've performed some sort of "functional update"
890             # then we need to reload the object from the DB to know what's
891             # really going on. (ex SET Cost = Cost+5)
892 19 50       209 if ( $args{'IsSQLFunction'} ) {
893 0         0 $self->Load( $self->Id );
894             }
895             else {
896 19         127 $self->{'values'}->{"$column"} = $unmunged_value;
897             }
898 19         210 $ret->as_array( 1, "The new value has been set." );
899 19         715 return ( $ret->return_value );
900             }
901              
902             =head2 _Canonicalize PARAMHASH
903              
904             This routine massages an input value (VALUE) for FIELD into something that's
905             going to be acceptable.
906              
907             Takes
908              
909             =over
910              
911             =item FIELD
912              
913             =item VALUE
914              
915             =item FUNCTION
916              
917             =back
918              
919              
920             Takes:
921              
922             =over
923              
924             =item FIELD
925              
926             =item VALUE
927              
928             =item FUNCTION
929              
930             =back
931              
932             Returns a replacement VALUE.
933              
934             =cut
935              
936             sub _Canonicalize {
937 0     0   0 my $self = shift;
938 0         0 my $field = shift;
939              
940              
941              
942             }
943              
944              
945             =head2 _Validate FIELD VALUE
946              
947             Validate that VALUE will be an acceptable value for FIELD.
948              
949             Currently, this routine does nothing whatsoever.
950              
951             If it succeeds (which is always the case right now), returns true. Otherwise returns false.
952              
953             =cut
954              
955              
956              
957              
958             sub _Validate {
959 15     15   41 my $self = shift;
960 15         36 my $field = shift;
961 15         33 my $value = shift;
962              
963             #Check type of input
964             #If it's null, are nulls permitted?
965             #If it's an int, check the # of bits
966             #If it's a string,
967             #check length
968             #check for nonprintables
969             #If it's a blob, check for length
970             #In an ideal world, if this is a link to another table, check the dependency.
971 15         71 return(1);
972             }
973              
974              
975              
976             =head2 TruncateValue KEY VALUE
977              
978             Truncate a value that's about to be set so that it will fit inside the database'
979             s idea of how big the column is.
980              
981             (Actually, it looks at SearchBuilder's concept of the database, not directly into the db).
982              
983             =cut
984              
985             sub TruncateValue {
986 279     279 1 592 my $self = shift;
987 279         598 my $key = shift;
988 279         569 my $value = shift;
989              
990             # We don't need to truncate empty things.
991 279 100       828 return undef unless defined $value;
992              
993 261         844 my $metadata = $self->_ClassAccessible->{$key};
994 261 100       2636 return $value unless $metadata;
995              
996 260         504 my $truncate_to;
997 260 100 66     3099 if ( $metadata->{'length'} && !$metadata->{'is_numeric'} ) {
    100 100        
998 4         12 $truncate_to = int $metadata->{'length'};
999             }
1000             elsif ($metadata->{'type'} && $metadata->{'type'} =~ /char\((\d+)\)/ ) {
1001 137         626 $truncate_to = $1;
1002             }
1003 260 100       1137 return $value unless $truncate_to;
1004              
1005             # return asap if length in bytes is smaller than limit
1006 20 100   20   197 return $value if $truncate_to >= do { use bytes; length $value };
  20         43  
  20         205  
  141         265  
  141         1040  
1007              
1008 5 50       24 if ( Encode::is_utf8($value) ) {
1009 0         0 return Encode::decode_utf8(
1010             substr( Encode::encode_utf8( $value ), 0, $truncate_to ),
1011             Encode::FB_QUIET(),
1012             );
1013             }
1014             else {
1015             # XXX: if it's not UTF-8 then why do we convert it to?
1016 5         88 return Encode::encode_utf8( Encode::decode_utf8 (
1017             substr( $value, 0, $truncate_to ), Encode::FB_QUIET(),
1018             ) );
1019             }
1020             }
1021              
1022              
1023             =head2 _Object
1024              
1025             _Object takes a single column name and an array reference.
1026             It creates new object instance of class specified in _ClassAccessable
1027             structure and calls LoadById on recently created object with the
1028             current column value as argument. It uses the array reference as
1029             the object constructor's arguments.
1030             Subclasses can override _Object to insert custom access control or
1031             define default constructor arguments.
1032              
1033             Note that if you are using a C with a C field,
1034             this is unnecessary: the method to access the column's value will
1035             automatically turn it into the appropriate object.
1036              
1037             =cut
1038              
1039             sub _Object {
1040 1     1   14 my $self = shift;
1041 1         9 return $self->__Object(@_);
1042             }
1043              
1044             sub __Object {
1045 1     1   10 my $self = shift;
1046 1         7 my %args = ( Field => '', Args => [], @_ );
1047              
1048 1         3 my $field = $args{'Field'};
1049 1         4 my $class = $self->_Accessible( $field, 'object' );
1050              
1051             # Globs magic to be sure that we call 'eval "require $class"' only once
1052             # because eval is quite slow -- cubic@acronis.ru
1053 20     20   5388 no strict qw( refs );
  20         43  
  20         45746  
1054 1         3 my $vglob = ${ $class . '::' }{'VERSION'};
  1         7  
1055 1 50 50     9 unless ( $vglob && *$vglob{'SCALAR'} ) {
1056 0         0 eval "require $class";
1057 0 0       0 die "Couldn't use $class: $@" if ($@);
1058 0 0 0     0 unless ( $vglob && *$vglob{'SCALAR'} ) {
1059 0         0 *{ $class . "::VERSION" } = '-1, By DBIx::SearchBuilder';
  0         0  
1060             }
1061             }
1062              
1063 1         3 my $object = $class->new( @{ $args{'Args'} } );
  1         6  
1064 1         16 $object->LoadById( $self->__Value($field) );
1065 1         7 return $object;
1066             }
1067              
1068              
1069              
1070              
1071             # load should do a bit of overloading
1072             # if we call it with only one argument, we're trying to load by reference.
1073             # if we call it with a passel of arguments, we're trying to load by value
1074             # The latter is primarily important when we've got a whole set of record that we're
1075             # reading in with a recordset class and want to instantiate objefcts for each record.
1076              
1077             =head2 Load
1078              
1079             Takes a single argument, $id. Calls LoadById to retrieve the row whose primary key
1080             is $id
1081              
1082             =cut
1083              
1084              
1085              
1086             sub Load {
1087 49     49 1 31633 my $self = shift;
1088 49         315 return $self->LoadById(@_);
1089             }
1090              
1091              
1092             =head2 LoadByCol
1093              
1094             Takes two arguments, a column and a value. The column can be any table column
1095             which contains unique values. Behavior when using a non-unique value is
1096             undefined
1097              
1098             =cut
1099              
1100             sub LoadByCol {
1101 2     2 1 21 my $self = shift;
1102 2         7 return $self->LoadByCols(@_);
1103             }
1104              
1105              
1106              
1107             =head2 LoadByCols
1108              
1109             Takes a hash of columns and values. Loads the first record that matches all
1110             keys.
1111              
1112             The hash's keys are the columns to look at.
1113              
1114             The hash's values are either: scalar values to look for
1115             OR has references which contain 'operator' and 'value'
1116              
1117             =cut
1118              
1119              
1120             sub LoadByCols {
1121 59     59 1 153 my $self = shift;
1122 59         246 my %hash = (@_);
1123 59         283 my (@bind, @phrases);
1124 59         294 foreach my $key (sort keys %hash) {
1125 61 100 100     681 if (defined $hash{$key} && $hash{$key} ne '') {
1126 59         120 my $op;
1127             my $value;
1128 59         132 my $function = "?";
1129 59 100       195 if (ref $hash{$key} eq 'HASH') {
1130 1         5 $op = $hash{$key}->{operator};
1131 1         2 $value = $hash{$key}->{value};
1132 1   50     9 $function = $hash{$key}->{function} || "?";
1133             } else {
1134 58         111 $op = '=';
1135 58         152 $value = $hash{$key};
1136             }
1137              
1138 59         198 push @phrases, "$key $op $function";
1139 59         232 push @bind, $value;
1140             }
1141             else {
1142 2         9 push @phrases, "($key IS NULL OR $key = ?)";
1143 2         10 my $meta = $self->_ClassAccessible->{$key};
1144 2   50     86 $meta->{'type'} ||= '';
1145             # TODO: type checking should be done in generic way
1146 2 100 66     30 if ( $meta->{'is_numeric'} || $meta->{'type'} =~ /INT|NUMERIC|DECIMAL|REAL|DOUBLE|FLOAT/i ) {
1147 1         5 push @bind, 0;
1148             } else {
1149 1         5 push @bind, '';
1150             }
1151             }
1152             }
1153              
1154 59         313 my $QueryString = "SELECT * FROM ".$self->QuotedTableName." WHERE ".
1155             join(' AND ', @phrases) ;
1156 59         297 return ($self->_LoadFromSQL($QueryString, @bind));
1157             }
1158              
1159              
1160              
1161              
1162             =head2 LoadById
1163              
1164             Loads a record by its primary key. Your record class must define a single primary key column.
1165              
1166             =cut
1167              
1168              
1169             sub LoadById {
1170 55     55 1 190 my ($self, $id) = @_;
1171 55 100       321 return $self->LoadByCols( $self->_PrimaryKey, defined $id? $id: 0 );
1172             }
1173              
1174              
1175              
1176              
1177             =head2 LoadByPrimaryKeys
1178              
1179             Like LoadById with basic support for compound primary keys.
1180              
1181             =cut
1182              
1183              
1184              
1185             sub LoadByPrimaryKeys {
1186 3     3 1 23 my $self = shift;
1187 3 100       15 my $data = (ref $_[0] eq 'HASH')? $_[0]: {@_};
1188              
1189 3         8 my %cols=();
1190 3         6 foreach (@{$self->_PrimaryKeys}) {
  3         11  
1191 3 100       20 return (0, "Missing PK field: '$_'") unless defined $data->{$_};
1192 2         7 $cols{$_}=$data->{$_};
1193             }
1194 2         9 return ($self->LoadByCols(%cols));
1195             }
1196              
1197              
1198              
1199              
1200             =head2 LoadFromHash
1201              
1202             Takes a hashref, such as created by DBIx::SearchBuilder and populates this record's
1203             loaded values hash.
1204              
1205             =cut
1206              
1207              
1208              
1209             sub LoadFromHash {
1210 1296     1296 1 2003 my $self = shift;
1211 1296         2010 my $hashref = shift;
1212              
1213 1296         3675 foreach my $f ( keys %$hashref ) {
1214 3261         7791 $self->{'fetched'}{lc $f} = 1;
1215             }
1216              
1217 1296         2749 $self->{'values'} = $hashref;
1218 1296         2881 return $self->id();
1219             }
1220              
1221              
1222              
1223             =head2 _LoadFromSQL QUERYSTRING @BIND_VALUES
1224              
1225             Load a record as the result of an SQL statement
1226              
1227             =cut
1228              
1229              
1230              
1231              
1232             sub _LoadFromSQL {
1233 63     63   143 my $self = shift;
1234 63         126 my $QueryString = shift;
1235 63         211 my @bind_values = (@_);
1236              
1237 63         168 my $sth = $self->_Handle->SimpleQuery( $QueryString, @bind_values );
1238              
1239             #TODO this only gets the first row. we should check if there are more.
1240              
1241 63 100       240 return ( 0, "Couldn't execute query: ".$self->_Handle->dbh->errstr ) unless $sth;
1242              
1243 62         3139 $self->{'values'} = $sth->fetchrow_hashref;
1244 62         353 $self->{'fetched'} = {};
1245 62 50 66     318 if ( !$self->{'values'} && $sth->err ) {
1246 0         0 return ( 0, "Couldn't fetch row: ". $sth->err );
1247             }
1248              
1249 62 100       277 unless ( $self->{'values'} ) {
1250 3         60 return ( 0, "Couldn't find row" );
1251             }
1252              
1253             ## I guess to be consistant with the old code, make sure the primary
1254             ## keys exist.
1255              
1256 59 100       399 if( grep { not defined } $self->PrimaryKeys ) {
  118         451  
1257 1         46 return ( 0, "Missing a primary key?" );
1258             }
1259              
1260 58         121 foreach my $f ( keys %{$self->{'values'}} ) {
  58         271  
1261 157         479 $self->{'fetched'}{lc $f} = 1;
1262             }
1263 58         1297 return ( 1, "Found Object" );
1264              
1265             }
1266              
1267              
1268              
1269              
1270              
1271             =head2 Create
1272              
1273             Takes an array of key-value pairs and drops any keys that aren't known
1274             as columns for this recordtype
1275              
1276             =cut
1277              
1278              
1279              
1280             sub Create {
1281 155     155 1 8146 my $self = shift;
1282 155         780 my %attribs = @_;
1283              
1284 155         311 my ($key);
1285 155         595 foreach $key ( keys %attribs ) {
1286              
1287 255 100       1035 if ( $self->_Accessible( $key, 'record-write' ) ) {
1288             $attribs{$key} = $attribs{$key}->id
1289 3 100       16 if UNIVERSAL::isa( $attribs{$key},
1290             'DBIx::SearchBuilder::Record' );
1291             }
1292              
1293 255 100       814 if ( defined $attribs{$key} ) {
1294 240 100 66     897 if ( $attribs{$key} eq '' &&
      100        
1295             ( $self->_Accessible( $key, 'is_numeric' )
1296             || ($self->_Accessible( $key, 'type' ) || '') =~ /INT/i ) )
1297             {
1298 1         2 $attribs{$key} = 0;
1299             }
1300             }
1301             else {
1302 15 100       43 $attribs{$key} = $self->_Accessible( $key, 'default' )
1303             if $self->_Accessible( $key, 'no_nulls' );
1304             }
1305              
1306             #Truncate things that are too long for their datatypes
1307 255         1110 $attribs{$key} = $self->TruncateValue( $key => $attribs{$key} );
1308              
1309             }
1310 155 50       538 unless ( $self->_Handle->KnowsBLOBs ) {
1311              
1312             # Support for databases which don't deal with LOBs automatically
1313 0         0 my $ca = $self->_ClassAccessible();
1314 0         0 foreach $key ( keys %attribs ) {
1315 0         0 my $type = $ca->{$key}->{'type'};
1316 0 0 0     0 next unless $type && $type =~ /^(text|longtext|clob|blob|lob|longblob)$/i;
1317              
1318 0         0 my $bhash = $self->_Handle->BLOBParams( $key, $type );
1319 0 0 0     0 if ( ref($bhash) eq 'HASH'
      0        
1320             && ( ( defined $attribs{$key} && length $attribs{$key} ) || $self->_Handle->HasSupportForEmptyString ) )
1321             {
1322 0         0 $bhash->{'value'} = $attribs{$key};
1323 0         0 $attribs{$key} = $bhash;
1324             }
1325             }
1326             }
1327 155         467 return ( $self->_Handle->Insert( $self->Table, %attribs ) );
1328             }
1329              
1330              
1331             =head2 Delete
1332              
1333             Delete this record from the database. On failure return a Class::ReturnValue with the error. On success, return 1;
1334              
1335             =cut
1336              
1337             *delete = \&Delete;
1338              
1339             sub Delete {
1340 2     2 1 299 $_[0]->__Delete;
1341             }
1342              
1343             sub __Delete {
1344 2     2   5 my $self = shift;
1345              
1346             #TODO Check to make sure the key's not already listed.
1347             #TODO Update internal data structure
1348              
1349             ## Constructs the where clause.
1350 2         5 my @bind=();
1351 2         7 my %pkeys=$self->PrimaryKeys();
1352 2         5 my $where = 'WHERE ';
1353 2         9 foreach my $key (sort keys %pkeys) {
1354 2         7 $where .= $key . "=?" . " AND ";
1355 2         7 push (@bind, $pkeys{$key});
1356             }
1357              
1358 2         14 $where =~ s/AND\s$//;
1359 2         6 my $QueryString = "DELETE FROM ". $self->QuotedTableName . ' ' . $where;
1360 2         6 my $return = $self->_Handle->SimpleQuery($QueryString, @bind);
1361              
1362 2 50       36 if (UNIVERSAL::isa($return, 'Class::ReturnValue')) {
1363 0         0 return ($return);
1364             } else {
1365 2         75 return(1);
1366             }
1367             }
1368              
1369              
1370              
1371              
1372              
1373             =head2 Table
1374              
1375             Returns or sets the name of the current Table
1376              
1377             =cut
1378              
1379              
1380              
1381             sub Table {
1382 1671     1671 1 8598 my $self = shift;
1383 1671 100       3629 if (@_) {
1384 1453         4031 $self->{'table'} = shift;
1385             }
1386 1671         4343 return ($self->{'table'});
1387             }
1388              
1389             =head2 QuotedTableName
1390              
1391             Returns the name of current Table, or the table provided as an argument, including any quoting
1392             based on yje Handle's QuoteTableNames flag and driver method.
1393              
1394             =cut
1395              
1396             sub QuotedTableName {
1397 64     64 1 181 my ($self, $name) = @_;
1398 64 50       201 unless ($name) {
1399 64 100       283 return $self->{'_quoted_table'} if defined $self->{'_quoted_table'};
1400 59 50       199 $self->{'_quoted_table'}
1401             = $self->_Handle->QuoteTableNames ? $self->_Handle->QuoteName( $self->Table ) : $self->Table;
1402 59         336 return $self->{'_quoted_table'};
1403             }
1404 0 0       0 return $self->_Handle->QuoteTableNames ? $self->_Handle->QuoteName($name) : $name;
1405             }
1406              
1407             =head2 _Handle
1408              
1409             Returns or sets the current DBIx::SearchBuilder::Handle object
1410              
1411             =cut
1412              
1413              
1414             sub _Handle {
1415 1968     1968   6072 my $self = shift;
1416 1968 100       4344 if (@_) {
1417 1487         2953 $self->{'DBIxHandle'} = shift;
1418             }
1419 1968         5191 return ($self->{'DBIxHandle'});
1420             }
1421              
1422              
1423             if( eval { require capitalization } ) {
1424             capitalization->unimport( __PACKAGE__ );
1425             }
1426              
1427             1;