Добрый вечер!Представим есть обертка шаблона.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<link rel="stylesheet" type="text/css" href="dbrm/root/styles/style.css"/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>[% page.title %]</title>
</head>
<body>
[% page.content %]
</body>
</html>
Также есть контролер page.pm
В зависимости от переменной, контролер вытаскивает данные из базы.Например страницу с id=1
my $my_content = $c->model('DBI::Content::Return')->return( '1' );
DBI::Content::Returnforeach my $content_f (@$contents) {
$keys_db->{ $content_f->{id} }->{title} = $content_f->{title};
$keys_db->{ $content_f->{id} }->{date} = $content_f->{date};
$keys_db->{ $content_f->{id} }->{published} = $content_f->{published};
$keys_db->{ $content_f->{id} }->{autor} = $content_f->{autor};
$keys_db->{ $content_f->{id} }->{content} = $content_f->{content};
}
#print Dumper $keys_db;
return \$keys_db;
Ни как не могу понять, как мне отправить данные хэша $my_content, в определенный шаблон. И уже подготовленный html код вставить в $c->page->contentПогуглив наткнулся на:
my $html = $c->view('TT')->render($c,'list_main.tt2', { result => $result, no_wrapper => 1 });
Но не могу понять как это работает.
Нашел пример:
sub email_stuff : Local {
my ($self, $c, $review_id) = @_;
$c->stash(
review_url => $c->uri_for("/thingy/${review_id}"),
status_msg => "Thank you for your feedback!",
template => 'view_all.tt2',
);
$c->email(
header => [
From => $c->config->{'from'},
To => $c->config->{'to'},
Subject => "Emaily stuff!",
],
body => $c->view('TT')->render($c, 'review.tt2'),
);
return;
}Я правильно понимаю, что линией body => $c->view('TT')->render($c, 'review.tt2'),
Мы присваиваем в $c->body шаблон review.tt2 с уже вставленными в него
header => [
From => $c->config->{'from'},
To => $c->config->{'to'},
Subject => "Emaily stuff!",
],
Если да, то помогите пожалуйста разобраться, где и как осуществляется эта самая вставка.
1) внимательно прочитай Catalyst::View::TT. В разделе RENDERING VIEWS - дурацкий выбор имени функции в первом примере, он запутывает понимание остальных примеров и не имеет к ним никакого отношения.
>[оверквотинг удален]
> <head>
> <link rel="stylesheet" type="text/css" href="dbrm/root/styles/style.css"/>
> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
> <title>[% page.title %]</title>
> </head>
> <body>
> [% page.content %]
> </body>
> </html>
> Например страницу с id=1
$c->stash->{page} = $c->model('DBI::Content::Return')->return( '1' );Всё, больше ничего не нужно, View тебе разрисует одну страницу. Хочешь [зачем-то] больше - описывай цикл в самом шаблоне.
> Ни как не могу понять, как мне отправить данные хэша $my_content, в
> определенный шаблон. И уже подготовленный html код вставить в $c->page->contentсм. 1) TT ожидает данные в $c->stash
Про Catalyst - я сначала заменил DBIx на самоделку, потом заменил TT на Xslate, потом бросил всё и ушёл на Poet::Mason. Однако Catalyst необходим, чтобы понять, как вообще писать такие прилады.
Большое спасибо за ответ.>
> $c->stash->{page} = $c->model('DBI::Content::Return')->return( '1' );
>
> Всё, больше ничего не нужно, View тебе разрисует одну страницу. Хочешь [зачем-то]
> больше - описывай цикл в самом шаблоне.Сейчас я так делаю:
$c->stash->{content} = $c->model('DBI::Content::Return')->return( $id );
$c->stash->{content} = $c->view('TT')->render( $c, 'content.html' );В итоге переменная $c->stash->{content} содержит подготовленные html с данными контента.
После сборки, все это вставляется во враппер:[% content %]
>
> $c->stash->{content} = $c->model('DBI::Content::Return')->return( $id );
> $c->stash->{content} = $c->view('TT')->render( $c, 'content.html' );
>
> В итоге переменная $c->stash->{content} содержит подготовленные html с данными контента.Это лишнее. Ты же всё равно раскатываешь для browser, пускай content.tt2 сам этим занимается.
Основная идея - насовать в хэш $c->stash объекты и значения, которые разрисует View. При этом принципиально не рендерить ничего вручную, а уж тем более в контроллере.
Водку с портвейном можно, конечно, мешать, но пронесёт.
>[оверквотинг удален]
>> $c->stash->{content} = $c->model('DBI::Content::Return')->return( $id );
>> $c->stash->{content} = $c->view('TT')->render( $c, 'content.html' );
>>
Спасибо.
Если я Вас правильно понял, то можно все сделать например так
$c->model('DBI::Content::Return')->return( $id );
В DBI::Content::Return
$keys_db->{ $content_f->{id} }->{title} = $content_f->{title};
$c->stash->{date} = $content_f->{date};
$c->stash->{published} = $content_f->{published};
$c->stash->{autor} = $content_f->{autor};
$c->stash->{content} = $content_f->{content};
И вопрос возможно немного не по теме.
Как лучше/рациональнее реализовать бизнес логику url
Сейчас у меня так:
Root.pm
sub begin : Private - работа с куки, чтение конфигов
Post.pm
sub index : Path :Args(1) { - url/post/$id - обращение к странице. Шаблон соответственно лежит в post/index.html
if ( $c->model('DBI::Content::Return')->return( $id_post ) == 1 ) {
$c->stash->{template} = 'error_404';
$c->response->status(404);
Таким костылем я осуществляю проверку параметра $id.
Но Все это тупо и не красиво.
Хочу избавиться от return.
Стоит ли отдать под контроль url в Root::begin?
И еще вопрос.
Куда писать данные(в том числе и временные системные)? чтобы они были доступны из любого места.
$c->stash ?
> Если я Вас правильно понял, то можно все сделать например так
> $c->model('DBI::Content::Return')->return( $id );
> В DBI::Content::Return
> $keys_db->{ $content_f->{id} }->{title} = $content_f->{title};
> $c->stash->{date} = $content_f->{date};
> $c->stash->{published} = $content_f->{published};
> $c->stash->{autor} = $content_f->{autor};
> $c->stash->{content} = $content_f->{content};Это тебя в другую сторону перекосило - не дело модели лазить в stash. Избегай излишних присваиваний. Если $c->stash не содержит ничего, кроме $content_f, смело делай $c->{stash} = $content_f. Приятный побочный эффект - что добавляешь в Model, сразу становится видимым во View.
>[оверквотинг удален]
> $c->stash->{template} = 'error_404';
> $c->response->status(404);
> Таким костылем я осуществляю проверку параметра $id.
> Но Все это тупо и не красиво.
> Хочу избавиться от return.
> Стоит ли отдать под контроль url в Root::begin?Вопрос сложный, следующий будет - а не нужно ли переделать URL, чтобы был вид /$id/xxx/xxx и применить Chained? То есть сначала убедиться, что $id существует, а уже потом разбираться, что следует делать.
Вся проблема - артефакт Catalyst с его Chained контроллерами. В других языках решается через наследование, где предок разбирается с ID, а потомки разбираются с более детальными инструкциями.
> И еще вопрос.
> Куда писать данные(в том числе и временные системные)? чтобы они были доступны
> из любого места.
> $c->stash ?В одном цикле запрос/ответ - всё равно. Хоть в $c->stash, хоть во $c->flash, хоть в глобальную переменную.
Неприятные вопросы появляются, когда один контроллер вызывает другой, то есть stash уже не годится, а session замусоривать не хочется. В Poet::Mason эта проблема решается принципиально по-другому, там нет нужды во всей банде go/visit/detach/Chained и неочевидной передачей параметров.
В Catalyst иногда приходится делать такое -
my @path = split /\//, $c->req->path;$path[1] = 'double';
$path[$#path-1] = $key;
$path[$#path] = $last;
$c->req->path(join('/', '',@path));
$c->dispatcher->prepare_action($c);
$c->detach($c->action, $c->req->args);Что означает "вызвать другой контроллер, передав ему в URL вычисленные аргументы"
Спасибо за разъяснения. В итоге получилось так
Post.pmsub index : Path :Args(1) {
my ( $self, $c, @args ) = @_;my $id_post = $c->request->arguments->[0];
my $error_404;
$c->stash->{site} = undef;unless ( $id_post =~ /^\d+?$/ ) {
$c->detach('/error_404');
}$c->stash->{site}->{content}->{id_post} = $id_post;
$c->model('DBI::Content::Return')->return( $c );
}DBI::Content::Return
sub return {
my ( $self, $c ) = @_;
my $dbh = $self->dbh;my $post_id = $c->stash->{site}->{content}->{id_post};
my $contents = $dbh->selectall_arrayref(qq{
SELECT * FROM `posts` WHERE `id` = $post_id },
{ Slice => {} } )
or die "Couldn't select books: " . $dbh->errstr;
unless ( @{$contents} ) {
$c->detach('/error_404');
} else {
foreach my $content_f (@$contents) {
$c->stash->{site}->{content} = $content_f;
}
}
}
> Post.pm
> sub index : Path :Args(1) {
> my ( $self, $c, @args ) = @_;
> my $id_post = $c->request->arguments->[0];sub index : Path :Args(1) {
my ( $self, $c, $id_post ) = @_;
> $c->stash->{site}->{content}->{id_post} = $id_post;
> $c->model('DBI::Content::Return')->return( $c );Не показывай модели $c, разводишь бардак. Верни значение контроллеру, это его забота, что делать дальше.
> DBI::Content::Return
> sub return {
> my ( $self, $c ) = @_;
> my $dbh = $self->dbh;
> my $post_id = $c->stash->{site}->{content}->{id_post};Ты, конечно, можешь прятать лифчики в ящике с цветными карандашами, но кто их потом сможет найти?
Модель должна вернуть данные.
Контроллер может слепить stash, если хочет. У каждого есть своё дело и очередь, когда его делать.
> my $contents = $dbh->selectall_arrayref(qq{
> SELECT * FROM
> `posts` WHERE `id` = $post_id },
> { Slice => {} } )
> or die "Couldn't select books: " . $dbh->errstr;Не, это не пойдёт. Прочитай про DBIx и вообще про ORM, у тебя типичная задача для него.
> unless ( @{$contents} ) {
> $c->detach('/error_404');
> } else {
> foreach my $content_f (@$contents)
> {
> $c->stash->{site}->{content} = $content_f;
> }Пробуй ещё, цикл здесь не нужен. Если posts.id не уникален, ты неправильно выполнил декомпозицию. В общем случае, если хочешь получить всё равно какой элемент массива, делай pop @$contents или $contents->[0]