#---------------------------------------------------------------------- package DBIx::DataModel::Source; #---------------------------------------------------------------------- # see POD doc at end of file use warnings; no warnings 'uninitialized'; use strict; use Carp; use base 'DBIx::DataModel::Base'; use DBIx::DataModel::Statement; our @CARP_NOT = qw/DBIx::DataModel DBIx::DataModel::Schema DBIx::DataModel::Table DBIx::DataModel::View DBIx::DataModel::Iterator/; #---------------------------------------------------------------------- # COMPILE-TIME PUBLIC METHODS #---------------------------------------------------------------------- sub MethodFromJoin { my ($class, $meth_name, @roles) = @_; @roles or croak "MethodFromJoin: not enough arguments"; # last arg may be a hashref of parameters to be passed to select() my $pre_args; $pre_args = pop @roles if ref $roles[-1]; my $meth = sub { my ($self, @args) = @_; # if called without args, and just one role, and that role # was previously expanded, then return the cached version if (@roles == 1 && !@args) { my $cached = $self->{$roles[0]}; return $cached if $cached; } unshift @args, %$pre_args if $pre_args; my $statement = $self->join(@roles); return ref $self ? $statement->select(@args) # when instance method : $statement->refine(@args); # when class method }; $class->schema->_defineMethod($class, $meth_name, $meth); return $class; } # backwards compatibility *MethodFromRoles = \&MethodFromJoin; #---------------------------------------------------------------------- # RUNTIME PUBLIC METHODS #---------------------------------------------------------------------- sub schema { my $self = shift; return $self->classData->{schema}; } sub table { my $self = shift; carp "the table() method is deprecated; call db_table() instead"; return $self->db_table; } sub db_table { my $self = shift; return $self->classData->{db_table}; } sub selectImplicitlyFor { my $self = shift; if (@_) { not ref($self) or croak "selectImplicitlyFor(value) should be called as class method"; $self->classData->{selectImplicitlyFor} = shift; } return exists($self->classData->{selectImplicitlyFor}) ? $self->classData->{selectImplicitlyFor} : $self->schema->selectImplicitlyFor; } sub blessFromDB { my ($class, $record) = @_; not ref($class) or croak "blessFromDB() should be called as class method"; bless $record, $class; $record->applyColumnHandler('fromDB'); return $record; } sub createStatement { my $class = shift; not ref($class) or croak "createStatement() should be called as class method"; return $class->schema->classData->{statementClass} ->new($class, @_); } sub select { my ($class, @args) = @_; not ref($class) or croak "select() should be called as class method"; return $class->createStatement->select(@args); } sub applyColumnHandler { my ($self, $handlerName, $objects) = @_; my $targets = $objects || [$self]; my $columnHandlers = $self->classData->{columnHandlers} || {}; my $results = {}; # iterate over all registered columnHandlers while (my ($columnName, $handlers) = each %$columnHandlers) { # is $handlerName registered in this column ? my $handler = $handlers->{$handlerName} or next; # apply that handler to all targets that possess the $columnName foreach my $obj (@$targets) { my $result = exists $obj->{$columnName} ? $handler->($obj->{$columnName}, $obj, $columnName, $handlerName) : undef; if ($objects) { push(@{$results->{$columnName}}, $result); } else { $results->{$columnName} = $result; } } } return $results; } sub expand { my ($self, $role, @args) = @_; $self->{$role} = $self->$role(@args); } sub autoExpand {} # default; overridden in subclasses through AutoExpand() sub join { my ($self, $first_role, @other_roles) = @_; my $class = ref $self || $self; my $isa_view = $class->isa('DBIx::DataModel::View'); my $schema = $class->schema; my $joins = $schema->classData->{joins}; my $table_classes = $isa_view ? $class->classData->{parentTables} : [$class]; # find first join information my ($join_data) = grep {$_} map {$joins->{$_}{$first_role}} @$table_classes or croak "could not find role $first_role in $class"; # build search criteria on %$self from first join information my (%criteria, @left_cols); while (my ($left_col, $right_col) = each %{$join_data->{where}}) { $criteria{$right_col} = "?$left_col"; push @left_cols, $left_col; } # choose source and build a statement my $source = @other_roles ? $schema->join($join_data->{table}, @other_roles) # build a view : $join_data->{table}; # just take the table my $statement = $source->createStatement(-where => \%criteria); # if called as an instance method if (ref $self) { # check that all foreign keys are present my $missing = join ", ", grep {not exists $self->{$_}} @left_cols; not $missing or croak "cannot follow role '$first_role': missing column '$missing'"; # bind to foreign keys $statement->bind(map {($_ => $self->{$_})} @left_cols); } return $statement; } # backwards compatibility *selectFromRoles = \&join; #---------------------------------------------------------------------- # RUNTIME PRIVATE METHODS OR FUNCTIONS #---------------------------------------------------------------------- sub _debug { # internal method to send debug messages my ($self, $msg) = @_; my $debug = $self->schema->classData->{debug}; if ($debug) { if (ref $debug && $debug->can('debug')) { $debug->debug($msg) } else { carp $msg; } } } 1; # End of DBIx::DataModel::Source __END__ =head1 NAME DBIx::DataModel::Source - Abstract parent for Table and View =head1 DESCRIPTION Abstract parent class for L and L. For internal use only. =head1 METHODS Methods are documented in L. This module implements =over =item L =item L =item L =item L =item L =item L =item L =item L =item L =item L =back =head1 PRIVATE METHOD NAMES The following methods or functions are used internally by this module and should be considered as reserved names, not to be redefined in subclasses : =over =item _debug =back =head1 AUTHOR Laurent Dami, Elaurent.dami AT etat ge chE =head1 COPYRIGHT AND LICENSE Copyright 2006, 2008 by Laurent Dami. This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.