Motomichi Works Blog

その日学習したことについて書いている日記です。誰かの役に立ったらそれはそれで嬉しいです。

さくらvpsとcakephp2.6.7で開発日記 その0003 cakephp-captchaを使ってみる

参考にさせて頂いたページ

inimist/cakephp-captcha · GitHub

cakephp-captcha-master.zipをダウンロードして解凍する

解凍したら以下のような構造になっていた

.
├── README.md
└── app
    ├── Controller
    │   ├── Component
    │   │  └── CaptchaComponent.php
    │   └── SignupsController.php
    ├── Model
    │   ├── Behavior
    │   │   └── CaptchaBehavior.php
    │   └── Signup.php
    └── View
        ├── Elements
        │   └── captchamath.ctp
        ├── Helper
        │   └── CaptchaHelper.php
        └── Signups
            ├── add.ctp
            └── demo.ctp

Controllerを配置する

zipの中にある

  • app/Controller/Component/CaptchaComponent.php
  • app/Controller/SignupsController.php

をそれぞれ開発環境のapp/Controller/にそれぞれ配置する。

Modelを配置する

zipの中にある

  • app/Model/Behavior/CaptchaBehavior.php
  • app/Model/Signup.php

をそれぞれ開発環境のapp/Model/にそれぞれ配置する。

Viewを配置する

zipの中にある

  • View/Helper/CaptchaHelper.php

を開発環境のView/Helper/に配置して、

  • View/Signups/index.ctp

を自分で作成する。

index.ctpの記述内容は以下の通り。

<?php
echo($this->Form->create());
?>
<div>
<?php
echo $this->Form->label('Signup.security_code', 'セキュリティコード: ');
echo $this->Captcha->render();
echo $this->Form->error('Signup.security_code');
?>
</div>
<?php
echo($this->Form->end('送信'));
?>



<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script>
jQuery('.creload').on('click', function() {
  var mySrc = $(this).prev().attr('src');
  var glue = '?';
  if(mySrc.indexOf('?')!=-1)  {
    glue = '&';
  }
  $(this).prev().attr('src', mySrc + glue + new Date().getTime());
  return false;
});
</script>

フォントを配置する

別途ダウンロードした

  • Sanchez-Regular.ttf
  • monofont.ttf

開発環境のapp/Lib/Fonts/ディレクトリに配置する。

使用するフォントを設定する

app/Controller/Component/CaptchaComponent.php

private $__fonts = array('monofont', 'sanchez');

となっている箇所を

private $__fonts = array('monofont', 'Sanchez-Regular');

に変更した。

SignupsController.phpを編集する

さきほど配置したSignupsController.phpを参考ページにならって編集した。
demo,addのアクションを削除したり、他にも色々編集して、以下のようにした。

<?php
/**
    * (Sample) Controller for Showing the use of Captcha*
    * @author     Arvind Kumar (arvind.mailto@gmail.com)
    * @link       http://www.devarticles.in/
    * @copyright  Copyright © 2014 http://www.devarticles.in/
    * @version 2.5 Tested OK in Cakephp 2.5.4
    */
class SignupsController extends AppController {

  // Controller名
  public $name = 'Signups';
  // 使用するコンポーネントとその設定
  public $components = array(
    'Captcha'=>array(
      'model'=>'Signup',
      'field'=>'security_code'
    )
  );
  // 使用するModel
  public $uses = array('Signup');
  // 使用するHelper
  public $helpers = array('Captcha');

  function captcha(){
    // 画像更新がクリックされたときの処理
    $this->autoRender = false;
    $this->layout='ajax';
    $this->Captcha->create();
  }

  function index(){
    // 送信データが無い場合の処理
    if(!$this->request->data){
      $this->render();
      return;
    }
    // フォームの送信データがModel内で使用できるようにsetする
    $this->Signup->set($this->request->data);
    // setCaptchaで画像に表示されている文字列をsetする
    $this->Signup->setCaptcha(
      'security_code',
      $this->Captcha->getCode('Signup.security_code')
    );
    // バリデーションを実行
    if($this->Signup->validates()){
      // バリデーションOKのときの処理
      $this->Session->setFlash('Signupのバリデーションはtrueです');
      $this->render();
      return;
    }else{
      // バリデーションNGのときの処理
      $this->Session->setFlash('Signupのバリデーションはfalseです');
      $this->render();
      return;
    }
  }
}

signups/indexにアクセスしてみる

この段階で、バリデーションが機能するようになった。

ちょっとだけCaptchaHelper.phpを改良する

個人的には、自動的に生成されるlabel, errorMessage, div, required をfalseにして、自分の好きなところに表示させたいので、

echo $this->Form->input( $this->settings['model'].'.'. $this->settings['field'], array('autocomplete'=>'off','label'=> $this->settings['clabel'],'class'=>'clabel'));

となっている箇所を

                echo $this->Form->input(
                    $this->settings['model'].'.'. $this->settings['field'],
                    array(
                        'autocomplete'=>'off',
                        'label'=> false,
                        'class'=>'clabel',
                        'errorMessage' => false,
                        'div' => false,
                        'required' => false
                    )
                );

とした。

今回追加したファイル

  • app/Controller/Component/CaptchaComponent.php
  • app/Controller/SignupsController.php
  • app/Lib/Fonts/monofont.ttf
  • app/Lib/Fonts/Sanchez-Regular.ttf
  • app/Model/Behavior/CaptchaBehavior.php
  • app/Model/Signup.php
  • app/View/Helper/CaptchaHelper.php
  • app/View/Signups/index

SignupsController.phpの記述内容について

今回のcaptcha導入にあたって、苦労した部分などについて少し書いておく。

$componentsとCaptchaComponent.phpの125行目の$settingsについて

  public $components = array(
    'Captcha'=>array(
      'model'=>'Signup',
      'field'=>'security_code'
    )
  );

上記はCaptchaComponent.phpの125行目にある、$settingsに設定された内容を上書きするもの。
各Controllerでこれらを設定すると、Controllerに記述した設定内容が優先される。
これらはバリデーションなどで使用する情報であると同時に、Viewで描画されるinput要素のname属性にも反映される。

いつも通りModelへのsetは必要

    // フォームの送信データがModel内で使用できるようにsetする
    $this->Signup->set($this->request->data);

上記の記述は、通常のModelやバリデーションと同様にsetする必要がある。

getCodeとsetCaptcha

    // setCaptchaで画像に表示されている文字列をsetする
    $this->Signup->setCaptcha(
      'security_code',
      $this->Captcha->getCode('Signup.security_code')
    );

上記の記述のうち、getCodeメソッドの引数には$componentsのmodelとfieldに設定した文字列をつなげたものが
'Signup.security_code'のような感じで入ることになる。
この詳細は、CaptchaComponent.php内のgetCodeメソッドが定義されている箇所を読むとわかるんだけど、sessionに格納された文字列を取得している。
これは、DebugKitのSessionをみながら作業するとよくわかる。

次に、setCaptchaの引数についてですが、その引数は二つ。
第一引数は$componentsのfieldに設定した文字列と同じ'security_code'を設定する。
第二引数はgetCodeメソッドの戻り値で、getCodeメソッドの戻り値とは、submitしたときに画像に表示されている文字列。
この詳細は、CaptchaBehavior.php内のsetCaptchaメソッドが定義されている箇所を読むとわかる。
実行時の引数はそれぞれ、$field, $captchaとして、setCaptchaメソッド内で使用される。

さらに、CaptchaBehavior.php内のvalidateCaptchaメソッドを読むと
return フォームに入力した文字列 == 画像に表示されていた文字列;
と比較結果をreturnしている事がわかる。

特徴として

gdをインストールしなくても動作する。と参考ページに書いてあった。

おまけ

画像更新リンクの文字列を変更する

CaptchaComponent.phpの125行目の$settingsの

      'reload_txt' => 'Can\'t read? Reload',

を編集する。

バリデーションエラーのときに表示する文字列を変更する

Model/Signup.php

            'error' => 'Incorrect captcha code value'

を編集する。

画像の外側やリンクの外側に要素を追加したい

CaptchaHelper.php

echo $this->Html->image($this->Html->url(array('controller'=>$controller, 'action'=>$action, '?'=> $qstring), true), array('vspace'=>2));
echo $this->Html->link( $this->settings['reload_txt'], '#', array('class' => 'creload', 'escape' => false));

の部分に例えばdiv要素を追加して、

echo '<div class="hogehoge">'.$this->Html->image($this->Html->url(array('controller'=>$controller, 'action'=>$action, '?'=> $qstring), true), array('vspace'=>2)).'</div>';
echo '<div class="hogehoge">'.$this->Html->link( $this->settings['reload_txt'], '#', array('class' => 'creload', 'escape' => false)).'</div>';

みたいな感じにする。

ただ、これらを編集したときはjsも少しだけ編集する必要がある。