package LIMS::Model::Roles::QueryFormatter; use Moose::Role; has search_fields_map => ( is => 'ro', isa => 'HashRef', lazy_build => 1 ); has relationships_map => ( is => 'ro', isa => 'HashRef', lazy_build => 1 ); has search_constraint => ( is => 'rw', isa => 'HashRef', default => sub { {} }); use strict; use Data::Dumper; #------------------------------------------------------------------------------- sub get_args_for_requests_count { my ($self, $search_constraints) = @_; # warn Dumper $search_constraints; $self->search_constraint($search_constraints); # require data from these tables: my @tables = (); # none for count # format args_for_search: my $args_for_search = $self->_format_args_for_search(\@tables); return $args_for_search; # warn Dumper $args_for_search; } #------------------------------------------------------------------------------- sub get_args_for_request_id { my ($self, $search_constraints) = @_; $self->search_constraint($search_constraints); # require data from these tables: my @tables = qw( patients specimens referrers referral_sources clinical_trials ); # format args_for_search: my $args_for_search = $self->_format_args_for_search(\@tables); return $args_for_search; # warn Dumper $self->args_for_search; } #------------------------------------------------------------------------------- # recieves $args hashref with keys 'search_constraints' & 'args_for_search': sub get_args_for_find_requests { my $self = shift; my $args = shift; # hashref with keys = 'search_constraints' & 'args_for_search' my $search_constraints = $args->{search_constraints}; # $self->debug($search_constraints); # add $search_constraints to $self for _format_relationships(): $self->search_constraint($search_constraints); # require data from these tables: my @tables = qw( patients referrers referral_sources ); # format args_for_search: my @args = (\@tables, $args->{args_for_search}); my $args_for_search = $self->_format_args_for_search(@args); return $args_for_search; # warn Dumper $self->args_for_search; } #------------------------------------------------------------------------------- # accepts pairs of params & returns them in a format suitable for RDBO::Manager sub format_query { my ($self, %params) = @_; # RBDO::Manager needs query as arrayref of pairs: return [ %params ]; } #------------------------------------------------------------------------------- # accepts arrayref of table_names, returns arrayref of relationship names: sub get_relationships { my ($self, $tables) = @_; # arrayref my $relationships_map = $self->relationships_map; # get relationship names needed for table joins: my @relationships = map { $relationships_map->{$_}->{rel_name} } grep { # check entry exists (or rel silently skipped & db gets hit again for missing data): $relationships_map->{$_} || die "no such entry '$_' in _build_relationships_map()" } @$tables; return \@relationships; } #------------------------------------------------------------------------------- # accepts $required_relationships arrayref and optional $args_for_search hashref # or creates new; adds 'query' arrayref from $self->search_constraints; adds # 'with_objects' & 'require_objects' arrayrefs if required; returns $args_for_search sub _format_args_for_search { my $self = shift; my $tables = shift; # arrayref my $args_for_search = shift || {}; # optional (contains limit, sort_by, etc) my $search_fields_map = $self->search_fields_map; # warn Dumper $search_fields_map; my $relationships_map = $self->relationships_map; my $search_constraints = $self->search_constraint; # warn Dumper $search_constraints; { # convert $search_constraints hashref format into arrayref for query func: my @params = %$search_constraints; # put 'query' into $args_for_search: $args_for_search->{query} = \@params; } # convert $tables array(ref) into a hash to prevent duplicate relationships # in FIELD block below: my %tables = map { $_ => 1 } @$tables; # warn Dumper \%tables; # get any additional table joins required for this query: FIELD: foreach my $field (keys %$search_constraints) { # warn $field; # get table name for 'key' of search_constraints (last_name, unit_number, etc): my $table_name = $search_fields_map->{$field} || next FIELD; # eg request_number, year - don't need additional table joins $tables{$table_name}++; } # warn Dumper \%tables; # create 'with_objects' & 'require_objects' from data in %tables: foreach my $table (keys %tables) { # keys are unique so tbl only loaded once my $relationship_data = $relationships_map->{$table} # eg with => 'request_trial' || die "no such entry '$table' in _build_relationships_map()"; my $join_type = $relationship_data->{join_type}; # 'with_objects' or 'require_objects' my $rel_name = $relationship_data->{rel_name}; # eg 'patient_case.patient', etc # push relationship name onto $args_for_search $join_type key: push @{ $args_for_search->{$join_type} }, $rel_name; } if ( $args_for_search->{with_objects} ) { # needs multi_many_ok => 1 to silence warnings: $args_for_search->{multi_many_ok} = 1; } # warn Dumper $args_for_search; # set default 'order by': $args_for_search->{sort_by} ||= 'requests.id'; # not supplied in patient merge return $args_for_search; } #------------------------------------------------------------------------------- sub _build_search_fields_map { my $self = shift; # create map of form field => $self->relationships_map name: my %search_fields_map = ( # field_name # in table last_name => 'patients', first_name => 'patients', dob => 'patients', nhs_number => 'patients', unit_number => 'patients', name => 'referrers', specimen_id => 'specimens', option_name => 'request_options', trial_id => 'clinical_trials', trial_number => 'patient_trials', referral_source_id => 'referral_sources', parent_code => 'parent_organisations', external_reference => 'request_external_ref', # for simple sql query: clinical_details => 'request_report', comment => 'request_report', detail => 'request_gross_description', sample_code => 'sample_code', 'diagnoses.name' => 'diagnoses', 'referrers.name' => 'referrers', # or could just use 'name' as above 'display_name' => 'referral_sources', 'patients.id' => 'patients', 'unit_number' => 'patient_case', 'screens.description' => 'request_initial_screen', results_summary => 'results_summary', trial_name => 'clinical_trials', ); return \%search_fields_map; } #------------------------------------------------------------------------------- # map of relationship names for tables, relative to LIMS::DB::Request: sub _build_relationships_map { my $self = shift; my %relationship_map = ( # table name => join-type / relationship name # require = 'inner join'; with = 'left outer join' clinical_trials => { join_type => 'with_objects', rel_name => 'request_trial.trial', }, diagnoses => { join_type => 'with_objects', rel_name => 'request_report.diagnosis', }, diagnostic_categories => { join_type => 'with_objects', # changed from diagnosis.icdo3_category.diagnostic_category rel_name => 'request_report.diagnosis.diagnostic_category', }, hospital_departments => { join_type => 'require_objects', rel_name => 'referrer_department.hospital_department', }, parent_organisations => { join_type => 'require_objects', rel_name => 'patient_case.referral_source.parent_organisation', }, patients => { join_type => 'require_objects', rel_name => 'patient_case.patient', }, patient_case => { join_type => 'require_objects', rel_name => 'patient_case', }, patient_trials => { join_type => 'require_objects', rel_name => 'patient_case.patient.patient_trial', }, referral_sources => { join_type => 'require_objects', rel_name => 'patient_case.referral_source', }, referrers => { join_type => 'require_objects', rel_name => 'referrer_department.referrer', }, request_external_ref => { join_type => 'with_objects', rel_name => 'request_external_ref', }, request_general_notes => { join_type => 'with_objects', rel_name => 'request_general_note', }, request_history => { join_type => 'require_objects', rel_name => 'request_history', }, request_gross_description => { join_type => 'with_objects', rel_name => 'request_gross_description', }, request_initial_screen => { join_type => 'with_objects', rel_name => 'request_initial_screen.screen', }, request_options => { join_type => 'with_objects', rel_name => 'request_option.option', }, request_report => { join_type => 'require_objects', rel_name => 'request_report', }, results_summary => { join_type => 'require_objects', rel_name => 'results_summary', }, sample_code => { # used by simple sql search only join_type => 'require_objects', rel_name => 'request_specimen.specimen', }, specimens => { join_type => 'require_objects', rel_name => 'request_specimen', }, status_options => { join_type => 'require_objects', rel_name => 'status_option', }, ); return \%relationship_map; } 1;