2023.09.23
【Phalcon】FormクラスでFormをコンポーネント化する

UnsplashAaron Burdenが撮影した写真

はじめに

PhalconにはFormをコンポーネント化するためのFormクラスが用意されています。

業務で使っているので、この記事にその使い方を簡単にメモします。

Formクラスを使うと、バリデーションエラーで元の画面に戻ってきたときに自動で$_POSTの値をセットしてくれたり、なにかと便利です。

環境

OS:Windows11 Home / WSL(Ubuntu20.04)
php:v7.4.33
phalcon:4.0.6
Phalcon DevTools:v4.2.0
DB:mysql v8.0.34

前提

以下のカラムを持つUsersテーブルにCRUDするためのFormを作成します。

【カラム】
 ・id
 ・name
 ・password

本題に入る前に上記のUsersテーブルを作成してください。

この記事では、Usersテーブルにadd(データを作成する)する機能を実装します。

本題

Formクラスを使うために、以下の作業をします。

①config.phpにformをまとめるフォルダのパスを追記する
②loader.phpに1行追記
③formsフォルダを作成する
④formファイルを作成する
⑤Controllerを編集する
⑥Viewでformを使う

それではさっそく①から始めます。

①config.phpにformをまとめるフォルダのパスを追記する

後程Formをまとめるフォルダを作成するので、config.phpにフォルダのパスを追記します。

/app/config/config.phpに以下の赤字の1行を追記します。


~~省略~~
return new \Phalcon\Config([
    'database' => [
        ~~省略~~
    ],
    'application' => [
        'appDir'         => APP_PATH . '/',
        'controllersDir' => APP_PATH . '/controllers/',
        'modelsDir'      => APP_PATH . '/models/',
        'migrationsDir'  => APP_PATH . '/migrations/',
        'viewsDir'       => APP_PATH . '/views/',
        'pluginsDir'     => APP_PATH . '/plugins/',
        'libraryDir'     => APP_PATH . '/library/',
        'cacheDir'       => BASE_PATH . '/cache/',
        'baseUri'        => '/',
        'formsDir'        => 'APP_PATH . '/forms/',
    ]
]);

②loader.phpに1行追記

/app/config/loader.phpに以下赤字部分の1行を追記します。

<?php

$loader = new \Phalcon\Loader();

/**
 * We're a registering a set of directories taken from the configuration file
 */
$loader->registerDirs(
    [
        $config->application->controllersDir,
        $config->application->modelsDir,
        $config->application->formsDir,
    ]
)->register();

③formsフォルダを作成する

次にFormをまとめるためのフォルダを作成します。

/app/formsとなるよう「forms」フォルダを作成してください。

④formファイルを作成する

③で作成したformsフォルダにUserForm.phpファイルを作成し、以下のように記述してください。

<?php

use Phalcon\Forms\Form;
use Phalcon\Forms\Element\Hidden;
use Phalcon\Forms\Element\Text;
use Phalcon\Forms\Element\Password;

use Phalcon\Validation;
use Phalcon\Validation\Validator\PresenceOf;
use Phalcon\Validation\Validator\Uniqueness as UniquenessValidator;
use Phalcon\Validation\Validator\Regex as RegexValidator;

class UserForm extends Form{
    public function initialize(){
        $user_id = new Hidden("id");
        $this->add($user_id);

        $name = new Text("name");
        $name->setLabel("名前");
        $name->addValidator(
            new PresenceOf(
                [
                    "message" => "名前は必須です"
                ]
            )
        );
        $name->addValidator(
            new UniquenessValidator(
                [
                    "model"     => new Users(),
                    "message"   => "この名前は既に使用されています",
                ]
            )
        );
        $this->add($name);

        $password = new Password("password");
        $password->setLabel("パスワード");
        $password->addValidator(
            new RegexValidator(
                [
                    "pattern"   => "/(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9]).{8, 16}/u",
                    "message"   => "パスワードは大文字、小文字、数字を含む8-16文字を指定してください",
                ]
            )
        );
        $this->add($password);
    }
}

なお、以下のリンクからFormクラスで使える要素の一覧とバリデーションの一覧を確認できます。

※Formの要素一覧:https://docs.phalcon.io/4.0/en/forms#elements

※Validation一覧 :https://docs.phalcon.io/4.0/en/validation#validators

⑤Controllerを編集する

UsersController.phpのaddActionを以下のように編集してください。

public function addAction(){
        $form = new UserForm();
        $this->view->form = $form;

        //POSTされた時の処理
        if($_POST['operation'] == "send"){
            $user = new Users();
            $user->name = $_POST['name'];
            $user->password = password_hash($_POST['password'], PASSWORD_DEFAULT);

            $form = new UserForm();

            //バリデーション判定で条件分岐
            if($form->isValid($_POST) && $user->save()){
                //バリデーションOKの時の処理
                return $this->response->redirect("users/index");
            }else{
                //バリデーションNGの時の処理
                $this->view->errorMessages = $form->getMessages();
            }
        }
}

⑥Viewでformを使う

最後にadd.phtmlを以下のように編集します。

<h2>users/add</h2>

<!-- エラーメッセージを表示 -->
<?php
    if(!empty($errorMessages)){
        foreach($errorMessages as $message){
            echo "<p style='color:red;'>{$message}</p>";
        }
    }
?>

<form action="#" method="post">
    <p><?php echo $form->label("name");?></p>
    <p><?php echo $form->render("name");?></p>

    <p><?php echo $form->label("password");?></p>
    <p><?php echo $form->render("password");?></p>

    <button type="submit" name="operation" value="send">登録</button>
</form>

以上でFormクラスでのFormのコンポーネント化は完了です!

画面の確認

こんな感じで表示されていればOKです。

補足

Formクラスでバリデーションをするので、もしモデルにバリデーションを記述している場合はモデルからバリデーションを削除してください。

モデルにバリデーションを記述する場合は以下のようになっているかと思うので、赤字部分に該当する部分を丸ごと削除してください。

~~省略~~

class Users extends \Phalcon\Mvc\Model
{
    public $id;
    public $name;
    public $password;

    public function initialize()
    {
        $this->setSchema("ouchibar2");
        $this->setSource("users");
    }

    public function validation(){
        $validator = new Validation();
        $validator->add(
            ["name", "password"],
            new PresenceOf([
                "message" => [
                    "name" => "nameは必須です",
                    "password" => "passwordは必須です"
                ]
            ])
        );
        $validator->add(
            "password",
            new StringLength(
                [
                    "max" => 16,
                    "min" => 8,
                    "messageMaximum" => "passwordは8-16文字で入力してください",
                    "messageMinimun" => "passwordは8-16文字で入力してください",
                    "includeMaximum" => true,
                    "includeMinimum" => false
                ]
            )
        );
        return $validator->validate($_POST);
    }
    ~~省略~~
}