CakePHP3でモデルを使わないでお問い合わせフォームを作りたい

お問い合わせフォームをとにかく作りたかったんです。

でもモデルからDBのテーブルから全部作って…というわけではなく、入力フォームがあってメールで送信できる、というようなお問い合わせフォームにしたかったんです。

でもバリデーションチェックはしたいんです。

上記のような場合、モデルのないフォームがおすすめです。

割とブログ記事も多いのですが、実際に役立ったので自分も書きます。

【自分の環境】
macOS Catalina
PHP7.4.2
CakePHP3.8
MAMP5.7
Apache2.2
MySQL5.7

「モデルのないフォーム」

この名称で知られているようです。

公式ドキュメントもあります。

モデルのないフォーム - 3.9

では実際に作っていきましょう。

必要なのは3つのファイル

必要なのは以下の3つのファイルです。

Formディレクトリがなかったら作成しましょう。

/src/Form/ContactForm.php (フォーム)

/src/Controller/ContactsController.php (コントローラ)

/src/Template/Contacts/index.php (ビューテンプレート)

割と手軽ですよね。

ContactForm.php(フォーム)を作る

今回は体裁として名前欄、メール欄、お問い合わせ内容欄の3つを設けます。

それぞれname、email、bodyという値の命名です。

<?php

namespace App\Form;

use Cake\Form\Form;
use Cake\Form\Schema;
use Cake\Validation\Validator;
use Cake\Mailer\Email;

class ContactForm extends Form
{

    // お問い合わせフォームのスキーマを定義する
    protected function _buildSchema(Schema $schema)
    {
        $schema
            ->addField('name', ['type' => 'string'])
            ->addField('email', ['type' => 'string'])
            ->addField('body', ['type' => 'text']);

        return $schema;
    }

    // バリデーション内容を定義する
    protected function _buildValidator(Validator $validator)
    {        
        $validator
            ->notEmpty('name','名前を入力してください。');

        $validator
            ->notEmpty('email','メールアドレスを入力してください。')
            ->add('email', 'format', [
                'rule' => 'email',
                'message' => 'メールアドレスを入力してください。']);

        $validator
            ->notEmpty('body','本文を入力してください。');

        return $validator;
    }

    // バリデーション後に実行したい処理を記述する
    protected function _execute(array $data)
    {
        // メールを送信する処理(今回は省略)

        return true;
    }
}

ご覧のようにファンクションは3つあります。

スキーマを定義する_buildSchemaファンクション。

バリデーション内容を定義する_buildValidatorファンクション。

バリデーション後に実行したい処理を記述する_executeファンクション。

内容は他のファイルでも見たようなかんじで、あまり難しいことは書いてないので、特に説明はしません。

ContactsController.php(コントローラ)を作る

次にコントローラです。

<?php

namespace App\Controller;

use App\Form\ContactsForm;

class ContactsController extends AppController
{
    public function initialize()
    {
        parent::initialize();

        $this->loadComponent('Flash');
        $this->viewBuilder()->setLayout(false);
    }

    public function index()
    {
        $contacts = new ContactForm();

        if ($this->request->is('post')) {
            if ($contacts->validate($this->request->getData())) {
                if ($contacts->execute($this->request->getData())) {
                    $this->Flash->success(__('ご意見・ご要望を送信しました!'));
                } else {
                    $this->Flash->error(__('送信に失敗しました。'));
                }
            }else{
                $this->Flash->error(__('送信に失敗しました。'));
            }
        }

        $this->set(compact('contacts'));
    }
}

普通のコントローラと変わりませんが、上の方で以下の記述をしています。

「use App\Form\ContactForm;」

これで先ほどのContactFormを使うよ、と宣言しています。

indexアクションで入力があった時の処理を書いています。

ちなみに自分の場合はどうしてもバリデーションが作動しなかったので、「$contacts->validate($this->request->getData())」と明示的にバリデーションさせています。

本来はその先の「$contacts->execute($this->request->getData())」でバリデーションが動くらしいのですが、自分の場合はvalidate()としていることはお伝えしておきます。

いま少し言いましたが、execute()メソッドではデータが有効な時のふるまいを定義します。

ここではフラッシュを表示させています。

index.php(ビューテンプレート)を作る

最後にビューテンプレートを作ります。

<?php echo $this->Html->css('contacts/index'); ?>

<?= $this->Flash->render() ?>

<div class="main">

    <div class="users form">

        <div class="logo">
            <?= $this->Html->image('logo.png', [
                "alt" => "cake",
                'url' => ['controller'=>'pages','action'=>'top'],
                'width'=>'230',
                'height'=>'100'
                ]); ?>
        </div>

        <?= $this->Form->create($contacts) ?>
        <fieldset>
            <legend>ご意見・ご要望フォーム</legend>
            <?= '<p>名前</p>' ?>
            <?=  $this->Form->text('name',['size'=>30]) ?>
            <?= $this->Form->error('name'); ?>
            <?= '<p>メールアドレス</p>' ?>
            <?= $this->Form->text('email',['size'=>30]) ?>
            <?= $this->Form->error('email'); ?>
            <?= '<p>ご意見・ご要望</p>' ?>
            <?= $this->Form->textarea('body',['rows'=>10,'cols'=>40]) ?>
            <?= $this->Form->error('body'); ?>
            <br>
            <?= $this->Form->button('送信する!') ?>
            <?= $this->Form->end() ?>
        </fieldset>
    </div>
</div>

これに関しては、フォームの有無に関わらない内容なので、特に長い説明はしません。

名前欄、メール欄、お問い合わせ内容欄があります。

中盤ですが、「$this->Form->create($contacts)」として、$contactsを入れることを忘れないでください。

これを入れないとバリデーションメッセージが出なかったり、いろいろと困ります。

おまけ

フォームとしては説明は以上です。

ただお問い合わせフォームとして大事なのがメール送信です。

実際にフォームの_executeファンクションで書いていますが、記述は省略しています。

ここに関しては長くなるので、次回、別記事で説明します。

終わりに

以上となります。

モデルなしで手軽にお問い合わせフォームが作れるのは便利ですよね。

実際に動かすともっとわかってくると思います。

ぜひトライしてみてください。

メール送信に関しては次回詳しく説明します。

ありがとうございました。

CakePHP3で半角英数のみを通すバリデーションを付けたい

バリデーションはユーザーの使いやすさやらセキュリティやらで重要な機能の1つです。

数字じゃなかったら弾くとか、空文字だったら弾くとか、いろいろあります。

ただ意外と半角英数だけ通すっていうバリデーションがありません。

そこで半角英数だけ通すカスタムバリデーションを書いていきます。

以下の記事をとても参考にしました。

CakePHP3でカスタムバリデーション作ってみた。 - またどこかでCTOっぽいことやってる人のブログ

ではやっていきます。

【自分の環境】
macOS Catalina
PHP7.4.2
CakePHP3.8
MAMP5.7
Apache2.2
MySQL5.7

カスタムバリデーションを記述するファイルを作る

/[アプリ名]/src/Modelディレクトリの下にValidationディレクトリを作ります。

このValidationディレクトリにCustomValidation.phpを作成します。

以下のように記述します。

<?php
namespace App\Model\Validation;
use Cake\Validation\Validation;

class CustomValidation extends Validation {

      public static function alphaNumericCustom($check) {
             return (bool) preg_match('/^[a-zA-Z0-9]+$/', $check);
      }

}

Validationを継承したCustomValidationというクラスを作ります。

そしてalphaNumericCustomというファンクションを記述します。

ここで重要なポイントが2つあります。

①作成するfunctionは「static」にすること

②作成したfunctionの戻り値はbool型にすること

この2つを守らないとエラーになったり、意図通りに動かなくなります。

このpreg_matchは第一引数の正規表現に第二引数のテキストが一致していたら、trueを返してくれるファンクションです。

そして「'/^[a-zA-Z0-9]+$/'」の部分は正規表現と呼ばれます。

ざっくり言うと「英大文字A-Zの任意1文字にマッチして、英小文字a-zの任意1文字にマッチして、数字0-9の任意1文字にマッチする」という意味になります。

つまり半角英数だけ受け付けます、ということみたいです。

正規表現について詳しく知りたい方は、以下のリンクなどで調べてみてください。

【5分でまるっと理解】PHP正規表現の使い方まとめ

●●テーブルのvalidationDefaultファンクションにカスタムバリデーションを加える

validationDefaultファンクションにカスタムバリデーションを記述していきます。

検査したいテーブルである「●●Table.php」のようなファイルを開いてください。

今回はUsersTableのpassカラムにこのバリデーションを適用させます。

以下のように書きます。

public function validationDefault(Validator $validator)
    {
        $validator->provider('ProviderKey', 'App\Model\Validation\CustomValidation'); // 追加する
       (中略)
        $validator
            ->add('pass', 'alphaNumeric', [
                'rule' => ['alphaNumericCustom'], 
                'provider' => 'ProviderKey',   // カスタムバリデーション設定で書いたプロバイダのキーを入れます。
                'message' => 'パスワードは半角英数字で入力してください。']);
        (中略)
        return $validator;

passカラムに半角英数バリデーション、先ほどのバリデーションを付けました。

上の方の「$validator->provider('ProviderKey', 'App\Model\Validation\CustomValidation');」では先ほどのカスタムバリデーションとリンクさせています。

add()メソッド以降、いろいろ書かれていますね。

まずカラム名「pass」を指定します。

次にバリデーション名で、これは何でもいいみたいですが、わかりやすくalphaNumericにします。

続けてrule、provider、messageを記述します。

ruleは先ほどカスタムバリデーションクラスで書いたファンクションの名前「alphaNumericCustom」を記述します。

またproviderには上の方で書いた「ProviderKey」。

そしてmessageにはバリデーション に引っかかった際のメッセージを書きます。

これで完了となります。

実際に引っ掛けてみて試してみてください。

終わりに

以上となります。

1つ1つ書くと大変ですが、先人がすでに便利な道具を作ってくれていたので助かりました。

正規表現は自分でも書く場合があるかもしれないので、学んでおきたいです。

ありがとうございました。

VScodeで突然に別タブで開かなくなる時の対処

またVScodeのネタです。

今までソースコードが別タブで開いていたのに…。

突然、別タブで開かなくてすごく不便な時があります。

その時の対処法をお伝えします。

【自分の環境】
macOS Catalina
PHP7.4.2
CakePHP3.8
MAMP5.7
Apache2.2
MySQL5.7

タブやファイルをダブルクリックしてイタリック体から普通の字体へ戻す

別タブで開かない時は、上の方のタブのファイル名がイタリック体になっています。

そういう時は上の方のタブのファイル名をダブルクリックしてください。

もしくは、左に出ているサイドバーにあるファイル名をダブルクリックしてください。

すると上の方のタブのファイル名がイタリック体から普通の字体に戻ります。

これで別のファイルを開くと別タブで開きます。

お試しください。

終わりに

以上となります。

別タブで開いてくれないと比較ができず、非常に不便です。

なぜイタリック体になるのかはよくわかりません。

知っている方がいたらコメントください。

ありがとうございました。

VScodeで書いてたら、変なところを押したっぽくて、タブのスペース数が変わっていた

初めてCakePHPではない記事です。

VScodeです。

エディターはこれを使ってます。

無料ですし、みんな使ってますし…。

今回はそれに関することで、タブのスペース数が変わってしまっていた、というどうでもいいお話です。

【自分の環境】
macOS Catalina
PHP7.4.2
CakePHP3.8
MAMP5.7
Apache2.2
MySQL5.7

たぶんデフォルトは「スペース:4」

デフォルトはスペースが4つだと思うんです。

でも変なところを押したのか、たまに6つとかになります。

…正直1回しかなってないですけど、不便でした。

よく見るとエディターの右下に「スペース:4」って書いてあります。

スペース:4にする、直し方

右下の「スペース:●」をクリックして、「スペースによるインデント(Indent Using Spaces)」をクリック→数が選べる、という仕様になっています。

スペースの他にもタブにすることもできますね。

縛りがなければ、好きなものを使ってみてください。

終わりに

以上となります。

スペース数が変わると地味に困るので、困ったら直してみてください。

ありがとうございました。

CakePHP3でエラーメッセージを表示させる

CakePHP3でバリデーションなんかでエラーメッセージを表示させたいけど、なぜか出ない…。

こういう状況ありませんか?

自分はありました。

こういう時に上手く表示させる方法を説明します。

【自分の環境】
macOS Catalina
PHP7.4.2
CakePHP3.8
MAMP5.7
Apache2.2
MySQL5.7

こうなっているかも?

入力フォームなんかで、バリデーションに引っかかった時にエラーメッセージを表示させたりします。

バリデーションではちゃんと記述しているのに、なぜか表示が出ない…ってとき、ありました。

自分はこんなかんじでした。

<p>メールアドレス</p>
<?= $this->Form->text('mail',['size'=>30]) ?>

なんてことないフォームヘルパーを用いたtextフォームです。

ですがこれではエラーメッセージは出ません。

こうする

以下のようにすると出ます。

<p>メールアドレス</p>
<?= $this->Form->text('mail',['size'=>30]) ?>
<?= $this->Form->error('mail'); ?>

フォームヘルパーのerrorメソッドを足しました。

これでバリデーションが上手く設定されていれば、出るはずです。

なぜこのような間違いをするのか?

おそらくですが、元々は以下のようにしていたのではないかと思います。

 <?= $this->Form->control('mail',['size'=>30]) ?>

微妙な違いですが、text()ではなくcontrol()を使っています。

control()を使うと、ラベルも出るしエラーメッセージも出してくれるんです。

ただラベルの値をいじれなかったりするので、text()に変えた…という過程を踏む方が多いのではないかと思います。

「$this->Form->error(云々);」があるか確かめてみてください。

終わりに

以上となります。

エラーメッセージが出ない原因はいくつかありますが、今回はフォームによるものを説明しました。

お役に立てば幸いです。

ありがとうございました。

CakePHP3でリンクを別タブで開きたい

リンクしたけど別タブで開きたい、っていう時ありますよね。

そういう時のCakePHP3においての設定を説明します。

【自分の環境】
macOS Catalina
PHP7.4.2
CakePHP3.8
MAMP5.7
Apache2.2
MySQL5.7

ヘルパーでのリンクの方法

知ってるよっていう方もいると思いますが、ヘルパーでのリンクの方法からです。

<?= $this->Html->link(__('TOP'), ['controller' => 'pages','action' => 'top']) ?>

上記だとPagesControllerのtop.ctpのページに飛びます。

別タブで開きたい

これを以下のようにすると、別タブで開くようになります。

<?= $this->Html->link(__('TOP'), ['controller' => 'pages','action' => 'top'],['target' => '_blank']) ?>

見てわかるように、link()メソッドの第3引数に「['target' => '_blank']」を入れました。

こうすると別タブで開くようになります。

終わりに

以上となります。

今回は小ネタ感が強いですね。

知らなかった方はぜひ試してみてください。

ありがとうございました。

CakePHP3でCSSを反映させたい

CSSを書いていこう、と思ったら、CSSが反映されないってことありますよね。

かなり萎えますし、不安になります。

ちょっとした設定が必要なので、それをご説明します。

【自分の環境】
macOS Catalina
PHP7.4.2
CakePHP3.8
MAMP5.7
Apache2.2
MySQL5.7

まずはcssディレクトリにcssファイルを配置する

cssファイルを配置してください。

当たり前ですが、処理も書き込んでおきましょう。

読み込みたいビューテンプレート等で読み込みの宣言をする

こんなかんじで読み込みを宣言します。

(ビューテンプレートもしくはレイアウトテンプレート)

<head>
   (中略)
    <?php
        echo $this->Html->css('reset');
        echo $this->Html->css('mypage');
    ?>
   (中略)
</head>

headタグ内でphpタグで囲みながら$this->Html->css('reset');などと書きます。

今回はreset.cssとmypage.cssを読み込んでいます。

これでテンプレートでの読み込み宣言はOKです。

app.phpでキャッシュ無効化の設定をする

キャッシュが有効になっていると、CSSが反映されません。

app.phpにあるassetのタイムスタンプのコメントアウトを外し、以下のように設定しましょう。

'Asset' => [
        'timestamp' => true,
        // 'cacheTime' => '+1 year'
    ],

これでキャッシュが無効化されて反映するはずです。

終わりに

以上となります。

CSSを書き始める時に陥りがちな点について説明しました。

もし反映されなかったら試してみてください。

ありがとうございました。