понедельник, 14 февраля 2011 г.

Прелести Цепочек (chain) в фреймворке Catalyst

Цепочки в Catalyst это последовательность методов, позволяющая работать с сотавными урлами вида
 
/somewhere/*/somewhat/new
/somewhere/*/somewhat/*/edit
/somewhere/*/somewhat/list
/somewhere/*/somewhat/*/delete

Удобно применять для набора взаимосвязанных объектов. Например для админки и CRUD операций над объектами которыми нужно управлять.

Допустим в нашей предполагаемой админке нужно управлять объектом "новости". Т.е. отображать список новостей, уметь создавать новую запись, редактировать её и удалять.

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

Итак... допустим, что по таким адресам будет проходить обработка списка и создания новой записи

/admin/news/list
/admin/news/new

а по таким адресам мы будем редактировать запись и удалять её

/admin/news/*/edit
/admin/news/*/delete

Примерно вот так будет выглядеть наш контроллер для работы с новостями.

package MyApp::Controller::Admin::News;
use Moose;
use namespace::autoclean;

BEGIN {extends 'Catalyst::Controller'; }

# начало цепочки
sub admin_base_chain :Chained('') :PathPart('admin') :CaptureArgs(0) {
    my ( $self, $c ) = @_;
}

# продолжение цепочки для адресов у которых после /admin/news 
# не следует идентификатор новости
sub chain_1 :Chained('admin_base_chain') :PathPart('news') :CaptureArgs(0) {
    my ( $self, $c ) = @_;
}

# вариант цепочки для адресов у которых после /admin/news/ 
# идёт идентификатор новости
sub chain_2 :Chained('admin_base_chain') :PathPart('news') :CaptureArgs(1) {
    my ( $self, $c, $news_id ) = @_;
    
    # тут следует загрузить объект по идентификатору $obj_id полученому из урла
    # ... и для всех последующих методов цепочки загруженный объект будет доступен 
    # в $c->stash->{my_obj}
    $c->stash->{news} = $c->model('DB::News')->find($news_id);
}

sub news_list :Chained('chain_1') :PathPart('list') :Args(0) {
    my ( $self, $c ) = @_;
    
    # сформировать список
}

sub news_new :Chained('chain_1') :PathPart('new') :Args(0) {
    my ( $self, $c ) = @_;
    
    # отобразить форму для создания новости и 
    # обработать полученный от формы результат
}

sub news_edit :Chained('chain_2') :PathPart('edit') :Args(0) {
    my ( $self, $c ) = @_;

    # объект у нас уже загружен
    my $news = $c->stash->{news};

    # отобразить форму для редактирования новости и 
    # обработать полученный от формы результат
}

sub news_delete :Chained('chain_2') :PathPart('delete') :Args(0) {
    my ( $self, $c ) = @_;

    # объект уже загружен в предыдущем звене цепочки
    my $news = $c->stash->{news};
    
    # тут нужно обработать удаление формы
}

__PACKAGE__->meta->make_immutable;

1;

Url разбирается последовательно от начала к концу. Адрес вида /admin/news/*/edit при разборе последовательно пройдёт через sub admin_base_chain {} потом через sub chain_2 {} и sub news_edit {}

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

Второй уровень цепочек - chain_1 и chain_2 относится уже непосредственно к объектам над которыми производятся манипуляции. Тут можно подгрузить объект по его id

Третий уровень - это уже непосредственно управляющие манипуляция с объектами с предыдущих уровней.

Вот так. В целом очень удобная и полезная вещь.

Комментариев нет:

Отправить комментарий