2013-06-04 2 views
2

좋아요, 저는 DBIx :: Class를 처음 접했습니다. 나는 다음과 같이 일대 다 관계를 설정했습니다 :Perl : DBIx :: Class Beginner - 서브 세트 관계 및 프리 페치

User -> has_many -> Addresses 

좋습니다. 나는 쿼리를 할 수 있고 다음과 같이 JOIN 된 테이블을 프리 페칭이라고 부른다 :

Foo::DBIC->storage->debug(1); # output SQL to STDOUT 

my $user = Foo::DBIC->resultset('Users')->search({}, { 
    prefetch => [ 'addresses' ], 
    join  => [ 'addresses' ], 
    rows  => 1 
})->single; 

for my $address ($user->addresses->all) { 
    say $address->zip_code; 
} 

두 개의 테이블, 하나의 SQL 쿼리 (디버그를 통해 검증 됨). 모든 것이 잘됩니다.

그러나 이제 특정 조건에 따라 하위 집합을 반환하는 Foo :: DBIC :: Result :: Users에 두 가지 오버로드 메서드를 작성하려고한다고 가정 해 봅시다. 여기에이 사용자 클래스에 추가 한 내용은 다음과 같습니다 내가 지금 같은이 오버로드를 호출 할 수 있습니다

sub home_addresses { 
    my $self = shift; 

    return $self->search_related('addresses', { address_type => 'home' }); 
} 

sub business_addresses { 
    my $self = shift; 

    return $self->search_related('addresses', { address_type => 'business' }); 
} 

, 그들은 작동 :

for my $address ($user->home_addresses->all) { 
    say $address->zip_code; 
} 

그러나,이 내가 프리 페치했다고 사실을 무시 내 조인, 그리고 추가 쿼리를 수행합니다 (마치 내가 프리 페치하고 아무것도 참가하지 않은 것처럼).

그럼, 내 질문은 : 관련 테이블의 하위 집합을 반환하지만 이미 prefetched 조인을 사용하는 오버로드 메서드는 어떻게 정의합니까? (단지 preheetch에 WHERE 절을 추가) ...

내 문제는 관련된 테이블 하위 집합을 반환하는 많은 오버로드 된 메서드가있는 경우 쿼리 수가 폭발 할 수 있습니다. 특히 루프 내에서 호출하는 경우.

나는 이것을하는 이유가 있습니다. 물론 추한 것입니다. 내 실생활 스키마는 사용자와 주소보다 훨씬 많이, 많이, 많이 엉망이며, 나는 가능한 한 가장 추악한 것을 추상화하려고 노력하고있다.

감사합니다.

+0

데이터베이스를 다시 쿼리하지 않으려 고하므로 DBIC 메서드를 호출하는 이유는 무엇입니까? 이득 쿼리를 얻지 않고, 펄 함수 (예 : grep)로 반환 된 데이터를 필터링하는 메서드를 작성하면됩니다. – MkV

+0

왜 내가 오버로드 된 메서드라고 부르는지 궁금합니다. Perl에서 오버로드하는 것은 예를 들어 객체가 숫자로 반환되어야하는 것을 정의 할 때입니다. –

+0

나는 단어를 빨아 때문에. "열 접근 자 오버로드"라는 측면에서 생각하고 있었지만 실제로는 어쨌든 새로운 접근 자 메서드를 만들었습니다. 어쨌든, 당신은 내가 무엇을 의미하는지 알았습니다. –

답변

0

뭔가가 작동하지 않을 수 있습니다 :

sub home_addresses { 
    my $self = shift; 
    my $addresses = $self->addresses; 
    my $home_addresses; 
    while (my $row = $addresses->next()) { 
    push @$home_addresses, $row if $row->address_type() eq 'home'; 
    } 
    my $home_rs = $addresses->result_source->resultset; 
    $home_rs->set_cache($home_addresses); 
    $home_rs; 
} 

다른 방법으로, 만약이 같은 주소 유형 뭔가 많은 :

sub addresses_by_type { 
    my $self = shift; 
    my $addresses = $self->addresses; 
    my $type; 
    my $rs_type; 
    while (my $row = $addresses->next()) { 
    push @{$type->{"".$row->address_type}}, 
     $row; 
    } 
    for (keys %$type) { 
    my $new_rs = $addresses->result_source->resultset; 
    $new_rs->set_cache($type->{$_}); 
    $rs_type->{$_} = $new_rs 
    } 
    return $rs_type 
} 

다음과 같이 '집'주소에 액세스 할 수 있습니다 :

while (my $r = $user->next) { 
    use Data::Dumper; 
    local $Data::Dumper::Maxdepth = 2; 
    print $r->username,"\n"; 
    my $d = $r->addresses_by_type(); 
    my $a = $d->{home}; 
    while (defined $a and my $ar = $a->next) { 
    print $ar->address,"\n"; 
    } 
} 
+0

이것은 트릭을 만들었습니다. 고맙습니다! –

+0

@MkV result_source-> resultset가 $ addresses ($ self-> addresses)에서 액세스되기 때문에 set_cache는 $ self-> addresses를 변경하는 부작용이 있습니까? – melutovich

-1

당신은 이런 식으로 뭔가를 시도 할 수 : home_addresses이 같은

sub home_addresses { 
    my $self = shift; 
    my $return = []; 
    my @addresses = $self->addresses->all(); 
    foreach my $row (@addresses) { 
    push @$return, $row if $row->address_type() eq 'home'; 
    } 

    return $return; 
} 
+0

$ self-> 주소를 임시로 먼저 할당하여 영원히 반복하지 않아도됩니까? 기본적으로 루프의 각 반복에서 조인 된 주소 테이블에서 첫 번째 레코드를 계속 가져옵니다. 대신 * my $ ar = $ self-> 주소; while (my $ row = $ ar-> next()) {* – MkV

+0

당신이 맞습니다.나는 서브 -를 모두 사용하도록 업데이트했습니다. – user353255

+0

이것은 거의 끝났지 만 위의 MkV의 대답은 ResultSet과 함께 제공되는 모든 메소드를 반환합니다. 감사! –