package Koha::Objects::Mixin::ExtendedAttributes; # Copyright 2024 PTFS Europe Ltd # # This file is part of Koha. # # Koha is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # Koha is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Koha; if not, see . use Modern::Perl; use Scalar::Util qw( reftype ); =head1 NAME Koha::Objects::Mixin::ExtendedAttributes =head2 Class methods =head3 search Overwrites the search method to include extended attributes rewrite and dynamic relation accessors =cut sub search { my ( $self, $params, $attributes ) = @_; my $class = ref($self) ? ref($self) : $self; $self->handle_query_extended_attributes( { attributes => $attributes, filtered_params => $params, } ); my $rs = $self->_resultset()->search( $params, $attributes ); return $class->_new_from_dbic($rs); } =head3 handle_query_extended_attributes Checks for the presence of extended_attributes in a query If present, it builds the dynamic extended attributes relations and rewrites the query to include the extended_attributes relation =cut sub handle_query_extended_attributes { my ( $self, $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 @array = $self->_get_extended_attributes_entries($filtered_params); # Calling our private method to build the extended attributes relations my @joins = $self->_build_extended_attributes_relations( \@array ); push @{ $attributes->{join} }, @joins; } } =head3 _get_extended_attributes_entries $self->_get_extended_attributes_entries( $filtered_params, 0 ) Recursive function that returns the rewritten extended_attributes query entries. Given: [ '-and', [ { 'extended_attributes.code' => 'CODE_1', 'extended_attributes.attribute' => { 'like' => '%Bar%' } }, { 'extended_attributes.attribute' => { 'like' => '%Bar%' }, 'extended_attributes.code' => 'CODE_2' } ] ]; Returns : [ 'CODE_1', 'CODE_2' ] =cut sub _get_extended_attributes_entries { my ( $self, $params, @array ) = @_; if ( reftype($params) && reftype($params) eq 'HASH' ) { # rewrite additional_field_values table query params @array = _rewrite_related_metadata_query( $params, 'field_id', 'value', @array ) if $params->{'extended_attributes.field_id'}; # rewrite borrower_attributes table query params @array = _rewrite_related_metadata_query( $params, 'code', 'attribute', @array ) if $params->{'extended_attributes.code'}; # rewrite illrequestattributes table query params @array = _rewrite_related_metadata_query( $params, 'type', 'value', @array ) if $params->{'extended_attributes.type'}; foreach my $key ( keys %{$params} ) { return $self->_get_extended_attributes_entries( $params->{$key}, @array ); } } elsif ( reftype($params) && reftype($params) eq 'ARRAY' ) { foreach my $ea_instance (@$params) { @array = $self->_get_extended_attributes_entries( $ea_instance, @array ); } return @array; } else { return @array; } } =head3 _rewrite_related_metadata_query $extended_attributes_entries = _rewrite_related_metadata_query( $params, 'field_id', 'value', @array ) 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): [ { "extended_attributes.attribute":{"like":"%123%"}, "extended_attributes.code":"CODE_1" } ], [ { "extended_attributes.attribute":{"like":"%abc%" }, "extended_attributes.code":"CODE_2" } ] It'll be rewritten as: [ { 'extended_attributes_CODE_1.attribute' => { 'like' => '%123%' }, 'extended_attributes_CODE_1.code' => 'CODE_1' } ], [ { 'extended_attributes_CODE_2.attribute' => { 'like' => '%abc%' }, 'extended_attributes_CODE_2.code' => 'CODE_2' } ] =cut sub _rewrite_related_metadata_query { my ( $params, $key, $value, @array ) = @_; if ( ref \$params->{ 'extended_attributes.' . $key } eq 'SCALAR' ) { my $old_key_value = delete $params->{ 'extended_attributes.' . $key }; my $new_key_value = "extended_attributes_$old_key_value" . "." . $key; $params->{$new_key_value} = $old_key_value; my $old_value_value = delete $params->{ 'extended_attributes.' . $value }; my $new_value_value = "extended_attributes_$old_key_value" . "." . $value; $params->{$new_value_value} = $old_value_value; push @array, $old_key_value; } return @array; } =head3 _build_extended_attributes_relations Method to dynamically add has_many relations for Koha classes that support extended_attributes. Returns a list of relation accessor names. =cut sub _build_extended_attributes_relations { my ( $self, $types ) = @_; return if ( !grep ( /extended_attributes/, keys %{ $self->_resultset->result_source->_relationships } ) ); my $ea_config = $self->extended_attributes_config; my $result_source = $self->_resultset->result_source; for my $type ( @{$types} ) { $result_source->add_relationship( "extended_attributes_$type", "$ea_config->{schema_class}", sub { my $args = shift; return { "$args->{foreign_alias}.$ea_config->{id_field}->{foreign}" => { -ident => "$args->{self_alias}.$ea_config->{id_field}->{self}" }, "$args->{foreign_alias}.$ea_config->{key_field}" => { '=', $type }, }; }, { accessor => 'multi', join_type => 'LEFT', cascade_copy => 0, cascade_delete => 0, is_depends_on => 0 }, ); } return map { 'extended_attributes_' . $_ } @{$types}; } 1;