Featured image of post 第1回-【Perl入門】シンプルなユーザー登録バリデーションを作る

第1回-【Perl入門】シンプルなユーザー登録バリデーションを作る

Perl 5.36でユーザー登録フォームの入力検証を実装します。メールアドレス形式チェック、必須項目確認など基本的なバリデーションロジックをif文で構築。Moo OOPシリーズ卒業者向けの実践編です。

@nqounetです。

「ユーザー登録バリデータで学ぶ責任の連鎖」シリーズの第1回です。

このシリーズは、「Mooで覚えるオブジェクト指向プログラミング」シリーズの応用編です。まだ読んでいない方は、先にこちらをご覧ください。

今回は、ユーザー登録フォームの入力検証(バリデーション)を実装します。まずはシンプルなif文で書いてみて、バリデーションの基本を学びましょう。

バリデーションとは

バリデーション(validation)とは、ユーザーが入力したデータが正しいかどうかをチェックする処理です。Webアプリケーションでは、フォームから送信されたデータを受け取る前に、必ずバリデーションを行います。

たとえば、ユーザー登録フォームでは以下のようなチェックが必要です。

  • 名前が入力されているか(必須チェック)
  • メールアドレスが入力されているか(必須チェック)
  • メールアドレスの形式が正しいか(形式チェック)

これらのチェックを通過したデータだけを、データベースに保存します。

シンプルなバリデーションを書いてみる

まずは、名前とメールアドレスの検証を行うスクリプトを書いてみましょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# validate_user.pl
# Perl v5.36+, 外部依存なし

use v5.36;
use utf8;
use warnings;
binmode STDOUT, ':utf8';

sub validate_user ($input) {
    # 名前の必須チェック
    my $name = $input->{name} // '';
    if ($name eq '') {
        say "エラー: 名前を入力してください";
        return 0;
    }

    # メールアドレスの必須チェック
    my $email = $input->{email} // '';
    if ($email eq '') {
        say "エラー: メールアドレスを入力してください";
        return 0;
    }

    # メールアドレスの形式チェック
    if ($email !~ /\A[^@\s]+\@[^@\s]+\.[^@\s]+\z/) {
        say "エラー: メールアドレスの形式が正しくありません";
        return 0;
    }

    say "検証成功: $name ($email)";
    return 1;
}

# テストしてみる
say "=== テスト1: 正常なデータ ===";
validate_user({ name => '山田太郎', email => 'yamada@example.com' });

say "\n=== テスト2: 名前が空 ===";
validate_user({ name => '', email => 'yamada@example.com' });

say "\n=== テスト3: メールアドレスが空 ===";
validate_user({ name => '山田太郎', email => '' });

say "\n=== テスト4: メールアドレスの形式が不正 ===";
validate_user({ name => '山田太郎', email => 'invalid-email' });

実行すると、以下のような結果が表示されます。

1
perl validate_user.pl
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
=== テスト1: 正常なデータ ===
検証成功: 山田太郎 (yamada@example.com)

=== テスト2: 名前が空 ===
エラー: 名前を入力してください

=== テスト3: メールアドレスが空 ===
エラー: メールアドレスを入力してください

=== テスト4: メールアドレスの形式が不正 ===
エラー: メールアドレスの形式が正しくありません

動きましたね。これがバリデーションの基本形です。

コードのポイント

このコードにはいくつかのポイントがあります。

1. 早期リターン

エラーが見つかった時点で return 0 して関数を終了しています。これを「早期リターン」と呼びます。ネストが深くならず、コードが読みやすくなります。

2. 正規表現によるメール形式チェック

$email !~ /\A[^@\s]+\@[^@\s]+\.[^@\s]+\z/ は、メールアドレスの簡易的な形式チェックです。

  • \A - 文字列の先頭
  • [^@\s]+ - @とスペース以外の1文字以上
  • \@ - @記号
  • [^@\s]+ - @とスペース以外の1文字以上
  • \. - ドット
  • [^@\s]+ - @とスペース以外の1文字以上
  • \z - 文字列の末尾

これは完璧なメールアドレス検証ではありませんが、多くの場合は十分です。

3. 未定義値のハンドリング

$input->{name} // '' は、キーが存在しない場合やundefの場合に空文字列を使う書き方です。// は「defined-or演算子」と呼ばれます。

すべてのエラーをまとめて返す

先ほどのコードは、最初のエラーで処理を終了していました。しかし実際のアプリケーションでは、すべてのエラーをまとめて表示したいことがあります。

ハッシュを使ってエラーメッセージを収集するように改良してみましょう。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# validate_user_v2.pl
# Perl v5.36+, 外部依存なし

use v5.36;
use utf8;
use warnings;
binmode STDOUT, ':utf8';

sub validate_user ($input) {
    my %errors;

    # 名前の必須チェック
    my $name = $input->{name} // '';
    if ($name eq '') {
        $errors{name} = '名前を入力してください';
    }

    # メールアドレスの必須チェック
    my $email = $input->{email} // '';
    if ($email eq '') {
        $errors{email} = 'メールアドレスを入力してください';
    }
    # メールアドレスの形式チェック(必須チェックを通過した場合のみ)
    elsif ($email !~ /\A[^@\s]+\@[^@\s]+\.[^@\s]+\z/) {
        $errors{email} = 'メールアドレスの形式が正しくありません';
    }

    # エラーがあるかどうかを判定
    if (%errors) {
        say "検証失敗:";
        for my $field (sort keys %errors) {
            say "  - $field: $errors{$field}";
        }
        return (0, \%errors);
    }

    say "検証成功: $name ($email)";
    return (1, undef);
}

# テストしてみる
say "=== テスト1: 正常なデータ ===";
validate_user({ name => '山田太郎', email => 'yamada@example.com' });

say "\n=== テスト2: 名前とメールアドレスが両方空 ===";
validate_user({ name => '', email => '' });

say "\n=== テスト3: メールアドレスの形式が不正 ===";
validate_user({ name => '山田太郎', email => 'invalid-email' });

say "\n=== テスト4: すべてのフィールドが未定義 ===";
validate_user({});

実行すると、複数のエラーがまとめて表示されます。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
=== テスト1: 正常なデータ ===
検証成功: 山田太郎 (yamada@example.com)

=== テスト2: 名前とメールアドレスが両方空 ===
検証失敗:
  - email: メールアドレスを入力してください
  - name: 名前を入力してください

=== テスト3: メールアドレスの形式が不正 ===
検証失敗:
  - email: メールアドレスの形式が正しくありません

=== テスト4: すべてのフィールドが未定義 ===
検証失敗:
  - email: メールアドレスを入力してください
  - name: 名前を入力してください

フィールド名をキーにしたハッシュでエラーを管理することで、どのフィールドにどんなエラーがあるかを呼び出し側で把握できるようになりました。

まとめ

  • バリデーションとは、ユーザー入力が正しいかチェックする処理である
  • 必須チェックと形式チェックが基本的な検証項目である
  • 早期リターンでネストを浅く保つとコードが読みやすくなる
  • ハッシュでエラーを収集すると、すべてのエラーをまとめて返せる

次回予告

今回はシンプルなif文でバリデーションを実装しました。しかし、実際のアプリケーションではパスワードの強度チェック、確認パスワードの一致、利用規約への同意など、検証ルールがどんどん増えていきます。

次回は、検証ルールが増えてコードが複雑化する問題を体験します。if/elseのネストが深くなって保守が困難になる「あるある」な状況を見てみましょう。

完成コード

この回の完成コードを1つのスクリプトにまとめました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#!/usr/bin/env perl
# form-validation-01.pl
# ユーザー登録バリデーション(基本版)
# Perl v5.36+, 外部依存なし

use v5.36;
use utf8;
use warnings;
binmode STDOUT, ':utf8';

sub validate_user ($input) {
    my %errors;

    # 名前の必須チェック
    my $name = $input->{name} // '';
    if ($name eq '') {
        $errors{name} = '名前を入力してください';
    }

    # メールアドレスの必須チェック
    my $email = $input->{email} // '';
    if ($email eq '') {
        $errors{email} = 'メールアドレスを入力してください';
    }
    # メールアドレスの形式チェック(必須チェックを通過した場合のみ)
    elsif ($email !~ /\A[^@\s]+\@[^@\s]+\.[^@\s]+\z/) {
        $errors{email} = 'メールアドレスの形式が正しくありません';
    }

    # 結果を返す
    if (%errors) {
        return { ok => 0, errors => \%errors };
    }

    return { ok => 1, data => { name => $name, email => $email } };
}

# === 実行例 ===
my @test_cases = (
    { name => '山田太郎', email => 'yamada@example.com' },
    { name => '',         email => '' },
    { name => '山田太郎', email => 'invalid-email' },
    {},
);

for my $i (0 .. $#test_cases) {
    say "=== テスト" . ($i + 1) . " ===";
    my $result = validate_user($test_cases[$i]);

    if ($result->{ok}) {
        say "検証成功: $result->{data}{name} ($result->{data}{email})";
    }
    else {
        say "検証失敗:";
        for my $field (sort keys $result->{errors}->%*) {
            say "  - $field: $result->{errors}{$field}";
        }
    }
    say "";
}
comments powered by Disqus
Hugo で構築されています。
テーマ StackJimmy によって設計されています。