Perl: DBIx :: Class Beginner - Подмножество отношений и предварительная выборка

Хорошо, я новичок в DBIx :: Class. У меня настроены отношения "один ко многим", например:

User -> has_many -> Addresses

Ладно, хорошо. Я могу сделать запрос и называть его предварительной выборкой JOINed таблиц, например:

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, которые возвращают подмножество адресов на основе определенных критериев. Вот что я добавил в класс 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;
}

Однако это игнорирует тот факт, что я предварительно выбрал свое соединение, и выполняет ДОПОЛНИТЕЛЬНЫЕ ЗАПРОСЫ (как если бы я не выполнял предварительную выборку и не присоединялся к чему-либо).

Итак, мой вопрос заключается в следующем: как мне определить метод перегрузки, который возвращает подмножество связанной таблицы, но использует уже предварительно загруженное соединение? (просто добавляя предложение WHERE к предварительной выборке) ...

Моя проблема в том, что если у меня есть много перегруженных методов, возвращающих связанные подмножества таблиц, количество запросов может резко увеличиться; особенно если я вызываю их из цикла.

У меня для этого есть причины, конечно, безобразные. Моя реальная жизненная схема намного, намного, намного запутаннее, чем Пользователи и Адреса, и я пытаюсь абстрагироваться от уродства, насколько могу.

Спасибо!


person Joshua Pruitt    schedule 04.06.2013    source источник
comment
Поскольку вы не хотите повторно запрашивать базу данных, почему вы вызываете методы DBIC? Вы не получите никаких преимуществ с помощью запросов, просто напишите методы, которые фильтруют возвращаемые данные с помощью функций Perl (например, grep).   -  person MkV    schedule 05.06.2013
comment
Интересно, почему вы называете их перегруженными методами? Перегрузка в Perl - это когда вы, например, определяете, что объект должен возвращать в числовом контексте.   -  person Alexander Hartmaier    schedule 07.06.2013
comment
Потому что я плохо разбираюсь в словах. Я думал о перегрузке средства доступа к столбцу, но на самом деле я все равно просто создаю новый метод доступа. Как бы то ни было, вы знали, что я имел в виду.   -  person Joshua Pruitt    schedule 08.06.2013


Ответы (2)


что-то вроде этого для home_addresses может работать:

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";
  }
}
person MkV    schedule 05.06.2013
comment
@MkV Поскольку доступ к result_source- ›к набору результатов осуществляется по $ адресам ($ self-› адреса), будет ли set_cache иметь побочный эффект изменения $ self- ›адресов? - person melutovich; 30.07.2016

Не могли бы вы попробовать что-то вроде этого:

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;
}
person user353255    schedule 05.06.2013
comment
Разве вам не нужно сначала назначать $ self- адреса временным, чтобы они не зацикливались вечно? Обычно он будет получать первую запись из объединенной таблицы адресов на каждой итерации цикла. вместо этого мои $ ar = $ self- ›адреса; while (my $ row = $ ar- ›next ()) { - person MkV; 05.06.2013
comment
Это было близко, но ответ MkV выше возвращает ResultSet со всеми методами, которые идут с ним. Спасибо! - person Joshua Pruitt; 05.06.2013