ВыходВход

Меню сайта

Категории статей
Теория [1]
Древовидные структуры данных. Методология, описание
Использование в SQL DB [0]
Решения используемые в SQL DB
Использование в Perl [7]
Применение технологии Nested Sets в Perl программировании
Использование в PHP [2]
Применение технологии Nested Sets в PHP программировании

Меню пользователя

Поиск по статьям

Друзья сайта

Модуль Perl - 3. Дополнительные возможности
» Каталог статей » Использование в Perl
Модуль Perl - 3. Дополнительные возможности

Модуль Perl - 3. Дополнительные возможности

6. Тюнинг

Итак, мы рассмотрели все опрерации управления деревом. Но раз уж модуль у нас управляет, то неплохо бы было научить его пользоваться деревьями. Просматривая модуль DBIx::Tree::NestedSet (CPAN), честно говоря, увидел много бесполезных методов, и мало полезных. но попробуем определить, какие методы для работы нам понадобятся, а какие нет. Анализируя методы, я исхожу сугубо из своего опыта, только то, что я действительно использую. Многие методы из вышесказанного модуля, я даже не беру в оборот, т.к. либо для них есть уже замена, либо смысла в них не вижу никакого, тем более, хаить этот модуль, я в коей мере не собираюсь, так как у меня есть свой ;-).

  •     
    Создание таблицы - весьма бесполезный метод, просмотрев несколько своих таблиц хранящих деревья, понял, что общее в них - только поля относящиеся с структуре дерева. Тем более таблицы создаются весьма редко, а так же один раз и навсегда;
  •     
    Редактирование узла - тоже бесполезный метод. В модуле подразумевается, только то, что есть дополнительное поле name, и все, соответсвенно и редактировать можем мы только его (у меня, как минимум, всегда есть поле note);
  •     
    Выборка данных отдельного узла - весьма сомнительный метод, если мы знаем идентификатор узла, то выбрать данные, дело одного простейшего запроса, но особо ленивые могут для себя описать и его;
  •     
    Выборка родительской ветки (родительского узла) - вот этот метод нужен. Определить родительский узел для текущего узла можно в один запрос, но не совсем простой, и собирать его каждый раз в скрипте - не самое любимое мое занятие. Но остается определить какие данные модуль будет нам возвращать:
    • список идентификаторов узлов родительской ветки - возвращается обычный массив который подставляется в простой запрос:         'SELECT * FROM catalog_category WHERE id IN('.join("','",@array).') ORDER BY left_key'
    • результат запроса в виде обычного массива, элементы которого - ссылки на хеши и данными об узле;
  •     
    Выборка подчиненных узлов - этот метод тоже нужен, причем, как выборка узлов непосредственного подчинения (на уровень ниже), так и всех подчиненных ветвей. Возвращаемые данные - такие же как и при выборке родительской ветки.
  •     
    Статус дерева, она же проверка - можно включить этот метод, на всякий случай (хотя я не помню, когда у меня "рушилась" целостность дерева из-за этого модуля);

6.1. Родительская ветка, родительский узел

6.1.1. Возврат идентификаторов родительской ветки:

Смысл один, либо мы дерем только родителя, либо всю родительскую ветку целиком:

sub get_parent_id {
# Получаем данные: объект, параметры 
    my ($self, %common)= @_;
# перемещаемый узел
    my $unit = $common{'unit'} || undef;
# что возвращаем
    my $branch = $common{'branch'} || undef;
# объявляем переменную, массив идентификаторов
    my @data;
# определяем данные узла
    if ($unit) {$self = &select_unit($self, $unit)}
    elsif (!$self->{'unit'}->{'id'}) {croak("NestedSets failed: Your must first select unit, for using it!!!")}
# определяем, есть ли подчиненные узлы
    unless ($self->{'unit'}->{'level'} > 1) {return ['root']}
# Производим выборку ветви
    my $sql = 'SELECT '.$self->{'id'}.' FROM '.$self->{'table'}.
              ' WHERE '.
                $self->{'left'}.' < '.$self->{'unit'}->{'left'}.' AND '.
                $self->{'right'}.' > '.$self->{'unit'}->{'right'}.
# Если мультидерево, ограничение
                ($self->{'type'} eq 'M' ? 
                 ' AND '.$self->{'multi'}.' = \''.$self->{'unit'}->{'multi'}.'\'' : '').
# Вся ветвь или непосредственный родитель
               ($branch && $branch eq 'all' ? 
                ' ORDER BY '.$self->{'left'} :
                ' ORDER BY '.$self->{'left'}.' DESC LIMIT 1');
    my $sth = $self->{'DBI'}->prepare($sql); $sth->execute();
# Формируем массив
    while (my @row = $sth->fetchrow_array()) {push @data, $row[0]}
    $sth->finish();
# Возвращаем массив
    return \@data
}

Хочу обратить внимание, что в разпросе используется оператор LIMIT, что может привести к некоторому ограничению использования определенных SQL баз данных. Так же при отсутсвии родительских узлов возвращается ссылка на массив с одним элементом (['root']) для того, что бы не возникало ошибки, во время выборки родителей узла находящегося в корне.

Вызов данного метода:

...
my $unit = ... # Определяем идентификатор узла 
... 
use MyModule::NestedSets;
my $nested = new MyModule::NestedSets {table=>'catalog_category', type=>'M', DBI=>$dbh};
my $parents = $nested->get_parent_id(unit=>$unit, branch=>'all');
... 

Так же хочу обратить внимание, что возвращается не массив, а только ссылка на него, если мы хотим получить сразу массив, то можно просто его сразу разименовать (хотя, честно говоря, смысла в этом - никакого):

... 
    my @parents = @{$nested->get_parent_id(unit=>$unit, branch=>'all')};
... 

6.1.2. Возврат родительской ветки в виде массива:

Этот метод вызывает у меня лично противоречивые чувства, так как он может вернуть относительно большой объем данных (что может привести не рациональному использованию памяти), но с другой стороны полностью берет на себя работу с базой. В общем - на любителя. Итак, в каком виде должны вернуться данные - обычный массив элементы которого ссылки на хеш массивы для каждого узла дерева. Почему обычный массив - да что бы не "париться" с сортировкой хеша, так как сортировка у нас всегда производится по левому ключу узла, то особых сложностей с ней возникнуть не должно. Так же немаловажным является то, какие данные будут выбираться: естественно - данные структуры дерева (идентификатор, ключи, уровень), а так же дополнительные поля таблицы. Изначально, я дополнительные поля таблицы передавал в виде массива, но часто бывает так, что требуется указать псевдомимы для полей или призвести какие либо действия над ними, поэтому я предаю дополнительные поля, как часть запроса находящуюся между оператором SELECT и FROM.

sub get_parent_in_array {
# Получаем данные: объект, перемещаемый узел, место перемещения, порядок перемещения 
    my ($self, %common)= @_;
# перемещаемый узел
    my $unit = $common{'unit'} || undef;
# что возвращаем 
    my $branch = $common{'branch'} || undef;
# дополнительные поля запроса
    my $field = $common{'field'} || undef;
# если выбираем все поля 
    $field = $self->{'table'}.'.*' if $field =~ /\*/;
# объявляем переменную, массив идентификаторов
    my @data;
# определяем данные узла
    if ($unit) {$self = &select_unit($self, $unit)}
    elsif (!$self->{'unit'}->{'id'}) {croak("NestedSets failed: Your must first select unit, for using it!!!")}
# определяем, есть ли подчиненные узлы
    unless ($self->{'unit'}->{'level'} > 1) {return [{id=>'root'}]}
# Производим выборку ветви
    my $sql = 'SELECT '.$self->{'id'}.', '.$self->{'left'}.', '.$self->{'right'}.', '.$self->{'level'}.
                ($field ? ', '.$field : '').
              ' FROM '.$self->{'table'}.
              ' WHERE '.
                $self->{'left'}.' < '.$self->{'unit'}->{'left'}.' AND '.
                $self->{'right'}.' > '.$self->{'unit'}->{'right'}.
# Если мультидерево, ограничение
                ($self->{'type'} eq 'M' ? 
                 ' AND '.$self->{'multi'}.' = \''.$self->{'unit'}->{'multi'}.'\'' : '').
# Вся ветвь или непосредственный родитель
              ($branch && $branch eq 'all' ?
               ' ORDER BY '.$self->{'left'} :
               ' ORDER BY '.$self->{'level'}.' DESC LIMIT 1');
    my $sth = $self->{'DBI'}->prepare($sql); $sth->execute();
    while (my $row = $sth->fetchrow_hashref()) {push @data, $row}
    $sth->finish();
# возвращаем массив
    return \@data
}

Как видим этот метод тоже не особо отличается от предыдущего, правда, данные возвращаются в ином виде:

@array = (
          {field1 => 'value1_1',
           field2 => 'value1_2',
           field3 => 'value1_3',
           ...},
          {field1 => 'value2_1',
           field2 => 'value2_2',
           field3 => 'value2_3',
           ...},
          {field1 => 'value3_1',
           field2 => 'value3_2',
           field3 => 'value3_3',
           ...},
           ...
         )

Вызов метода, как всегда:

...
    my $unit = ... # Определяем идентификатор узла 
... 
use MyModule::NestedSets;
my $nested = new MyModule::NestedSets {table=>'catalog_category', type=>'M', DBI=>$dbh};
my $parents = $nested->get_parent_in_array(unit=>$unit, branch=>'all', field=>'name AS name_field, note');
... 

6.2. Подчиненые узлы

Выборка подчиненных узлов кардинально ничем не отличается от выборки родительских. Основное отличие только лишь в том, что добавляется еще одно дополнительное условие относительно уровней узлов, а так же отсутсвием оператора LIMIT, так как подчиенных узлов на уровне может быть несколько, когда родительских - только один. Поэтому, особенно описывать и нечего.

6.2.1. Возврат идентификаторов подчиненных узлов:

Код:

sub get_child_id {
# Получаем данные: объект, перемещаемый узел, место перемещения, порядок перемещения 
    my ($self, %common)= @_;
# перемещаемый узел
    my $unit = $common{'unit'} || undef;
# что выбираем
    my $branch = $common{'branch'} || undef;
# объявляем переменную, массив идентификаторов
    my @data;
# определяем данные узла
    if ($unit) {$self = &select_unit($self, $unit)}
    elsif (!$self->{'unit'}->{'id'}) {$unit = 'root'; $self->{'unit'}->{'level'} = '0'}
# определяем, есть ли подчиненные узлы
    unless ($unit eq 'root' || $self->{'unit'}->{'right'} - $self->{'unit'}->{'left'} > 1) {return ['none']}
# Производим выборку ветви
    my $sql = 'SELECT '.$self->{'id'}.' FROM '.$self->{'table'}.
              ' WHERE '.
                $self->{'left'}.' > '.$self->{'unit'}->{'left'}.' AND '.
                $self->{'right'}.' < '.$self->{'unit'}->{'right'}.
# Если мультидерево, ограничение
                ($self->{'type'} eq 'M' ? 
                 ' AND '.$self->{'multi'}.' = \''.$self->{'unit'}->{'multi'}.'\'' : '').
# Вся ветвь или непосредственный родитель
              ($branch && $branch eq 'all' ?
               ' ORDER BY '.$self->{'left'} :
               ' AND '.$self->{'level'}.' = \''.$self->{'unit'}->{'level'}.' + 1 ORDER BY '.$self->{'left'});
    my $sth = $self->{'DBI'}->prepare($sql); $sth->execute();
    while (my @row = $sth->fetchrow_array()) {push @data, $row[0]}
    $sth->finish();
# возвращаем массив
    return \@data
}

Вызов:

...
    my $unit = ... # Определяем идентификатор узла 
... 
use MyModule::NestedSets;
my $nested = new MyModule::NestedSets {table=>'catalog_category', type=>'M', DBI=>$dbh};
my $child = $nested->get_child_id(unit=>$unit, branch=>'all');
...

6.2.2. Возврат подчиненных узлов в виде массива:

Код:

sub get_child_in_array {
# Получаем данные: объект, перемещаемый узел, место перемещения, порядок перемещения 
    my ($self, %common)= @_;
# перемещаемый узел
    my $unit = $common{'unit'} || undef;
# что выбираем
    my $branch = $common{'branch'} || undef;
# дополнительные поля запроса
    my $field = $common{'field'} || undef;
# если выбираем все поля 
    $field = $self->{'table'}.'.*' if $field =~ /\*/;
# объявляем переменную, массив идентификаторов
    my @data;
# определяем данные узла
    if ($unit) {$self = &select_unit($self, $unit)}
    elsif (!$self->{'unit'}->{'id'}) {$unit = 'root'; $self->{'unit'}->{'level'} = '0'}
# определяем, есть ли подчиненные узлы
    unless ($unit eq 'root' || $self->{'unit'}->{'right'} - $self->{'unit'}->{'left'} > 1) {return [{id=>'none'}]}
# Производим выборку ветви
    my $sql = 'SELECT '.$self->{'id'}.', '.$self->{'left'}.', '.$self->{'right'}.', '.$self->{'level'}.
                ($field ? ', '.$field : '').
              ' FROM '.$self->{'table'}.
              ' WHERE '.
                $self->{'left'}.' > '.$self->{'unit'}->{'left'}.' AND '.
                $self->{'right'}.' < '.$self->{'unit'}->{'right'}.
# Если мультидерево, ограничение
                ($self->{'type'} eq 'M' ? 
                 ' AND '.$self->{'multi'}.' = \''.$self->{'unit'}->{'multi'}.'\'' : '').
# Вся ветвь или непосредственный родитель
              ($branch && $branch eq 'all' ?
               ' ORDER BY '.$self->{'left'} :
               ' AND '.$self->{'level'}.' = \''.$self->{'unit'}->{'level'}.' + 1 ORDER BY '.$self->{'left'});
    my $sth = $self->{'DBI'}->prepare($sql); $sth->execute();
    while (my $row = $sth->fetchrow_hashref()) {push @data, $row}
    $sth->finish();
# возвращаем массив
    return \@data
}

Вызов:

...
    my $unit = ... # Определяем идентификатор узла 
... 
use MyModule::NestedSets;
my $nested = new MyModule::NestedSets {table=>'catalog_category', type=>'M', DBI=>$dbh};
my $child = $nested->get_parent_in_array(unit=>$unit, branch=>'all', field=>'name AS name_field, note');
...
Категория: Использование в Perl | Добавил: phoinix (2005-10-31) | Автор: Сергей Томулевич (aka Phoinix)
Просмотров: 1883 | Рейтинг: 0.0 |

Комментарии

 

Бесплатный конструктор сайтов - uCoz