Perlでの迅速なWebアプリケヌション開発入門

珟圚、Perl蚀語が䞍圓に忘れられおいる状況がありたす。 私はこの玠晎らしい蚀語の暩嚁をメモで少し䞊げたいです。
このマクロノヌトは、Perlの孊習者、この蚀語の専門家、およびPerlの詳现を知りたいだけの人を察象ずしおいたす。 蚘事では、私の経隓だけを共有したいず思いたす。

私の意芋では、䞭小芏暡のプロゞェクトを開発する際によく起こる簡単な状況を考えたいず思いたす。 そしお状況はこれです小さな䞭サむトを䜜成する必芁があり、CMSを攟棄する決定が䞋されたす、゚ンゞンが小さいので、管理パネルにベルやホむッスルの必芁はなく、耇雑さは玄16-24人/時間です たずえば、特定の皮類の蚘事通垞のテキスト蚘事ずニュヌスを含む小さなサむトが必芁です。 さらに、蚘事やニュヌスを远加するための小さな管理パネル。 この蚘事の2皮類のコンテンツには「倧きな」違いがあるこずに同意したしょう。

問題


そのような状況では、倚くの堎合、自転車、぀たり゚ンゞンを曞くずいう決定が䞋されたす。 そのような状況を考えおみたしょう。その䟋では、PerlずCPANの楜しさも考慮したす。
MVCの本栌的な実装は提䟛しおいたせん。これは私たちの小さなプロゞェクトには倚すぎたす。 キャリッゞずフレヌムワヌクの小さなカヌトMVCずそうでない䞡方がPerl甚に曞かれたした。たずえば、優れたCatalystは 、RubyOnRailsず非垞によく䌌おいたすたたはその逆、私は幎衚を知りたせん。 小さいものも倚数ありたす 。奜奇心the盛な方は、 こちらをご芧ください 。

簡単にするために、同様のメカニズムを実装しおいたすが、よりシンプルです。 それでは、モゞュヌルの圢匏で゚ンゞンのコンポヌネント LAMPがデフォルトですを芋おみたしょう。
1.デヌタ-DBIx ::クラス
2.衚瀺-テンプレヌトツヌルキット
3.管理-自分で行う
小さな䜙談。 私は長い間cgi-binフォルダヌが奜きではありたせん。可胜な限りあらゆる方法で.htaccessファむルを蚱可しようずしおいたすほずんどすべおのホスティングサヌビスでさらには自宅でも。 このようなファむルをプロゞェクトのルヌトフォルダヌに䜜成し、そこに曞き蟌みたす。
Options +ExecCGI
AddHandler cgi-script pl
DirectoryIndex index.pl

これで、珟圚のディレクトリで拡匵子.plのスクリプトを盎接実行できたす。 さらに、デフォルトのペヌゞはindex.plスクリプトになりたす。
次に、垞に蚭定を䜜成するこずをお勧めしたす。 倚くのバリ゚ヌションがありたすが、誰もが異なっお奜きです、私にずっおは最小限に芋えたす
package Conf;
use warnings;
use strict;

BEGIN
{
use Exporter;
our (@ISA, @EXPORT);
@ISA = qw(Exporter);
@EXPORT = qw(
$DB_Host $DB_Port $DB_Name $DB_User $DB_Pass
);
}

our $DB_Host = "host";
our $DB_Port = 3306;
our $DB_Name = "our_db";
our $DB_User = "our_table";
our $DB_Pass = "our_password";
1;


ここでは軍事的、グロヌバルなプロゞェクトパラメヌタは導入されず、゚クスポヌトされたせん。

デヌタ


DB構造


゚ンゞンに戻りたす。 最初のポむントはデヌタの操䜜です。DBIx:: Classパッケヌゞにはいく぀かのモゞュヌルが含たれおいたす。 たず、䜜業に䜿甚する単玔なデヌタベヌスを䜜成したす。 デヌタベヌスの構造に批刀的である必芁はありたせん。可胜な限り単玔で、䜙分な構文がないだけで、コストを最小限に抑えたす。
create table users (
id smallint not null primary key auto_increment,
name varchar(32) not null,
pass varchar(32) not null);

create table categories (
id int not null primary key auto_increment,
name varchar(128) not null) charset cp1251;

create table articles (
id int not null primary key auto_increment,
category_id int not null,
title varchar(255) not null,
content text not null,
author varchar(128) not null comment 'Author of article',
added_at timestamp not null,
added_by smallint not null comment 'Admin user ID') charset cp1251;

create table news (
id int not null primary key auto_increment,
added_at timestamp not null,
title varchar(255) not null,
content text not null,
is_put_on_main bool not null default 0 comment 'Show on main page?',
added_by smallint not null) charset cp1251;

ナヌザヌテヌブルには最も基本的なデヌタが含たれ蚪問数などはただ気になりたせん、 カテゎリテヌブルには蚘事のセクション「自動」、「スポヌツ」、「料理」などが含たれ、 蚘事テヌブルには実際の蚘事、 ニュヌステヌブルが含たれたす-ニュヌス。 埌者には、メむンのニュヌスを衚瀺するフィヌルドis_put_on_mainがありたす。 たた、ほずんどすべおのテヌブルで、゚ンコヌディングを蚭定したす-これは習慣です、確かに-しないでください。

コヌドぞのマッピング


さお、テヌブルがありたす。今床はコヌドでそれらを衚瀺する必芁がありたす。 DBIx :: Classモゞュヌルを䜿甚するず、SQLコヌドの蚘述から完党に離れ、オブゞェクトずしおテヌブルず通信できたす。 このモゞュヌルを䜿甚するには、2぀の方法がありたす。各テヌブルの構造を手動で蚘述するか、自動化を䜿甚したす。 䞡方の方法を順番に考えおみたしょう。

手䜜業による方法


コヌドを芋お、さらに説明がありたす。 プロゞェクトのルヌトにDBずいうフォルダヌを䜜成し、その䞭に4぀のファむルを䜜成したすUser.pm、Category.pm、Article.pm、News.pm、これらのファむルの内容は次のずおりです。
# file User.pm
package DB::User;

use base qw/DBIx::Class/;

__PACKAGE__->load_components(qw/PK::Auto Core/);
__PACKAGE__->table('users');
__PACKAGE__->add_columns(qw/id name pass/);
__PACKAGE__->set_primary_key('id');

__PACKAGE__->has_many('articles' => 'DB::Article',
{ 'foreign.added_by' => 'self.id' });
__PACKAGE__->has_many('news' => 'DB::News',
{ 'foreign.added_by' => 'self.id' });

1;

# file Category.pm
package DB::Category;

use base qw/DBIx::Class/;

__PACKAGE__->load_components(qw/PK::Auto Core/);
__PACKAGE__->table('categories');
__PACKAGE__->add_columns(qw/id name/);
__PACKAGE__->set_primary_key('id');

__PACKAGE__->has_many('articles' => 'DB::Article',
{ 'foreign.category_id' => 'self.id' });

1;

# file Article.pm
package DB::Article;

use base qw/DBIx::Class/;

__PACKAGE__->load_components(qw/InflateColumn::DateTime PK::Auto Core/);
__PACKAGE__->table('articles');
__PACKAGE__->add_columns(qw/id category_id title content added_by author/);
__PACKAGE__->add_columns('added_at' => { data_type => 'timestamp' });
__PACKAGE__->set_primary_key('id');

__PACKAGE__->belongs_to('category' => 'DB::Category',
{ 'foreign.id' => 'self.category_id' });
__PACKAGE__->belongs_to('user' => 'DB::User',
{ 'foreign.id' => 'self.added_by' });

1;

# file News.pm
package DB::News;

use base qw/DBIx::Class/;

__PACKAGE__->load_components(qw/InflateColumn::DateTime PK::Auto Core/);
__PACKAGE__->table('news');
__PACKAGE__->add_columns(qw/id title content is_put_on_main added_by/);
__PACKAGE__->add_columns('added_at' => { data_type => 'timestamp' });
__PACKAGE__->set_primary_key('id');

__PACKAGE__->belongs_to('user' => 'DB::User',
{ 'foreign.id' => 'self.added_by' });

1;

だから、少し説明。 4぀の非垞に類䌌したファむルがあり、最初に基本モゞュヌルDBIx :: Classを宣蚀し、次に__PACKAGE__メカニズムを䜿甚しおそのメ゜ッドを呌び出したす load_components-モゞュヌルのコンポヌネントをロヌドしたすリンク、行、列を含む。 次に、テヌブルを指定し、その埌に列の名前を远加したす。 日時、日付、タむムスタンプなどのタむプの列を操䜜するには、小さなモゞュヌルInflateColumn :: DateTimeが䜿甚されたす。 これを䜿甚するず、瀺されたタむプのフィヌルドは、 DateTimeタむプのオブゞェクトずしおプログラムで䜿甚でき、すべおの埌続のアメニティを䜿甚できたす。 その埌、䞻キヌを指定したす耇合キヌの堎合は、耇数のフィヌルドset_primary_keyqw / name1 name2 /を指定したす;。
次に、RubyOnRails、has_many、belongs_to、およびその他を知っおいる人にずっお銎染みのあるメ゜ッドがありたす。 これらのメ゜ッドは、テヌブル間の関係を䜜成するように蚭蚈されおいたす。
すばらしいDBIx :: Classモゞュヌルのドキュメント 。チュヌトリアルやクックブックなど、すべおが詳现に説明されおいたす。

次に、この奇跡を䜿甚する必芁がありたす。そのためには、 DBIx :: Class :: Shemaモゞュヌルが必芁です。これは、デヌタスキヌムの抜象化です。 プロゞェクトのルヌトフォルダヌに、テヌブルを蚘述するクラスを持぀フォルダヌの名前ず同じ名前のファむルを䜜成したす。この堎合はDB.pmになりたす。
package SDB;

use base qw/DBIx::Class::Schema/;
use Conf;

__PACKAGE__->load_classes();

sub GetSchema()
{
my $dsn = "dbi:mysql:$DB_Name:$DB_Host";
my $sch = __PACKAGE__->connect($dsn, $DB_User, $DB_Pass);

return $sch;
}

1;

䞀般に、 GetShema関数なしでDBIx :: Class :: Schemaを䜿甚できたす。load_classes メ゜ッドは、同じ名前のフォルダヌで芋぀かったすべおのファむルを自動的にロヌドしたす。 回路を取埗する方が䟿利になるように、小さな関数を远加したした。 この関数がないず、コヌド内の接続は次のようになりたす。
my $dsn = "dbi:mysql:$DB_Name:$DB_Host";
my $sch = DB->connect($dsn, $DB_User, $DB_Pass);

funkiyaの堎合、それらのいく぀かを蚘述したり、異なるタむプのベヌスずの接続を別々に構成したりできたす。

自動方法


「手動」の䟋では、テヌブル間のすべおの関係を手動で蚭定したす。 クラスを自動的にロヌドおよび䜜成するDBIx :: Class :: Shema :: Loaderモゞュヌルがありたす。 これを行うには、倖郚キヌの説明をデヌタベヌス構造に远加したす。 ロヌダヌを䜿甚するず、必芁な接続が自動的に䜜成されたす。 これは次のようなものです。
package DB;
use base qw/DBIx::Class::Schema::Loader/;

__PACKAGE__->loader_options(
inflect_singular => 1,
components => qw/InflateColumn::DateTime/
);

1;

#

use DB;
my $sch = DB->connect( $dsn, $user, $password, $attrs);

䞊蚘のGetShema関数を远加䞊蚘を参照しお䜿甚するこずもできたす。 この堎合、DBフォルダヌずその䞭の4぀のファむルは䞍芁になり、スキヌマ蚘述ファむルはただ1぀ありたす。 ロヌダヌは、䜜成するクラスの名前空間、クラス名を生成するためのパラメヌタヌなどを指定する倚くのオプションをサポヌトしおいたす 。

スキヌマを䜿甚する


次に、これらすべおがコヌドで盎接䜿甚される方法を芋おみたしょう。
use DB;

my $sch = DB->GetShema();
# id
my $user = $sch->resultset('User')->find({ id => $id });

#
my $new_id = $sch->resultset('Category')->populate(
[
[qw/title content is_put_on_main added_by/],
[$ntitle, $ncontent, 0, $user_id]
]);

#
$sch->resultset('Article')->find({ id => $aid })->delete;

次に、デヌタを衚瀺したす。

ディスプレむ


Template Toolkitを䜿甚したす 。 さらにいく぀かのシステム、たずえばMasonがありたすが、歎史的にそうなったため、私の遞択はTemplate Toolkitに委ねられたした。
Template Toolkitは、テンプレヌト凊理システムです。 䟋でその䜿甚方法を芋おみたしょう。 たず、プロゞェクトルヌトにtmplフォルダヌを䜜成し、その䞭にサむトフォルダヌを䜜成したす。 tmpl / siteフォルダヌで、次の内容のサむトファむルを䜜成したす。
Portal


[% PROCESS $content %]





次に、そこにstart_pageファむルを䜜成したす。
News and articles

これは、1行の単玔なファむルです。 これは、スタヌトペヌゞの空癜になりたす。 すべおをたずめお、 index.plスクリプト甚に次のコヌドのようなものを取埗したす。
#!/usr/bin/perl -w

use strict;

use CGI;
use Template;

use Conf;
use DB;

# CGI
my $q = CGI->new;
my %p = $q->Vars;

# ...
my $tmpl = Template->new(
{
INCLUDE_PATH => 'tmpl/site',
INTERPOLATE => 1,
EVAL_PERL => 1
}) || die "$Template::ERROR\n";

# ...
my $sch = DB->GetShema();

#
my $tmpl_vars = {};
$tmpl_vars->{content} = 'start_page';

print $q->header(-type => 'text/html', -charset => 'windows-1251');
$tmpl->process('site', $tmpl_vars) || die $tmpl->error(), "\n";

誰もがCGIに぀いおの2行を理解しおいるず思いたす。次にテンプレヌトテンプレヌトを䜜成したす。テンプレヌトのメむンパラメヌタはINCLUDE_PATHです。 もう少し、デヌタスキヌムを䜜成し、デヌタベヌスに接続したす。 次に、テンプレヌトに枡す必芁のあるすべおの倉数を远加するハッシュを䜜成したす。 この堎合、1぀の倉数contentのみを枡したす。この倉数は、 サむトテンプレヌトのPROCESSディレクティブで䜿甚されたす。 さらに䜎いのは、テンプレヌトの凊理を開始し、開始テンプレヌト-siteを指定し、倉数のハッシュを転送するこずです。

サむトテンプレヌトはPROCESSディレクティブを䜿甚し、名前がパラメヌタヌずしお枡される別のテンプレヌトのネストされた凊理を開始したすが、倉数に名前が栌玍されおいるため、これを盎接瀺したす- [PROCESS $ content] したがっお、 start_pageテンプレヌトのコンテンツは、 サむトテンプレヌトの本文に挿入されたす。 少しバラ゚ティを远加したす。 メむンペヌゞでは、蚘事ずニュヌスを衚瀺する必芁がありたすが、すべおではなく、最埌の10件を衚瀺する必芁がありたす。 たた、ニュヌスは、テヌブル内の察応するフラグでマヌクされおいるニュヌスのみです。 テンプレヌトを凊理する前に、スクリプトに数行を远加したす。
my $articles = [$sch->resultset('Article')->search(undef,
{
order_by => 'added_at desc',
rows => 10,
page => 1
})];
my $news = [$sch->resultset('News')->search(
{
is_put_on_main => 1
},
{
order_by => 'added_at desc',
rows => 10,
page => 1
})];
$tmpl_vars->{articles} = $articles;
$tmpl_vars->{news} = $news;

[]を䜿甚しおリストコンテキストを䜜成したこずに泚意しおください。そうでない堎合、スカラヌコンテキストでは、 search関数はResultSet型のオブゞェクトを返し、正確にデヌタの配列が必芁です。

ですから、すべおがかなり明癜なので、詳现に説明するのは意味がありたせん。 唯䞀のものは、 行/ペヌゞパラメヌタの䜿甚です。 これらは、ペヌゞネヌションを敎理するのに䟿利な、いわゆるペヌゞャヌを䜜成するために必芁であり、特殊なケヌスであるレコヌドの単玔な遞択にも䜿甚されたす。 たた、蚘事ずニュヌスの数を構成に送信できたす。

次に、 start_pageテンプレヌトを倉曎したす 。


[% FOREACH n = news %]
[% n.added_at.dmy('.') %] [% n.title %]

[% n.content FILTER html %]

[% END %]

[% FOREACH a = articles %]
[% a.added_at.dmy('.') %] [% a.title %]

: [% a.category.name %]
[% a.content FILTER html %]

[% END %]


[% FOREACH n = news %]
[% n.added_at.dmy('.') %] [% n.title %]

[% n.content FILTER html %]

[% END %]

[% FOREACH a = articles %]
[% a.added_at.dmy('.') %] [% a.title %]

: [% a.category.name %]
[% a.content FILTER html %]

[% END %]


[% FOREACH n = news %]
[% n.added_at.dmy('.') %] [% n.title %]

[% n.content FILTER html %]

[% END %]

[% FOREACH a = articles %]
[% a.added_at.dmy('.') %] [% a.title %]

: [% a.category.name %]
[% a.content FILTER html %]

[% END %]


[% FOREACH n = news %]
[% n.added_at.dmy('.') %] [% n.title %]

[% n.content FILTER html %]

[% END %]

[% FOREACH a = articles %]
[% a.added_at.dmy('.') %] [% a.title %]

: [% a.category.name %]
[% a.content FILTER html %]

[% END %]

added_atフィヌルドをオブゞェクトずしお䜿甚しおいるこずに泚意しおください。 そのために、 dmyメ゜ッドが呌び出されたす 。このメ゜ッドは、指定された区切り文字この堎合はピリオドを䜿甚しおDD-MM-YYYY圢匏で日付をフォヌマットしたす。 DateTimeオブゞェクトはロケヌルをサポヌトし、珟圚のたたは遞択されたロケヌルに応じお日付を正しく衚瀺したす。 たた、日付の曞匏蚭定ず操䜜のための倚くのメ゜ッドが含たれおいたす。

有効なリンクをただ意図的に远加しおいたせん。これは埌で行いたす。
䞀般的に、ホテルファむルに移動する必芁がある2぀の類䌌したブロックがありたす。 tmpl / siteフォルダヌにshort_noteファむルを䜜成したす。
[% text = node.content;
IF text.length > 512;
text = text.substr(0, 512);
END %]
[% note.added_at.dmy('.') %] [% note.title %]

[% IF note.category %]
: [% note.category.name %]
[% END %]
[% text FILTER html %]


これで、 start_pageテンプレヌトは次のようになりたす。


[% FOREACH n = news %]
[% PROCESS short_note note = n %]
[% END %]

[% FOREACH a = articles %]
[% PROCESS short_note note = a %]
[% END %]


[% FOREACH n = news %]
[% PROCESS short_note note = n %]
[% END %]

[% FOREACH a = articles %]
[% PROCESS short_note note = a %]
[% END %]


[% FOREACH n = news %]
[% PROCESS short_note note = n %]
[% END %]

[% FOREACH a = articles %]
[% PROCESS short_note note = a %]
[% END %]


[% FOREACH n = news %]
[% PROCESS short_note note = n %]
[% END %]

[% FOREACH a = articles %]
[% PROCESS short_note note = a %]
[% END %]

ここで、 short_noteテンプレヌトの凊理を呌び出し、珟圚のニュヌスたたは蚘事をパラメヌタヌずしおnoteに枡したす。

テンプレヌトは、蚘事の機胜であるカテゎリフィヌルドの存圚を確認したす。この堎合、セクションの名前を衚瀺したす。

ポヌタルでは、蚘事党䜓たたはニュヌスを衚瀺し、蚘事カテゎリのリスト、怜玢フォヌム、および怜玢結果を衚瀺するためのテンプレヌトも必芁です。 䞀般に、耇雑さの点で䞊蚘ずあたり倉わらないテンプレヌトがさらにいく぀か远加されたす。

運営管理


䞊蚘では、すべおの皮類のフレヌムワヌクを䜿甚しないこずに同意したした。私たちは自分の手で最小限のこずをしようずしたす。 これを行うために、次の単玔な構造を䜜成したすラマヌ、はい
my $act = $p{'a'} || 'start';

if ($act eq 'start')
{
}
elsif ($act eq 'article')
{
}
elsif ($act eq 'news')
{
}
# ....
else
{
}

そのため、スクリプト内の各リンクには- アクションパラメヌタヌが付随したす。 珟圚のコンテキストを蚭定したす。 したがっお、テンプレヌト内の䞊蚘のリンクは次のように倉曎できたす。

, . id , . :
$p{'id'} =~ s/\D//g if ($p{'id'});

, - , , , -. .

.
if ($act eq 'start')
{
$tmpl_vars->{content} = 'start_page';
my $articles = [$sch->resultset('Article')->search(undef,
{
order_by => 'added_at desc',
rows => 10,
page => 1
})];
my $news = [$sch->resultset('News')->search(
{
is_put_on_main => 1
},
{
order_by => 'added_at desc',
rows => 10,
page => 1
})];
$tmpl_vars->{articles} = $articles;
$tmpl_vars->{news} = $news;
}
elsif ($act eq 'article')
{
$tmpl_vars->{content} = 'full_article';
$tmpl_vars->{article} = $sch->resultset('Article')->find({ id => $p{'id'} });
}
elsif ($act eq 'category')
{
$tmpl_vars->{content} = 'category';
$tmpl_vars->{category} = $sch->resultset('Category')->find({ id => $p{'id'} });
}
elsif ($act eq 'news')
{
$tmpl_vars->{content} = 'full_article';
$tmpl_vars->{article} = $sch->resultset('News')->find({ id => $p{'id'} });
}
else
{
# .
}

, , . , Perl , , HTML-CheckArgs HTML-QuickCheck . , HTML-Widget HTML-Tag . . , . .

: , . ( , error_action , ), :
print $q->header(-location => '?a=start');
exit;

, . , , ( ):
my %action = (
'start' => 'Main page',
'news' => 'News page',
'article' => 'Full article',
# ....
);
my $act = ( $p{'act'} && defined( $actions{$p{'act'}} )) ? $p{'act'} : 'start';

, — , 'start'. - defined(...) .


. , . , tmpl/admin .
: Digest::SHA1 CGI::Session . , — .
, . .

:
[%# login %]
[% IF err %]
Wrong login
[% END %]
/>
Login: />
Password: />
/>



, :
use CGI::Session;
use Digest::SHA1 qw(sha1_hex);

# ... CGI
my $s = CGI::Session->load(undef, undef, { Directory => 'ssss' } );

# ...
if ($s->empty && $act !~ /login(_form)?|logout/)
{
print $q->header(-location => '?a=login_form');
exit;
}
else
{
my $user = $sch->resultset('User')->find({ id => $s->param('uid') });
$tmpl_vars->{user} = $user;
}

if ($act eq 'login_form')
{
$tmpl_vars->{content} = 'login_form';
}
elsif ($act eq 'login')
{
unless (my $u = &login($p{'login'}, $p{'pass'}))
{
$tmpl_vars->{content} = 'login';
$tmpl_vars->{err} = 1;
}
else
{
$s = $s->new;
$s->param('uid', $u->id);

print $s->header(-location => '?a=start');
exit;
}
}
elsif ($act eq 'logout')
{
$s->delete;
print $q->header(-location => '?a=login');
exit;
}

#
sub login
{
my ($u, $p) = @_;

my $pp = sha1_hex($p);
my $res = $sch->resultset('User')->search({
name => $u,
pass => $pp
});

my $user = $res->next;
return $user;
}


, , .
CGI::Session , . — expired. ssss .
Digest::SHA1 - MD5.

. -, CRUD- (CReate, Update, Delete). , , DBIx::Class::WebForm . CRUD CPAN .
-, . FCKeditor , . .
-, . , DBIx::Class::Validation , , , CGI::FormBuilder , CGI::QuickForm .. "Form", "Validate" "Widget" .


"" . , , , . , , . , SQL-.

, - . .

-NOT_FOR_HOLYWARS-


, . id , . :
$p{'id'} =~ s/\D//g if ($p{'id'});

, - , , , -. .

.
if ($act eq 'start')
{
$tmpl_vars->{content} = 'start_page';
my $articles = [$sch->resultset('Article')->search(undef,
{
order_by => 'added_at desc',
rows => 10,
page => 1
})];
my $news = [$sch->resultset('News')->search(
{
is_put_on_main => 1
},
{
order_by => 'added_at desc',
rows => 10,
page => 1
})];
$tmpl_vars->{articles} = $articles;
$tmpl_vars->{news} = $news;
}
elsif ($act eq 'article')
{
$tmpl_vars->{content} = 'full_article';
$tmpl_vars->{article} = $sch->resultset('Article')->find({ id => $p{'id'} });
}
elsif ($act eq 'category')
{
$tmpl_vars->{content} = 'category';
$tmpl_vars->{category} = $sch->resultset('Category')->find({ id => $p{'id'} });
}
elsif ($act eq 'news')
{
$tmpl_vars->{content} = 'full_article';
$tmpl_vars->{article} = $sch->resultset('News')->find({ id => $p{'id'} });
}
else
{
# .
}

, , . , Perl , , HTML-CheckArgs HTML-QuickCheck . , HTML-Widget HTML-Tag . . , . .

: , . ( , error_action , ), :
print $q->header(-location => '?a=start');
exit;

, . , , ( ):
my %action = (
'start' => 'Main page',
'news' => 'News page',
'article' => 'Full article',
# ....
);
my $act = ( $p{'act'} && defined( $actions{$p{'act'}} )) ? $p{'act'} : 'start';

, — , 'start'. - defined(...) .


. , . , tmpl/admin .
: Digest::SHA1 CGI::Session . , — .
, . .

:
[%# login %]
[% IF err %]
Wrong login
[% END %]
/>
Login: />
Password: />
/>



, :
use CGI::Session;
use Digest::SHA1 qw(sha1_hex);

# ... CGI
my $s = CGI::Session->load(undef, undef, { Directory => 'ssss' } );

# ...
if ($s->empty && $act !~ /login(_form)?|logout/)
{
print $q->header(-location => '?a=login_form');
exit;
}
else
{
my $user = $sch->resultset('User')->find({ id => $s->param('uid') });
$tmpl_vars->{user} = $user;
}

if ($act eq 'login_form')
{
$tmpl_vars->{content} = 'login_form';
}
elsif ($act eq 'login')
{
unless (my $u = &login($p{'login'}, $p{'pass'}))
{
$tmpl_vars->{content} = 'login';
$tmpl_vars->{err} = 1;
}
else
{
$s = $s->new;
$s->param('uid', $u->id);

print $s->header(-location => '?a=start');
exit;
}
}
elsif ($act eq 'logout')
{
$s->delete;
print $q->header(-location => '?a=login');
exit;
}

#
sub login
{
my ($u, $p) = @_;

my $pp = sha1_hex($p);
my $res = $sch->resultset('User')->search({
name => $u,
pass => $pp
});

my $user = $res->next;
return $user;
}


, , .
CGI::Session , . — expired. ssss .
Digest::SHA1 - MD5.

. -, CRUD- (CReate, Update, Delete). , , DBIx::Class::WebForm . CRUD CPAN .
-, . FCKeditor , . .
-, . , DBIx::Class::Validation , , , CGI::FormBuilder , CGI::QuickForm .. "Form", "Validate" "Widget" .


"" . , , , . , , . , SQL-.

, - . .

-NOT_FOR_HOLYWARS-


, . id , . :
$p{'id'} =~ s/\D//g if ($p{'id'});

, - , , , -. .

.
if ($act eq 'start')
{
$tmpl_vars->{content} = 'start_page';
my $articles = [$sch->resultset('Article')->search(undef,
{
order_by => 'added_at desc',
rows => 10,
page => 1
})];
my $news = [$sch->resultset('News')->search(
{
is_put_on_main => 1
},
{
order_by => 'added_at desc',
rows => 10,
page => 1
})];
$tmpl_vars->{articles} = $articles;
$tmpl_vars->{news} = $news;
}
elsif ($act eq 'article')
{
$tmpl_vars->{content} = 'full_article';
$tmpl_vars->{article} = $sch->resultset('Article')->find({ id => $p{'id'} });
}
elsif ($act eq 'category')
{
$tmpl_vars->{content} = 'category';
$tmpl_vars->{category} = $sch->resultset('Category')->find({ id => $p{'id'} });
}
elsif ($act eq 'news')
{
$tmpl_vars->{content} = 'full_article';
$tmpl_vars->{article} = $sch->resultset('News')->find({ id => $p{'id'} });
}
else
{
# .
}

, , . , Perl , , HTML-CheckArgs HTML-QuickCheck . , HTML-Widget HTML-Tag . . , . .

: , . ( , error_action , ), :
print $q->header(-location => '?a=start');
exit;

, . , , ( ):
my %action = (
'start' => 'Main page',
'news' => 'News page',
'article' => 'Full article',
# ....
);
my $act = ( $p{'act'} && defined( $actions{$p{'act'}} )) ? $p{'act'} : 'start';

, — , 'start'. - defined(...) .


. , . , tmpl/admin .
: Digest::SHA1 CGI::Session . , — .
, . .

:
[%# login %]
[% IF err %]
Wrong login
[% END %]
/>
Login: />
Password: />
/>



, :
use CGI::Session;
use Digest::SHA1 qw(sha1_hex);

# ... CGI
my $s = CGI::Session->load(undef, undef, { Directory => 'ssss' } );

# ...
if ($s->empty && $act !~ /login(_form)?|logout/)
{
print $q->header(-location => '?a=login_form');
exit;
}
else
{
my $user = $sch->resultset('User')->find({ id => $s->param('uid') });
$tmpl_vars->{user} = $user;
}

if ($act eq 'login_form')
{
$tmpl_vars->{content} = 'login_form';
}
elsif ($act eq 'login')
{
unless (my $u = &login($p{'login'}, $p{'pass'}))
{
$tmpl_vars->{content} = 'login';
$tmpl_vars->{err} = 1;
}
else
{
$s = $s->new;
$s->param('uid', $u->id);

print $s->header(-location => '?a=start');
exit;
}
}
elsif ($act eq 'logout')
{
$s->delete;
print $q->header(-location => '?a=login');
exit;
}

#
sub login
{
my ($u, $p) = @_;

my $pp = sha1_hex($p);
my $res = $sch->resultset('User')->search({
name => $u,
pass => $pp
});

my $user = $res->next;
return $user;
}


, , .
CGI::Session , . — expired. ssss .
Digest::SHA1 - MD5.

. -, CRUD- (CReate, Update, Delete). , , DBIx::Class::WebForm . CRUD CPAN .
-, . FCKeditor , . .
-, . , DBIx::Class::Validation , , , CGI::FormBuilder , CGI::QuickForm .. "Form", "Validate" "Widget" .


"" . , , , . , , . , SQL-.

, - . .

-NOT_FOR_HOLYWARS-


, . id , . :
$p{'id'} =~ s/\D//g if ($p{'id'});

, - , , , -. .

.
if ($act eq 'start')
{
$tmpl_vars->{content} = 'start_page';
my $articles = [$sch->resultset('Article')->search(undef,
{
order_by => 'added_at desc',
rows => 10,
page => 1
})];
my $news = [$sch->resultset('News')->search(
{
is_put_on_main => 1
},
{
order_by => 'added_at desc',
rows => 10,
page => 1
})];
$tmpl_vars->{articles} = $articles;
$tmpl_vars->{news} = $news;
}
elsif ($act eq 'article')
{
$tmpl_vars->{content} = 'full_article';
$tmpl_vars->{article} = $sch->resultset('Article')->find({ id => $p{'id'} });
}
elsif ($act eq 'category')
{
$tmpl_vars->{content} = 'category';
$tmpl_vars->{category} = $sch->resultset('Category')->find({ id => $p{'id'} });
}
elsif ($act eq 'news')
{
$tmpl_vars->{content} = 'full_article';
$tmpl_vars->{article} = $sch->resultset('News')->find({ id => $p{'id'} });
}
else
{
# .
}

, , . , Perl , , HTML-CheckArgs HTML-QuickCheck . , HTML-Widget HTML-Tag . . , . .

: , . ( , error_action , ), :
print $q->header(-location => '?a=start');
exit;

, . , , ( ):
my %action = (
'start' => 'Main page',
'news' => 'News page',
'article' => 'Full article',
# ....
);
my $act = ( $p{'act'} && defined( $actions{$p{'act'}} )) ? $p{'act'} : 'start';

, — , 'start'. - defined(...) .


. , . , tmpl/admin .
: Digest::SHA1 CGI::Session . , — .
, . .

:
[%# login %]
[% IF err %]
Wrong login
[% END %]
/>
Login: />
Password: />
/>



, :
use CGI::Session;
use Digest::SHA1 qw(sha1_hex);

# ... CGI
my $s = CGI::Session->load(undef, undef, { Directory => 'ssss' } );

# ...
if ($s->empty && $act !~ /login(_form)?|logout/)
{
print $q->header(-location => '?a=login_form');
exit;
}
else
{
my $user = $sch->resultset('User')->find({ id => $s->param('uid') });
$tmpl_vars->{user} = $user;
}

if ($act eq 'login_form')
{
$tmpl_vars->{content} = 'login_form';
}
elsif ($act eq 'login')
{
unless (my $u = &login($p{'login'}, $p{'pass'}))
{
$tmpl_vars->{content} = 'login';
$tmpl_vars->{err} = 1;
}
else
{
$s = $s->new;
$s->param('uid', $u->id);

print $s->header(-location => '?a=start');
exit;
}
}
elsif ($act eq 'logout')
{
$s->delete;
print $q->header(-location => '?a=login');
exit;
}

#
sub login
{
my ($u, $p) = @_;

my $pp = sha1_hex($p);
my $res = $sch->resultset('User')->search({
name => $u,
pass => $pp
});

my $user = $res->next;
return $user;
}


, , .
CGI::Session , . — expired. ssss .
Digest::SHA1 - MD5.

. -, CRUD- (CReate, Update, Delete). , , DBIx::Class::WebForm . CRUD CPAN .
-, . FCKeditor , . .
-, . , DBIx::Class::Validation , , , CGI::FormBuilder , CGI::QuickForm .. "Form", "Validate" "Widget" .


"" . , , , . , , . , SQL-.

, - . .

-NOT_FOR_HOLYWARS-


, . id , . :
$p{'id'} =~ s/\D//g if ($p{'id'});

, - , , , -. .

.
if ($act eq 'start')
{
$tmpl_vars->{content} = 'start_page';
my $articles = [$sch->resultset('Article')->search(undef,
{
order_by => 'added_at desc',
rows => 10,
page => 1
})];
my $news = [$sch->resultset('News')->search(
{
is_put_on_main => 1
},
{
order_by => 'added_at desc',
rows => 10,
page => 1
})];
$tmpl_vars->{articles} = $articles;
$tmpl_vars->{news} = $news;
}
elsif ($act eq 'article')
{
$tmpl_vars->{content} = 'full_article';
$tmpl_vars->{article} = $sch->resultset('Article')->find({ id => $p{'id'} });
}
elsif ($act eq 'category')
{
$tmpl_vars->{content} = 'category';
$tmpl_vars->{category} = $sch->resultset('Category')->find({ id => $p{'id'} });
}
elsif ($act eq 'news')
{
$tmpl_vars->{content} = 'full_article';
$tmpl_vars->{article} = $sch->resultset('News')->find({ id => $p{'id'} });
}
else
{
# .
}

, , . , Perl , , HTML-CheckArgs HTML-QuickCheck . , HTML-Widget HTML-Tag . . , . .

: , . ( , error_action , ), :
print $q->header(-location => '?a=start');
exit;

, . , , ( ):
my %action = (
'start' => 'Main page',
'news' => 'News page',
'article' => 'Full article',
# ....
);
my $act = ( $p{'act'} && defined( $actions{$p{'act'}} )) ? $p{'act'} : 'start';

, — , 'start'. - defined(...) .


. , . , tmpl/admin .
: Digest::SHA1 CGI::Session . , — .
, . .

:
[%# login %]
[% IF err %]
Wrong login
[% END %]
/>
Login: />
Password: />
/>



, :
use CGI::Session;
use Digest::SHA1 qw(sha1_hex);

# ... CGI
my $s = CGI::Session->load(undef, undef, { Directory => 'ssss' } );

# ...
if ($s->empty && $act !~ /login(_form)?|logout/)
{
print $q->header(-location => '?a=login_form');
exit;
}
else
{
my $user = $sch->resultset('User')->find({ id => $s->param('uid') });
$tmpl_vars->{user} = $user;
}

if ($act eq 'login_form')
{
$tmpl_vars->{content} = 'login_form';
}
elsif ($act eq 'login')
{
unless (my $u = &login($p{'login'}, $p{'pass'}))
{
$tmpl_vars->{content} = 'login';
$tmpl_vars->{err} = 1;
}
else
{
$s = $s->new;
$s->param('uid', $u->id);

print $s->header(-location => '?a=start');
exit;
}
}
elsif ($act eq 'logout')
{
$s->delete;
print $q->header(-location => '?a=login');
exit;
}

#
sub login
{
my ($u, $p) = @_;

my $pp = sha1_hex($p);
my $res = $sch->resultset('User')->search({
name => $u,
pass => $pp
});

my $user = $res->next;
return $user;
}


, , .
CGI::Session , . — expired. ssss .
Digest::SHA1 - MD5.

. -, CRUD- (CReate, Update, Delete). , , DBIx::Class::WebForm . CRUD CPAN .
-, . FCKeditor , . .
-, . , DBIx::Class::Validation , , , CGI::FormBuilder , CGI::QuickForm .. "Form", "Validate" "Widget" .


"" . , , , . , , . , SQL-.

, - . .

-NOT_FOR_HOLYWARS-

Source: https://habr.com/ru/post/J23543/


All Articles