From 8a9d19c406157cdda5a7043d66029723da256355 Mon Sep 17 00:00:00 2001 From: Pedro Amorim Date: Thu, 30 Nov 2023 15:43:02 -0100 Subject: [PATCH] Bug 30645: Consider multiple instances of extended_attributes in query params This will rewrite a query like: { "-and":[ [ { "extended_attributes.value":{ "like":"abc%" }, "extended_attributes.code":[ [ "arbitrary_attr_code", "another_attr_code" ] ] } ], [ { "extended_attributes.value":{ "like":"123%" }, "extended_attributes.code":[ [ "arbitrary_attr_code", "another_attr_code" ] ] } ] ] } To: { "-and":[ [ { "extended_attributes.value":{ "like":"abc%" }, "extended_attributes.code":[ [ "arbitrary_attr_code", "another_attr_code" ] ] } ], [ { "extended_attributes_2.value":{ "like":"123%" }, "extended_attributes_2.code":[ [ "arbitrary_attr_code", "another_attr_code" ] ] } ] ] } And it'll also add the number of required 'join' attributes to the query attributes to match it. Test plan (with Joubu's test patch applied), run: prove t/db_dependent/selenium/patrons_search.t Or Test plan, k-t-d: 1) Create 2 patron attributes, visit: /cgi-bin/koha/admin/patron-attr-types.pl 2) Name the first attribute 'test1' and the second attribute 'test2' 3) On both, tick the 'Searchable' and 'Search by default' checkboxes 4) Edit a koha borrower, example visit: /cgi-bin/koha/members/memberentry.pl?op=modify&destination=circ&borrowernumber=51 5) Put '123' on the test1 attribute, and 'abc' on the test2 attribute 6) Visit the main patrons page: /cgi-bin/koha/members/members-home.pl 7) On the first left side filter input called 'Search', type '123' and search. Notice you get the patron result 8) Repeat step 7, but type 'abc' instead of '123'. Notice you get the patron result 9) Now do the same search but type '123 abc'. Notice you do not get the patron result. 10) Apply patch. Repeat test plan. Notice you get the patron result in step 9). --- Koha/REST/Plugin/Objects.pm | 8 ++ Koha/REST/Plugin/Query.pm | 152 ++++++++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+) diff --git a/Koha/REST/Plugin/Objects.pm b/Koha/REST/Plugin/Objects.pm index 32dab06d4b..7f06582b4d 100644 --- a/Koha/REST/Plugin/Objects.pm +++ b/Koha/REST/Plugin/Objects.pm @@ -287,6 +287,14 @@ controller, and thus shouldn't be called twice in it. $c->stash('koha.pagination.base_total' => $result_set->count); $c->stash('koha.pagination.query_params' => $args); + # Check and handle related metadata table joins + $c->dbic_extended_attributes_join( + { + attributes => $attributes, + filtered_params => $filtered_params + } + ); + # Generate the resultset my $objects_rs = $result_set->search( $filtered_params, $attributes ); # Stash the page total if requires, total otherwise diff --git a/Koha/REST/Plugin/Query.pm b/Koha/REST/Plugin/Query.pm index 7aa092fae2..53decc94bc 100644 --- a/Koha/REST/Plugin/Query.pm +++ b/Koha/REST/Plugin/Query.pm @@ -159,6 +159,34 @@ Generates the DBIC prefetch attribute based on embedded relations, and merges in } ); +=head3 dbic_extended_attributes_join + + $attributes = $c->dbic_extended_attributes_join({ attributes => $attributes, result_set => $result_set }); + +Generates the DBIC join attribute based on extended_attributes query entries, and merges into I<$attributes>. + +=cut + + $app->helper( + 'dbic_extended_attributes_join' => sub { + my ( $c, $args ) = @_; + + my $attributes = $args->{attributes}; + my $filtered_params = $args->{filtered_params}; + + if ( reftype( $attributes->{prefetch} ) + && reftype( $attributes->{prefetch} ) eq 'ARRAY' + && grep ( /extended_attributes/, @{ $attributes->{prefetch} } ) ) + { + my $ea_entries = $self->_get_extended_attributes_entries( $filtered_params, 0 ); + while ( $ea_entries > 0 ) { + push( @{ $attributes->{join} }, 'extended_attributes' ); + $ea_entries--; + } + } + } + ); + =head3 _build_query_params_from_api my $params = _build_query_params_from_api( $filtered_params, $reserved_params ); @@ -486,7 +514,131 @@ sub _parse_dbic_query { } else { return $q_params; } +} + +=head3 _get_extended_attributes_entries + + $self->_get_extended_attributes_entries( $filtered_params, 0 ) + +Recursive function that returns the number of extended_attributes entries present in a query. +Example: Returns 2 if given a $filtered_params containing the below: + +{ + "-and":[ + [ + { + "extended_attributes.value":{ + "like":"abc%" + }, + "extended_attributes.code":[ + [ + "arbitrary_attr_code", + "another_attr_code" + ] + ] + } + ], + [ + { + "extended_attributes.value":{ + "like":"123%" + }, + "extended_attributes.code":[ + [ + "arbitrary_attr_code", + "another_attr_code" + ] + ] + } + ] + ] +} + +=cut + +sub _get_extended_attributes_entries { + my ( $self, $params, $extended_attributes_entries ) = @_; + + if ( reftype($params) && reftype($params) eq 'HASH' ) { + + # rewrite additional_field_values table query params + $extended_attributes_entries = + _rewrite_related_metadata_query( $params, $extended_attributes_entries, 'field_id', 'value' ) + if $params->{'extended_attributes.field_id'}; + + # rewrite borrower_attributes table query params + $extended_attributes_entries = + _rewrite_related_metadata_query( $params, $extended_attributes_entries, 'code', 'attribute' ) + if $params->{'extended_attributes.code'}; + + # rewrite illrequestattributes table query params + $extended_attributes_entries = + _rewrite_related_metadata_query( $params, $extended_attributes_entries, 'type', 'value' ) + if $params->{'extended_attributes.type'}; + + foreach my $key ( keys %{$params} ) { + return $self->_get_extended_attributes_entries( $params->{$key}, $extended_attributes_entries ); + } + } elsif ( reftype($params) && reftype($params) eq 'ARRAY' ) { + foreach my $ea_instance (@$params) { + $extended_attributes_entries = + +$self->_get_extended_attributes_entries( $ea_instance, $extended_attributes_entries ); + } + return $extended_attributes_entries; + } else { + return $extended_attributes_entries; + } +} + +=head3 _rewrite_related_metadata_query + + $extended_attributes_entries = + _rewrite_related_metadata_query( $params, $extended_attributes_entries, 'field_id', 'value' ) + +Helper function that rewrites all subsequent extended_attributes queries to match the alias generated by the dbic self left join +Take the below example (patrons), assuming it is the third instance of an extended_attributes query: +{ + "extended_attributes.value":{ + "like":"123%" + }, + "extended_attributes.code":[ + [ + "arbitrary_attr_code", + "another_attr_code" + ] + ] +} + +It'll be rewritten as: +{ + "extended_attributes_3.value":{ + "like":"123%" + }, + "extended_attributes_3.code":[ + [ + "arbitrary_attr_code", + "another_attr_code" + ] + ] +} + +=cut + +sub _rewrite_related_metadata_query { + my ( $params, $extended_attributes_entries, $key, $value ) = @_; + + $extended_attributes_entries++; + if ( $extended_attributes_entries > 1 ) { + my $old_key_value = delete $params->{ 'extended_attributes.' . $key }; + my $new_key_value = "extended_attributes_$extended_attributes_entries" . "." . $key; + $params->{$new_key_value} = $old_key_value; + + my $old_value_value = delete $params->{ 'extended_attributes.' . $value }; + my $new_value_value = "extended_attributes_$extended_attributes_entries" . "." . $value; + $params->{$new_value_value} = $old_value_value; + } + return $extended_attributes_entries; } 1; -- 2.30.2