View | Details | Raw Unified | Return to bug 37389
Collapse All | Expand All

(-)a/Koha/ILL/Requests.pm (-1 / +15 lines)
Lines 22-29 use Modern::Perl; Link Here
22
use Koha::Database;
22
use Koha::Database;
23
use Koha::ILL::Request;
23
use Koha::ILL::Request;
24
use Koha::ILL::Request::Config;
24
use Koha::ILL::Request::Config;
25
use Koha::Objects::Mixin::ExtendedAttributes;
25
26
26
use base qw(Koha::Objects);
27
use base qw(Koha::Objects::Mixin::ExtendedAttributes Koha::Objects);
27
28
28
=head1 NAME
29
=head1 NAME
29
30
Lines 106-111 sub search_incomplete { Link Here
106
    } );
107
    } );
107
}
108
}
108
109
110
=head3 extended_attributes_config
111
112
=cut
113
114
sub extended_attributes_config {
115
    my ( $self ) = @_;
116
    return {
117
        'id_field'     => { 'foreign' => 'illrequest_id', 'self' => 'illrequest_id' },
118
        'key_field'    => 'type',
119
        'schema_class' => 'Koha::Schema::Result::Illrequestattribute',
120
    };
121
}
122
109
=head2 Internal methods
123
=head2 Internal methods
110
124
111
=head3 _type
125
=head3 _type
(-)a/Koha/Objects/Mixin/ExtendedAttributes.pm (+234 lines)
Line 0 Link Here
1
package Koha::Objects::Mixin::ExtendedAttributes;
2
3
# Copyright 2024 PTFS Europe Ltd
4
#
5
# This file is part of Koha.
6
#
7
# Koha is free software; you can redistribute it and/or modify it
8
# under the terms of the GNU General Public License as published by
9
# the Free Software Foundation; either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# Koha is distributed in the hope that it will be useful, but
13
# WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with Koha; if not, see <http://www.gnu.org/licenses>.
19
20
use Modern::Perl;
21
use Scalar::Util qw( reftype );
22
23
=head1 NAME
24
25
Koha::Objects::Mixin::ExtendedAttributes
26
27
=head2 Class methods
28
29
30
=head3 search
31
32
    Overwrites the search method to include extended attributes rewrite and dynamic relation accessors
33
34
=cut
35
36
sub search {
37
    my ( $self, $params, $attributes ) = @_;
38
39
    my $class = ref($self) ? ref($self) : $self;
40
41
    $self->handle_query_extended_attributes(
42
        {
43
            attributes      => $attributes,
44
            filtered_params => $params,
45
        }
46
    );
47
48
    my $rs = $self->_resultset()->search( $params, $attributes );
49
    return $class->_new_from_dbic($rs);
50
}
51
52
=head3 handle_query_extended_attributes
53
54
    Checks for the presence of extended_attributes in a query
55
    If present, it builds the dynamic extended attributes relations and rewrites the query to include the extended_attributes relation
56
57
=cut
58
59
sub handle_query_extended_attributes {
60
    my ( $self, $args ) = @_;
61
62
    my $attributes      = $args->{attributes};
63
    my $filtered_params = $args->{filtered_params};
64
65
    if (   reftype( $attributes->{prefetch} )
66
        && reftype( $attributes->{prefetch} ) eq 'ARRAY'
67
        && grep ( /extended_attributes/, @{ $attributes->{prefetch} } ) )
68
    {
69
70
        my @array = $self->_get_extended_attributes_entries($filtered_params);
71
72
        # Calling our private method to build the extended attributes relations
73
        my @joins = $self->_build_extended_attributes_relations( \@array );
74
        push @{ $attributes->{join} }, @joins;
75
76
    }
77
}
78
79
=head3 _get_extended_attributes_entries
80
81
    $self->_get_extended_attributes_entries( $filtered_params, 0 )
82
83
Recursive function that returns the rewritten extended_attributes query entries.
84
85
Given:
86
[
87
    '-and',
88
    [
89
        {
90
            'extended_attributes.code'      => 'CODE_1',
91
            'extended_attributes.attribute' => { 'like' => '%Bar%' }
92
        },
93
        {
94
            'extended_attributes.attribute' => { 'like' => '%Bar%' },
95
            'extended_attributes.code'      => 'CODE_2'
96
        }
97
    ]
98
];
99
100
Returns :
101
102
[
103
    'CODE_1',
104
    'CODE_2'
105
]
106
107
=cut
108
109
sub _get_extended_attributes_entries {
110
    my ( $self, $params, @array ) = @_;
111
112
    if ( reftype($params) && reftype($params) eq 'HASH' ) {
113
114
        # rewrite additional_field_values table query params
115
        @array = _rewrite_related_metadata_query( $params, 'field_id', 'value', @array )
116
            if $params->{'extended_attributes.field_id'};
117
118
        # rewrite borrower_attributes table query params
119
        @array = _rewrite_related_metadata_query( $params, 'code', 'attribute', @array )
120
            if $params->{'extended_attributes.code'};
121
122
        # rewrite illrequestattributes table query params
123
        @array = _rewrite_related_metadata_query( $params, 'type', 'value', @array )
124
            if $params->{'extended_attributes.type'};
125
126
        foreach my $key ( keys %{$params} ) {
127
            return $self->_get_extended_attributes_entries( $params->{$key}, @array );
128
        }
129
    } elsif ( reftype($params) && reftype($params) eq 'ARRAY' ) {
130
        foreach my $ea_instance (@$params) {
131
            @array = $self->_get_extended_attributes_entries( $ea_instance, @array );
132
        }
133
        return @array;
134
    } else {
135
        return @array;
136
    }
137
}
138
139
=head3 _rewrite_related_metadata_query
140
141
        $extended_attributes_entries =
142
            _rewrite_related_metadata_query( $params, 'field_id', 'value', @array )
143
144
Helper function that rewrites all subsequent extended_attributes queries to match the alias generated by the dbic self left join
145
Take the below example (patrons):
146
        [
147
            {
148
                "extended_attributes.attribute":{"like":"%123%"},
149
                "extended_attributes.code":"CODE_1"
150
            }
151
        ],
152
        [
153
            {
154
                "extended_attributes.attribute":{"like":"%abc%" },
155
                "extended_attributes.code":"CODE_2"
156
            }
157
        ]
158
159
It'll be rewritten as:
160
        [
161
            {
162
                'extended_attributes_CODE_1.attribute' => { 'like' => '%123%' },
163
                'extended_attributes_CODE_1.code' => 'CODE_1'
164
            }
165
        ],
166
            [
167
            {
168
                'extended_attributes_CODE_2.attribute' => { 'like' => '%abc%' },
169
                'extended_attributes_CODE_2.code' => 'CODE_2'
170
            }
171
        ]
172
173
=cut
174
175
sub _rewrite_related_metadata_query {
176
    my ( $params, $key, $value, @array ) = @_;
177
178
    if ( ref \$params->{ 'extended_attributes.' . $key } eq 'SCALAR' ) {
179
        my $old_key_value = delete $params->{ 'extended_attributes.' . $key };
180
        my $new_key_value = "extended_attributes_$old_key_value" . "." . $key;
181
        $params->{$new_key_value} = $old_key_value;
182
183
        my $old_value_value = delete $params->{ 'extended_attributes.' . $value };
184
        my $new_value_value = "extended_attributes_$old_key_value" . "." . $value;
185
        $params->{$new_value_value} = $old_value_value;
186
        push @array, $old_key_value;
187
    }
188
189
    return @array;
190
}
191
192
=head3 _build_extended_attributes_relations
193
194
    Method to dynamically add has_many relations for Koha classes that support extended_attributes.
195
196
    Returns a list of relation accessor names.
197
198
=cut
199
200
sub _build_extended_attributes_relations {
201
    my ( $self, $types ) = @_;
202
203
    return if( !grep ( /extended_attributes/, keys %{$self->_resultset->result_source->_relationships} ) );
204
205
    my $ea_config = $self->extended_attributes_config;
206
207
    my $result_source = $self->_resultset->result_source;
208
    for my $type ( @{$types} ) {
209
        $result_source->add_relationship(
210
            "extended_attributes_$type",
211
            "$ea_config->{schema_class}",
212
            sub {
213
                my $args = shift;
214
215
                return {
216
                    "$args->{foreign_alias}.$ea_config->{id_field}->{foreign}" =>
217
                        { -ident => "$args->{self_alias}.$ea_config->{id_field}->{self}" },
218
                    "$args->{foreign_alias}.$ea_config->{key_field}" => { '=', $type },
219
                };
220
            },
221
            {
222
                accessor       => 'multi',
223
                join_type      => 'LEFT',
224
                cascade_copy   => 0,
225
                cascade_delete => 0,
226
                is_depends_on  => 0
227
            },
228
        );
229
230
    }
231
    return map { 'extended_attributes_' . $_ } @{$types};
232
}
233
234
1;
(-)a/Koha/Patrons.pm (-1 / +14 lines)
Lines 29-35 use Koha::Patron; Link Here
29
use Koha::Exceptions::Patron;
29
use Koha::Exceptions::Patron;
30
use Koha::Patron::Categories;
30
use Koha::Patron::Categories;
31
31
32
use base qw(Koha::Objects);
32
use base qw(Koha::Objects::Mixin::ExtendedAttributes Koha::Objects);
33
33
34
=head1 NAME
34
=head1 NAME
35
35
Lines 566-571 sub filter_by_have_permission { Link Here
566
    );
566
    );
567
}
567
}
568
568
569
=head3 extended_attributes_config
570
571
=cut
572
573
sub extended_attributes_config {
574
    my ( $self ) = @_;
575
    return {
576
        'id_field'     => { 'foreign' => 'borrowernumber', 'self' => 'borrowernumber' },
577
        'key_field'    => 'code',
578
        'schema_class' => 'Koha::Schema::Result::BorrowerAttribute',
579
    };
580
}
581
569
=head3 _type
582
=head3 _type
570
583
571
=cut
584
=cut
(-)a/Koha/REST/Plugin/Objects.pm (-8 lines)
Lines 287-300 controller, and thus shouldn't be called twice in it. Link Here
287
            $c->stash('koha.pagination.base_total'   => $result_set->count);
287
            $c->stash('koha.pagination.base_total'   => $result_set->count);
288
            $c->stash('koha.pagination.query_params' => $args);
288
            $c->stash('koha.pagination.query_params' => $args);
289
289
290
            # Check and handle related metadata table joins
291
            $c->dbic_extended_attributes_join(
292
                {
293
                    attributes      => $attributes,
294
                    filtered_params => $filtered_params
295
                }
296
            );
297
298
            # Generate the resultset
290
            # Generate the resultset
299
            my $objects_rs = $result_set->search( $filtered_params, $attributes );
291
            my $objects_rs = $result_set->search( $filtered_params, $attributes );
300
            # Stash the page total if requires, total otherwise
292
            # Stash the page total if requires, total otherwise
(-)a/Koha/REST/Plugin/Query.pm (-154 lines)
Lines 159-192 Generates the DBIC prefetch attribute based on embedded relations, and merges in Link Here
159
        }
159
        }
160
    );
160
    );
161
161
162
=head3 dbic_extended_attributes_join
163
164
    $attributes = $c->dbic_extended_attributes_join({ attributes => $attributes, result_set => $result_set });
165
166
Generates the DBIC join attribute based on extended_attributes query entries, and merges into I<$attributes>.
167
168
=cut
169
170
    $app->helper(
171
        'dbic_extended_attributes_join' => sub {
172
            my ( $c, $args ) = @_;
173
174
            my $attributes      = $args->{attributes};
175
            my $filtered_params = $args->{filtered_params};
176
177
            if (   reftype( $attributes->{prefetch} )
178
                && reftype( $attributes->{prefetch} ) eq 'ARRAY'
179
                && grep ( /extended_attributes/, @{ $attributes->{prefetch} } ) )
180
            {
181
                my $ea_entries = $self->_get_extended_attributes_entries( $filtered_params, 0 );
182
                while ( $ea_entries > 0 ) {
183
                    push( @{ $attributes->{join} }, 'extended_attributes' );
184
                    $ea_entries--;
185
                }
186
            }
187
        }
188
    );
189
190
=head3 _build_query_params_from_api
162
=head3 _build_query_params_from_api
191
163
192
    my $params = _build_query_params_from_api( $filtered_params, $reserved_params );
164
    my $params = _build_query_params_from_api( $filtered_params, $reserved_params );
Lines 516-644 sub _parse_dbic_query { Link Here
516
    }
488
    }
517
}
489
}
518
490
519
=head3 _get_extended_attributes_entries
520
521
    $self->_get_extended_attributes_entries( $filtered_params, 0 )
522
523
Recursive function that returns the number of extended_attributes entries present in a query.
524
Example: Returns 2 if given a $filtered_params containing the below:
525
526
{
527
    "-and":[
528
        [
529
            {
530
                "extended_attributes.value":{
531
                    "like":"abc%"
532
                },
533
                "extended_attributes.code":[
534
                    [
535
                        "arbitrary_attr_code",
536
                        "another_attr_code"
537
                    ]
538
                ]
539
            }
540
        ],
541
        [
542
            {
543
                "extended_attributes.value":{
544
                    "like":"123%"
545
                },
546
                "extended_attributes.code":[
547
                    [
548
                        "arbitrary_attr_code",
549
                        "another_attr_code"
550
                    ]
551
                ]
552
            }
553
        ]
554
    ]
555
}
556
557
=cut
558
559
sub _get_extended_attributes_entries {
560
    my ( $self, $params, $extended_attributes_entries ) = @_;
561
562
    if ( reftype($params) && reftype($params) eq 'HASH' ) {
563
564
        # rewrite additional_field_values table query params
565
        $extended_attributes_entries =
566
            _rewrite_related_metadata_query( $params, $extended_attributes_entries, 'field_id', 'value' )
567
            if $params->{'extended_attributes.field_id'};
568
569
        # rewrite borrower_attributes table query params
570
        $extended_attributes_entries =
571
            _rewrite_related_metadata_query( $params, $extended_attributes_entries, 'code', 'attribute' )
572
            if $params->{'extended_attributes.code'};
573
574
        # rewrite illrequestattributes table query params
575
        $extended_attributes_entries =
576
            _rewrite_related_metadata_query( $params, $extended_attributes_entries, 'type', 'value' )
577
            if $params->{'extended_attributes.type'};
578
579
        foreach my $key ( keys %{$params} ) {
580
            return $self->_get_extended_attributes_entries( $params->{$key}, $extended_attributes_entries );
581
        }
582
    } elsif ( reftype($params) && reftype($params) eq 'ARRAY' ) {
583
        foreach my $ea_instance (@$params) {
584
            $extended_attributes_entries =
585
                +$self->_get_extended_attributes_entries( $ea_instance, $extended_attributes_entries );
586
        }
587
        return $extended_attributes_entries;
588
    } else {
589
        return $extended_attributes_entries;
590
    }
591
}
592
593
=head3 _rewrite_related_metadata_query
594
595
        $extended_attributes_entries =
596
            _rewrite_related_metadata_query( $params, $extended_attributes_entries, 'field_id', 'value' )
597
598
Helper function that rewrites all subsequent extended_attributes queries to match the alias generated by the dbic self left join
599
Take the below example (patrons), assuming it is the third instance of an extended_attributes query:
600
{
601
    "extended_attributes.value":{
602
        "like":"123%"
603
    },
604
    "extended_attributes.code":[
605
        [
606
            "arbitrary_attr_code",
607
            "another_attr_code"
608
        ]
609
    ]
610
}
611
612
It'll be rewritten as:
613
{
614
    "extended_attributes_3.value":{
615
        "like":"123%"
616
    },
617
    "extended_attributes_3.code":[
618
        [
619
            "arbitrary_attr_code",
620
            "another_attr_code"
621
        ]
622
    ]
623
}
624
625
=cut
626
627
sub _rewrite_related_metadata_query {
628
    my ( $params, $extended_attributes_entries, $key, $value ) = @_;
629
630
    $extended_attributes_entries++;
631
    if ( $extended_attributes_entries > 1 ) {
632
        my $old_key_value = delete $params->{ 'extended_attributes.' . $key };
633
        my $new_key_value = "extended_attributes_$extended_attributes_entries" . "." . $key;
634
        $params->{$new_key_value} = $old_key_value;
635
636
        my $old_value_value = delete $params->{ 'extended_attributes.' . $value };
637
        my $new_value_value = "extended_attributes_$extended_attributes_entries" . "." . $value;
638
        $params->{$new_value_value} = $old_value_value;
639
    }
640
641
    return $extended_attributes_entries;
642
}
643
644
1;
491
1;
645
- 

Return to bug 37389